use of org.opengis.referencing.IdentifiedObject in project sis by apache.
the class Formatter method appendComplement.
/**
* Appends the optional complementary attributes common to many {@link IdentifiedObject} subtypes.
* Those attributes are {@code ANCHOR}, {@code SCOPE}, {@code AREA}, {@code BBOX}, {@code VERTICALEXTENT},
* {@code TIMEEXTENT}, {@code ID} (previously known as {@code AUTHORITY}) and {@code REMARKS},
* and have a special treatment: they are written by {@link #append(FormattableObject)}
* after the {@code formatTo(Formatter)} method returned.
*
* <p>The {@code ID[<name>,<code>,…]} element is normally written only for the root element
* (unless the convention is {@code INTERNAL}), but there is various exceptions to this rule.
* If formatted, the {@code ID} element will be by default on the same line than the enclosing
* element (e.g. {@code SPHEROID["Clarke 1866", …, ID["EPSG", 7008]]}). Other example:</p>
*
* {@preformat text
* PROJCS["NAD27 / Idaho Central",
* GEOGCS[...etc...],
* ...etc...
* ID["EPSG", 26769]]
* }
*
* For non-internal conventions, all elements other than {@code ID[…]} are formatted
* only for {@link CoordinateOperation} and root {@link ReferenceSystem} instances,
* with an exception for remarks of {@code ReferenceSystem} embedded inside {@code CoordinateOperation}.
* Those restrictions are our interpretation of the following ISO 19162 requirement:
*
* <blockquote>(…snip…) {@code <scope extent identifier remark>} is a collection of four optional attributes
* which may be applied to a coordinate reference system, a coordinate operation or a boundCRS. (…snip…)
* Identifier (…snip…) may also be utilised for components of these objects although this is not recommended
* except for coordinate operation methods (including map projections) and parameters. (…snip…)
* A {@code <remark>} can be included within the descriptions of source and target CRS embedded within
* a coordinate transformation as well as within the coordinate transformation itself.</blockquote>
*/
@SuppressWarnings("null")
private void appendComplement(final IdentifiedObject object, final FormattableObject parent, final FormattableObject gp) {
isComplement = true;
// Whether to format ID[…] elements.
final boolean showIDs;
// Whether we shall limit to a single ID[…] element.
final boolean filterID;
// Whether to format any element other than ID[…] and Remarks[…].
final boolean showOthers;
// Whether to format Remarks[…].
final boolean showRemarks;
if (convention == Convention.INTERNAL) {
showIDs = true;
filterID = false;
showOthers = true;
showRemarks = true;
} else {
/*
* Except for the special cases of OperationMethod and Parameters, ISO 19162 recommends to format the
* ID only for the root element. But Apache SIS adds an other exception to this rule by handling the
* components of CompoundCRS as if they were root elements. The reason is that users often create their
* own CompoundCRS from standard components, for example by adding a time axis to some standard CRS like
* "WGS84". The resulting CompoundCRS usually have no identifier. Then the users often need to extract a
* particular component of a CompoundCRS, most often the horizontal part, and will need its identifier
* for example in a Web Map Service (WMS). Those ID are lost if we do not format them here.
*/
if (parent == null || parent instanceof CompoundCRS) {
showIDs = true;
} else if (gp instanceof CoordinateOperation && !(parent instanceof IdentifiedObject)) {
// "SourceCRS[…]" and "TargetCRS[…]" sub-elements in CoordinateOperation.
showIDs = true;
} else if (convention == Convention.WKT2_SIMPLIFIED) {
showIDs = false;
} else {
showIDs = (object instanceof OperationMethod) || (object instanceof GeneralParameterDescriptor);
}
if (convention.majorVersion() == 1) {
filterID = true;
showOthers = false;
showRemarks = false;
} else {
filterID = (parent != null);
if (object instanceof CoordinateOperation) {
showOthers = !(parent instanceof ConcatenatedOperation);
showRemarks = showOthers;
} else if (object instanceof ReferenceSystem) {
showOthers = (parent == null);
showRemarks = (parent == null) || (gp instanceof CoordinateOperation);
} else {
// Mandated by ISO 19162.
showOthers = false;
showRemarks = false;
}
}
}
if (showOthers) {
appendForSubtypes(object);
}
if (showIDs) {
Collection<ReferenceIdentifier> identifiers = object.getIdentifiers();
if (identifiers != null) {
// Paranoiac check
if (filterID) {
for (final ReferenceIdentifier id : identifiers) {
if (Citations.identifierMatches(authority, id.getAuthority())) {
identifiers = Collections.singleton(id);
break;
}
}
}
for (ReferenceIdentifier id : identifiers) {
if (!(id instanceof FormattableObject)) {
id = ImmutableIdentifier.castOrCopy(id);
}
append((FormattableObject) id);
if (filterID)
break;
}
}
}
if (showRemarks) {
appendOnNewLine(WKTKeywords.Remark, object.getRemarks(), ElementKind.REMARKS);
}
isComplement = false;
}
use of org.opengis.referencing.IdentifiedObject in project sis by apache.
the class EPSGDataAccess method createObject.
/**
* Returns an arbitrary object from a code. The default implementation delegates to more specific methods,
* for example {@link #createCoordinateReferenceSystem(String)}, {@link #createDatum(String)}, <i>etc.</i>
* until a successful one is found.
*
* <p><strong>Note that this method may be ambiguous</strong> since the same EPSG code can be used for different
* kind of objects. This method throws an exception if it detects an ambiguity on a <em>best-effort</em> basis.
* It is recommended to invoke the most specific {@code createFoo(String)} method when the desired type is known,
* both for performance reason and for avoiding ambiguity.</p>
*
* @param code value allocated by EPSG.
* @return the object 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.
*
* @see #createCoordinateReferenceSystem(String)
* @see #createDatum(String)
* @see #createCoordinateSystem(String)
*/
@Override
public synchronized IdentifiedObject createObject(final String code) throws NoSuchAuthorityCodeException, FactoryException {
ArgumentChecks.ensureNonNull("code", code);
final boolean isPrimaryKey = isPrimaryKey(code);
final StringBuilder query = new StringBuilder("SELECT ");
final int queryStart = query.length();
int found = -1;
try {
final int pk = isPrimaryKey ? toPrimaryKeys(null, null, null, code)[0] : 0;
for (int i = 0; i < TableInfo.EPSG.length; i++) {
final TableInfo table = TableInfo.EPSG[i];
final String column = isPrimaryKey ? table.codeColumn : table.nameColumn;
if (column == null) {
continue;
}
query.setLength(queryStart);
query.append(table.codeColumn);
if (!isPrimaryKey) {
// Only for filterFalsePositive(…).
query.append(", ").append(column);
}
query.append(" FROM ").append(table.table).append(" WHERE ").append(column).append(isPrimaryKey ? " = ?" : " LIKE ?");
try (PreparedStatement stmt = connection.prepareStatement(translator.apply(query.toString()))) {
/*
* Check if at least one record is found for the code or the name.
* Ensure that there is not two values for the same code or name.
*/
if (isPrimaryKey) {
stmt.setInt(1, pk);
} else {
stmt.setString(1, toLikePattern(code));
}
Integer present = null;
try (ResultSet result = stmt.executeQuery()) {
while (result.next()) {
if (isPrimaryKey || SQLUtilities.filterFalsePositive(code, result.getString(2))) {
present = ensureSingleton(getOptionalInteger(result, 1), present, code);
}
}
}
if (present != null) {
if (found >= 0) {
throw new FactoryDataException(error().getString(Errors.Keys.DuplicatedIdentifier_1, code));
}
found = i;
}
}
}
} catch (SQLException exception) {
throw databaseFailure(IdentifiedObject.class, code, exception);
}
/*
* If a record has been found in one table, then delegates to the appropriate method.
*/
if (found >= 0) {
switch(found) {
case 0:
return createCoordinateReferenceSystem(code);
case 1:
return createCoordinateSystem(code);
case 2:
return createCoordinateSystemAxis(code);
case 3:
return createDatum(code);
case 4:
return createEllipsoid(code);
case 5:
return createPrimeMeridian(code);
case 6:
return createCoordinateOperation(code);
case 7:
return createOperationMethod(code);
case 8:
return createParameterDescriptor(code);
// Can not cast Unit to IdentifiedObject
case 9:
break;
// Should not happen
default:
throw new AssertionError(found);
}
}
throw noSuchAuthorityCode(IdentifiedObject.class, code);
}
use of org.opengis.referencing.IdentifiedObject in project sis by apache.
the class CoordinateOperationRegistry method toAuthorityDefinition.
/**
* If the authority defines an object equal, ignoring metadata, to the given object, returns that authority object.
* Otherwise returns the given object unchanged. We do not invoke this method for user-supplied CRS, but only for
* CRS or other objects created by {@code CoordinateOperationRegistry} as intermediate step.
*/
final <T extends IdentifiedObject> T toAuthorityDefinition(final Class<T> type, final T object) throws FactoryException {
if (codeFinder != null) {
codeFinder.setIgnoringAxes(false);
final IdentifiedObject candidate = codeFinder.findSingleton(object);
codeFinder.setIgnoringAxes(true);
if (Utilities.equalsIgnoreMetadata(object, candidate)) {
return type.cast(candidate);
}
}
return object;
}
use of org.opengis.referencing.IdentifiedObject in project sis by apache.
the class CoordinateOperationRegistry method recreate.
/**
* Creates a new coordinate operation with the same method than the given operation, but different CRS.
* The CRS may differ either in the number of dimensions (i.e. let the vertical coordinate pass through),
* or in axis order (i.e. axis order in user CRS were not compliant with authority definition).
*
* @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
* @param transform the math transform to use in replacement to the one in {@code operation}.
* @param method the operation method, or {@code null} for attempting an automatic detection.
* @return a new operation from the given source CRS to target CRS using the given transform.
* @throws IllegalArgumentException if the operation method can not have the desired number of dimensions.
* @throws FactoryException if an error occurred while creating the new operation.
*/
private CoordinateOperation recreate(final CoordinateOperation operation, CoordinateReferenceSystem sourceCRS, CoordinateReferenceSystem targetCRS, final MathTransform transform, OperationMethod method) throws IllegalArgumentException, FactoryException {
/*
* If the user-provided CRS are approximatively equal to the coordinate operation CRS, keep the later.
* The reason is that coordinate operation CRS are built from the definitions provided by the authority,
* while the user-provided CRS can be anything (e.g. parsed from a quite approximative WKT).
*/
CoordinateReferenceSystem crs;
if (Utilities.equalsApproximatively(sourceCRS, crs = operation.getSourceCRS()))
sourceCRS = crs;
if (Utilities.equalsApproximatively(targetCRS, crs = operation.getTargetCRS()))
targetCRS = crs;
final Map<String, Object> properties = new HashMap<>(derivedFrom(operation));
/*
* Determine whether the operation to create is a Conversion or a Transformation
* (could also be a Conversion subtype like Projection, but this is less important).
* We want the GeoAPI interface, not the implementation class.
* The most reliable way is to ask to the 'AbstractOperation.getInterface()' method,
* but this is SIS-specific. The fallback uses reflection.
*/
final Class<? extends IdentifiedObject> type;
if (operation instanceof AbstractIdentifiedObject) {
type = ((AbstractIdentifiedObject) operation).getInterface();
} else {
type = Classes.getLeafInterfaces(operation.getClass(), CoordinateOperation.class)[0];
}
properties.put(ReferencingServices.OPERATION_TYPE_KEY, type);
/*
* Reuse the same operation method, but we may need to change its number of dimension.
* The capability to resize an OperationMethod is specific to Apache SIS, so we must
* be prepared to see the 'redimension' call fails. In such case, we will try to get
* the SIS implementation of the operation method and try again.
*/
if (SubTypes.isSingleOperation(operation)) {
final SingleOperation single = (SingleOperation) operation;
properties.put(ReferencingServices.PARAMETERS_KEY, single.getParameterValues());
if (method == null) {
final int sourceDimensions = transform.getSourceDimensions();
final int targetDimensions = transform.getTargetDimensions();
method = single.getMethod();
try {
method = DefaultOperationMethod.redimension(method, sourceDimensions, targetDimensions);
} catch (IllegalArgumentException ex) {
try {
method = factorySIS.getOperationMethod(method.getName().getCode());
method = DefaultOperationMethod.redimension(method, sourceDimensions, targetDimensions);
} catch (NoSuchIdentifierException | IllegalArgumentException se) {
ex.addSuppressed(se);
throw ex;
}
}
}
}
return factorySIS.createSingleOperation(properties, sourceCRS, targetCRS, AbstractCoordinateOperation.getInterpolationCRS(operation), method, transform);
}
use of org.opengis.referencing.IdentifiedObject in project sis by apache.
the class IdentifiedObjectFinder method findSingleton.
/**
* Lookups only one object which is approximatively equal to the specified object.
* This method invokes {@link #find(IdentifiedObject)}, then examine the returned {@code Set} as below:
*
* <ul>
* <li>If the set is empty, then this method returns {@code null}.</li>
* <li>If the set contains exactly one element, then this method returns that element.</li>
* <li>If the set contains more than one element, but only one element has the same axis order
* than {@code object} and all other elements have different axis order,
* then this method returns the single element having the same axis order.</li>
* <li>Otherwise this method considers that there is ambiguity and returns {@code null}.</li>
* </ul>
*
* @param object the object looked up.
* @return the identified object, or {@code null} if none or ambiguous.
* @throws FactoryException if an error occurred while creating an object.
*/
public IdentifiedObject findSingleton(final IdentifiedObject object) throws FactoryException {
/*
* Do not invoke Set.size() because it may be a costly operation if the subclass
* implements a mechanism that create IdentifiedObject instances only on demand.
*/
IdentifiedObject result = null;
boolean sameAxisOrder = false;
boolean ambiguous = false;
try {
for (final IdentifiedObject candidate : find(object)) {
final boolean so = !ignoreAxes || Utilities.deepEquals(candidate, object, COMPARISON_MODE);
if (result != null) {
ambiguous = true;
if (sameAxisOrder && so) {
// Found two matches even when taking in account axis order.
return null;
}
}
result = candidate;
sameAxisOrder = so;
}
} catch (BackingStoreException e) {
throw e.unwrapOrRethrow(FactoryException.class);
}
return (sameAxisOrder || !ambiguous) ? result : null;
}
Aggregations