use of org.opengis.referencing.IdentifiedObject in project sis by apache.
the class EPSGFactoryTest method testFindGeographic.
/**
* Tests {@link EPSGFactory#newIdentifiedObjectFinder()} method with a geographic CRS.
*
* @throws FactoryException if an error occurred while querying the factory.
*/
@Test
@DependsOnMethod("testWGS84")
public void testFindGeographic() throws FactoryException {
final EPSGFactory factory = TestFactorySource.factory;
assumeNotNull(factory);
final IdentifiedObjectFinder finder = factory.newIdentifiedObjectFinder();
final DefaultGeographicCRS crs = (DefaultGeographicCRS) CRS.fromWKT("GEOGCS[“WGS 84”,\n" + // Use the alias instead than primary name for forcing a deeper search.
" DATUM[“WGS 84”,\n" + // Different name for forcing a deeper search.
" SPHEROID[“WGS 1984”, 6378137.0, 298.257223563]],\n" + " PRIMEM[“Greenwich”, 0.0],\n" + " UNIT[“degree”, 0.017453292519943295],\n" + " AXIS[“Geodetic latitude”, NORTH],\n" + " AXIS[“Geodetic longitude”, EAST]]");
/*
* First, search for a CRS with axis order that does not match the ones in the EPSG database.
* IdentifiedObjectFinder should not accept EPSG:4326 as a match for the given CRS.
*/
assertEquals("Full scan should be enabled by default.", IdentifiedObjectFinder.Domain.VALID_DATASET, finder.getSearchDomain());
assertTrue("Should not find WGS84 because the axis order is not the same.", finder.find(crs.forConvention(AxesConvention.NORMALIZED)).isEmpty());
/*
* Ensure that the cache is empty.
*/
finder.setSearchDomain(IdentifiedObjectFinder.Domain.DECLARATION);
assertTrue("Should not find without a full scan, because the WKT contains no identifier " + "and the CRS name is ambiguous (more than one EPSG object have this name).", finder.find(crs).isEmpty());
/*
* Scan the database for searching the CRS.
*/
finder.setSearchDomain(IdentifiedObjectFinder.Domain.ALL_DATASET);
final IdentifiedObject found = finder.findSingleton(crs);
assertNotNull("With full scan allowed, the CRS should be found.", found);
assertEpsgNameAndIdentifierEqual("WGS 84", 4326, found);
/*
* Should find the CRS without the need of a full scan, because of the cache.
*/
finder.setSearchDomain(IdentifiedObjectFinder.Domain.DECLARATION);
assertSame("The CRS should still in the cache.", found, finder.findSingleton(crs));
}
use of org.opengis.referencing.IdentifiedObject in project sis by apache.
the class FeatureFormat method formatValue.
/**
* Appends the given attribute value, in a truncated form if it exceed the maximal value length.
*
* @param value the value to append.
* @param table where to append the value.
* @param length number of characters appended before this method call in the current table cell.
* @return number of characters appended after this method call in the current table cell, or -1 if
* the length exceed the maximal length (in which case the caller should break iteration).
*/
private int formatValue(final Object value, final TableAppender table, final int length) {
String text;
if (value instanceof InternationalString) {
text = ((InternationalString) value).toString(displayLocale);
} else if (value instanceof GenericName) {
text = toString((GenericName) value);
} else if (value instanceof AbstractIdentifiedType) {
text = toString(((AbstractIdentifiedType) value).getName());
} else if (value instanceof IdentifiedObject) {
text = IdentifiedObjects.getIdentifierOrName((IdentifiedObject) value);
} else if ((text = Geometries.toString(value)) == null) {
text = value.toString();
}
final int remaining = MAXIMAL_VALUE_LENGTH - length;
if (remaining >= text.length()) {
table.append(text);
return length + text.length();
} else {
table.append(text, 0, Math.max(0, remaining - 1)).append('…');
return -1;
}
}
use of org.opengis.referencing.IdentifiedObject in project sis by apache.
the class Formatter method append.
/**
* Appends the given {@code FormattableObject}.
* This method performs the following steps:
*
* <ul>
* <li>Invoke <code>object.{@linkplain FormattableObject#formatTo(Formatter) formatTo}(this)</code>.</li>
* <li>Prepend the keyword returned by the above method call (e.g. {@code "GEOCS"}).</li>
* <li>If the given object is an instance of {@link IdentifiedObject}, then append complementary information:</li>
* </ul>
*
* <blockquote><table class="sis">
* <caption>Complementary WKT elements</caption>
* <tr><th>WKT 2 element</th><th>WKT 1 element</th><th>For types</th></tr>
* <tr><td>{@code Anchor[…]}</td> <td></td> <td>{@link Datum}</td></tr>
* <tr><td>{@code Scope[…]}</td> <td></td> <td>{@link ReferenceSystem}, {@link Datum}, {@link CoordinateOperation}</td></tr>
* <tr><td>{@code Area[…]}</td> <td></td> <td>{@link ReferenceSystem}, {@link Datum}, {@link CoordinateOperation}</td></tr>
* <tr><td>{@code BBox[…]}</td> <td></td> <td>{@link ReferenceSystem}, {@link Datum}, {@link CoordinateOperation}</td></tr>
* <tr><td>{@code VerticalExtent[…]}</td><td></td> <td>{@link ReferenceSystem}, {@link Datum}, {@link CoordinateOperation}</td></tr>
* <tr><td>{@code TimeExtent[…]}</td> <td></td> <td>{@link ReferenceSystem}, {@link Datum}, {@link CoordinateOperation}</td></tr>
* <tr><td>{@code Id[…]}</td><td>{@code Authority[…]}</td><td>{@link IdentifiedObject}</td></tr>
* <tr><td>{@code Remarks[…]}</td> <td></td> <td>{@link ReferenceSystem}, {@link CoordinateOperation}</td></tr>
* </table></blockquote>
*
* @param object the formattable object to append to the WKT, or {@code null} if none.
*/
public void append(final FormattableObject object) {
if (object == null) {
return;
}
/*
* Safety check: ensure that we do not have circular dependencies (e.g. a ProjectedCRS contains
* a Conversion which may contain the ProjectedCRS as its target CRS). Without this protection,
* a circular dependency would cause an OutOfMemoryError.
*/
final int stackDepth = enclosingElements.size();
for (int i = stackDepth; --i >= 0; ) {
if (enclosingElements.get(i) == object) {
throw new IllegalStateException(Errors.getResources(locale).getString(Errors.Keys.CircularReference));
}
}
enclosingElements.add(object);
if (hasContextualUnit < 0) {
// Test if leftmost bit is set to 1.
throw new IllegalStateException(Errors.getResources(locale).getString(Errors.Keys.TreeDepthExceedsMaximum));
}
hasContextualUnit <<= 1;
/*
* Add a new line if it was requested, open the bracket and increase indentation in case the
* element to format contains other FormattableObject elements.
*/
appendSeparator();
int base = buffer.length();
elementStart = buffer.appendCodePoint(symbols.getOpeningBracket(0)).length();
indent(+1);
/*
* Formats the inner part, then prepend the WKT keyword.
* The result looks like the following:
*
* <previous text>,
* PROJCS["NAD27 / Idaho Central",
* GEOGCS[...etc...],
* ...etc...
*/
IdentifiedObject info = (object instanceof IdentifiedObject) ? (IdentifiedObject) object : null;
String keyword = object.formatTo(this);
if (keyword == null) {
if (info != null) {
setInvalidWKT(info, null);
} else {
setInvalidWKT(object.getClass(), null);
}
keyword = getName(object.getClass());
} else if (toUpperCase != 0) {
final Locale locale = symbols.getLocale();
keyword = (toUpperCase >= 0) ? keyword.toUpperCase(locale) : keyword.toLowerCase(locale);
}
if (highlightError && colors != null) {
final String color = colors.getAnsiSequence(ElementKind.ERROR);
if (color != null) {
buffer.insert(base, color + BACKGROUND_DEFAULT);
base += color.length();
}
}
highlightError = false;
buffer.insert(base, keyword);
/*
* When formatting geometry coordinates, we may need to shift all numbers by the width
* of the keyword inserted above in order to keep numbers properly aligned. Exemple:
*
* BOX[ 4.000 -10.000
* 50.000 2.000]
*/
if (keywordSpaceAt != null) {
final int length = keyword.length();
final CharSequence additionalMargin = CharSequences.spaces(keyword.codePointCount(0, length));
final int n = keywordSpaceAt.size();
for (int i = 0; i < n; ) {
int p = keywordSpaceAt.getInt(i);
// Take in account spaces added previously.
p += (++i * length);
buffer.insert(p, additionalMargin);
}
keywordSpaceAt.clear();
}
/*
* Format the SCOPE["…"], AREA["…"] and other elements. Some of those information
* are available only for Datum, CoordinateOperation and ReferenceSystem objects.
*/
if (info == null && convention.majorVersion() != 1 && object instanceof GeneralParameterValue) {
info = ((GeneralParameterValue) object).getDescriptor();
}
if (info != null) {
appendComplement(info, (stackDepth >= 1) ? enclosingElements.get(stackDepth - 1) : null, (stackDepth >= 2) ? enclosingElements.get(stackDepth - 2) : null);
}
/*
* Close the bracket, then update the queue of enclosed elements by removing this element.
*/
buffer.appendCodePoint(symbols.getClosingBracket(0));
indent(-1);
enclosingElements.remove(stackDepth);
hasContextualUnit >>>= 1;
}
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);
}
Aggregations