use of org.opengis.util.InternationalString in project sis by apache.
the class MetadataReader method addCitation.
/**
* Adds a {@code DataIdentification/Citation} element if at least one of the required attributes is non-null.
* This method will initialize the {@link #pointOfContact} field, than reuse it if non-null and suitable.
*
* <p>This method opportunistically collects the name of all publishers.
* Those names are useful to {@link #addIdentificationInfo(Set)}.</p>
*
* @return the name of all publishers, or {@code null} if none.
*/
private Set<InternationalString> addCitation() {
String title = stringValue(TITLE);
if (title == null) {
// THREDDS attribute documented in TITLE javadoc.
title = stringValue("full_name");
if (title == null) {
// THREDDS attribute documented in TITLE javadoc.
title = stringValue("name");
if (title == null) {
title = decoder.getTitle();
}
}
}
addTitle(title);
addEdition(stringValue(PRODUCT_VERSION));
addOtherCitationDetails(stringValue(REFERENCES));
addCitationDate(decoder.dateValue(METADATA_CREATION), DateType.CREATION, Scope.ALL);
addCitationDate(decoder.dateValue(METADATA_MODIFIED), DateType.REVISION, Scope.ALL);
addCitationDate(decoder.dateValue(DATE_CREATED), DateType.CREATION, Scope.RESOURCE);
addCitationDate(decoder.dateValue(DATE_MODIFIED), DateType.REVISION, Scope.RESOURCE);
addCitationDate(decoder.dateValue(DATE_ISSUED), DateType.PUBLICATION, Scope.RESOURCE);
/*
* Add the responsible party which is declared in global attributes, or in
* the THREDDS attributes if no information was found in global attributes.
* This responsible party is taken as the point of contact.
*/
for (final String path : searchPath) {
decoder.setSearchPath(path);
final ResponsibleParty party = createResponsibleParty(CREATOR, true);
if (party != pointOfContact) {
addPointOfContact(party, Scope.ALL);
if (pointOfContact == null) {
pointOfContact = party;
}
}
}
/*
* There is no distinction in netCDF files between "point of contact" and "creator".
* We take the first one as the data originator.
*/
addCitedResponsibleParty(pointOfContact, Role.ORIGINATOR);
/*
* Add the contributors only after we did one full pass over the creators. We keep those two
* loops separated in order to increase the chances that pointOfContact has been initialized
* (it may not have been initialized on the first pass).
*/
Set<InternationalString> publisher = null;
for (final String path : searchPath) {
decoder.setSearchPath(path);
final ResponsibleParty contributor = createResponsibleParty(CONTRIBUTOR, false);
if (contributor != pointOfContact) {
addCitedResponsibleParty(contributor, null);
}
final ResponsibleParty r = createResponsibleParty(PUBLISHER, false);
if (r != null) {
addDistributor(r);
/*
* TODO: There is some transfert option, etc. that we could set there.
* See UnidataDD2MI.xsl for options for OPeNDAP, THREDDS, etc.
*/
publisher = addIfNonNull(publisher, r.getOrganisationName());
publisher = addIfNonNull(publisher, toInternationalString(r.getIndividualName()));
}
}
decoder.setSearchPath(searchPath);
return publisher;
}
use of org.opengis.util.InternationalString in project sis by apache.
the class EPSGDataAccess method createParameterDescriptor.
/**
* Creates a definition of a single parameter used by an operation method.
*
* <div class="note"><b>Example:</b>
* some EPSG codes for parameters are:
*
* <table class="sis" summary="EPSG codes examples">
* <tr><th>Code</th> <th>Description</th></tr>
* <tr><td>8801</td> <td>Latitude of natural origin</td></tr>
* <tr><td>8802</td> <td>Longitude of natural origin</td></tr>
* <tr><td>8805</td> <td>Scale factor at natural origin</td></tr>
* <tr><td>8806</td> <td>False easting</td></tr>
* <tr><td>8807</td> <td>False northing</td></tr>
* </table></div>
*
* @param code value allocated by EPSG.
* @return the parameter descriptor 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 org.apache.sis.parameter.DefaultParameterDescriptor
*/
@Override
public synchronized ParameterDescriptor<?> createParameterDescriptor(final String code) throws NoSuchAuthorityCodeException, FactoryException {
ArgumentChecks.ensureNonNull("code", code);
ParameterDescriptor<?> returnValue = null;
try (ResultSet result = executeQuery("Coordinate_Operation Parameter", "PARAMETER_CODE", "PARAMETER_NAME", "SELECT PARAMETER_CODE," + " PARAMETER_NAME," + " DESCRIPTION," + " DEPRECATED" + " FROM [Coordinate_Operation Parameter]" + " WHERE PARAMETER_CODE = ?", code)) {
while (result.next()) {
final Integer epsg = getInteger(code, result, 1);
final String name = getString(code, result, 2);
final String description = getOptionalString(result, 3);
final boolean deprecated = getOptionalBoolean(result, 4);
Class<?> type = Double.class;
/*
* If the parameter appears to have at least one non-null value in the "Parameter File Name" column,
* then the type is assumed to be URI as a string. Otherwise, the type is a floating point number.
*/
try (ResultSet r = executeQuery("ParameterType", "SELECT PARAM_VALUE_FILE_REF FROM [Coordinate_Operation Parameter Value]" + " WHERE (PARAMETER_CODE = ?) AND PARAM_VALUE_FILE_REF IS NOT NULL", epsg)) {
while (r.next()) {
String element = getOptionalString(r, 1);
if (element != null && !element.isEmpty()) {
type = String.class;
break;
}
}
}
/*
* Search for units. We typically have many different units but all of the same dimension
* (for example metres, kilometres, feet, etc.). In such case, the units Set will have only
* one element and that element will be the most frequently used unit. But some parameters
* accept units of different dimensions. For example the "Ordinate 1 of evaluation point"
* (EPSG:8617) parameter value may be in metres or in degrees. In such case the units Set
* will have two elements.
*/
final Set<Unit<?>> units = new LinkedHashSet<>();
try (ResultSet r = executeQuery("ParameterUnit", "SELECT UOM_CODE FROM [Coordinate_Operation Parameter Value]" + " WHERE (PARAMETER_CODE = ?)" + " GROUP BY UOM_CODE" + " ORDER BY COUNT(UOM_CODE) DESC", epsg)) {
next: while (r.next()) {
final String c = getOptionalString(r, 1);
if (c != null) {
final Unit<?> candidate = owner.createUnit(c);
for (final Unit<?> e : units) {
if (candidate.isCompatible(e)) {
continue next;
}
}
units.add(candidate);
}
}
}
/*
* Determines if the inverse operation can be performed by reversing the parameter sign.
* The EPSG dataset uses "Yes" or "No" value, but SIS scripts use boolean type. We have
* to accept both. Note that if we do not recognize the string as a boolean value, then
* we need a SQLException, not a null value. If the value is wrongly null, this method
* will succeed anyway and EPSGDataAccess will finish its work without apparent problem,
* but Apache SIS will fail later when it will try to compute the inverse operation, for
* example in a call to CRS.findOperation(…). The exception thrown at such later time is
* much more difficult to relate to the root cause than if we throw the exception here.
*/
InternationalString isReversible = null;
try (ResultSet r = executeQuery("ParameterSign", "SELECT DISTINCT PARAM_SIGN_REVERSAL FROM [Coordinate_Operation Parameter Usage]" + " WHERE (PARAMETER_CODE = ?)", epsg)) {
if (r.next()) {
Boolean b;
if (translator.useBoolean()) {
b = r.getBoolean(1);
if (r.wasNull())
b = null;
} else {
// May throw SQLException - see above comment.
b = SQLUtilities.toBoolean(r.getString(1));
}
if (b != null) {
isReversible = b ? SignReversalComment.OPPOSITE : SignReversalComment.SAME;
}
}
}
/*
* Now creates the parameter descriptor.
*/
final NumberRange<?> valueDomain;
switch(units.size()) {
case 0:
valueDomain = null;
break;
default:
valueDomain = new EPSGParameterDomain(units);
break;
case 1:
valueDomain = MeasurementRange.create(Double.NEGATIVE_INFINITY, false, Double.POSITIVE_INFINITY, false, CollectionsExt.first(units));
break;
}
final Map<String, Object> properties = createProperties("Coordinate_Operation Parameter", name, epsg, isReversible, deprecated);
properties.put(ImmutableIdentifier.DESCRIPTION_KEY, description);
final ParameterDescriptor<?> descriptor = new DefaultParameterDescriptor<>(properties, 1, 1, type, valueDomain, null, null);
returnValue = ensureSingleton(descriptor, returnValue, code);
}
} catch (SQLException exception) {
throw databaseFailure(OperationMethod.class, code, exception);
}
if (returnValue == null) {
throw noSuchAuthorityCode(OperationMethod.class, code);
}
return returnValue;
}
use of org.opengis.util.InternationalString in project sis by apache.
the class EPSGDataAccess method getAuthority.
/**
* Returns the authority for this EPSG dataset. The returned citation contains the database version
* in the {@linkplain Citation#getEdition() edition} attribute, together with date of last update in
* the {@linkplain Citation#getEditionDate() edition date}.
* Example (the exact content will vary with Apache SIS versions, JDBC driver and EPSG dataset versions):
*
* {@preformat text
* Citation
* ├─ Title ……………………………………………………… EPSG Geodetic Parameter Dataset
* ├─ Identifier ………………………………………… EPSG
* ├─ Online resource (1 of 2)
* │ ├─ Linkage ………………………………………… http://epsg-registry.org/
* │ └─ Function ……………………………………… Browse
* └─ Online resource (2 of 2)
* ├─ Linkage ………………………………………… jdbc:derby:/my/path/to/SIS_DATA/Databases/SpatialMetadata
* ├─ Description ……………………………… EPSG dataset version 8.9 on “Apache Derby Embedded JDBC Driver” version 10.12.
* └─ Function ……………………………………… Connection
* }
*/
@Override
public synchronized Citation getAuthority() {
/*
* We do not cache this citation because the caching service is already provided by ConcurrentAuthorityFactory.
*/
final DefaultCitation c = new DefaultCitation("EPSG Geodetic Parameter Dataset");
c.setIdentifiers(Collections.singleton(new ImmutableIdentifier(null, null, Constants.EPSG)));
try {
/*
* Get the most recent version number from the history table. We get the date in local timezone
* instead then UTC because the date is for information purpose only, and the local timezone is
* more likely to be shown nicely (without artificial hours) to the user.
*/
final String query = translator.apply("SELECT VERSION_NUMBER, VERSION_DATE FROM [Version History]" + " ORDER BY VERSION_DATE DESC, VERSION_HISTORY_CODE DESC");
String version = null;
try (Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery(query)) {
while (result.next()) {
version = getOptionalString(result, 1);
// Local timezone.
final Date date = result.getDate(2);
if (version != null && date != null) {
// Paranoiac check.
c.setEdition(new SimpleInternationalString(version));
c.setEditionDate(date);
break;
}
}
}
/*
* Add some hard-coded links to EPSG resources, and finally add the JDBC driver name and version number.
* The list last OnlineResource looks like:
*
* Linkage: jdbc:derby:/my/path/to/SIS_DATA/Databases/SpatialMetadata
* Function: Connection
* Description: EPSG dataset version 8.9 on “Apache Derby Embedded JDBC Driver” version 10.12.
*
* TODO: A future version should use Citations.EPSG as a template.
* See the "EPSG" case in ServiceForUtility.createCitation(String).
*/
final DatabaseMetaData metadata = connection.getMetaData();
addURIs: for (int i = 0; ; i++) {
String url;
OnLineFunction function;
InternationalString description = null;
switch(i) {
case 0:
url = "http://epsg-registry.org/";
function = OnLineFunction.SEARCH;
break;
case 1:
url = "http://www.epsg.org/";
function = OnLineFunction.DOWNLOAD;
break;
case 2:
{
url = SQLUtilities.getSimplifiedURL(metadata);
function = OnLineFunction.valueOf(CONNECTION);
description = Resources.formatInternational(Resources.Keys.GeodeticDataBase_4, Constants.EPSG, version, metadata.getDatabaseProductName(), Version.valueOf(metadata.getDatabaseMajorVersion(), metadata.getDatabaseMinorVersion()));
break;
}
// Finished adding all URIs.
default:
break addURIs;
}
final DefaultOnlineResource r = new DefaultOnlineResource();
try {
r.setLinkage(new URI(url));
} catch (URISyntaxException exception) {
unexpectedException("getAuthority", exception);
}
r.setFunction(function);
r.setDescription(description);
c.getOnlineResources().add(r);
}
} catch (SQLException exception) {
unexpectedException("getAuthority", exception);
} finally {
c.freeze();
}
return c;
}
use of org.opengis.util.InternationalString in project sis by apache.
the class CommonAuthorityFactory method getDescriptionText.
/**
* Returns a description of the object corresponding to a code.
* The description can be used for example in a combo box in a graphical user interface.
*
* <p>Codes in the {@code "AUTO(2)"} namespace do not need parameters for this method.
* But if parameters are nevertheless specified, then they will be taken in account.</p>
*
* <table class="sis">
* <caption>Examples</caption>
* <tr><th>Argument value</th> <th>Return value</th></tr>
* <tr><td>{@code CRS:84}</td> <td>WGS 84</td></tr>
* <tr><td>{@code AUTO2:42001}</td> <td>WGS 84 / Auto UTM</td></tr>
* <tr><td>{@code AUTO2:42001,1,-100,45}</td> <td>WGS 84 / UTM zone 47N</td></tr>
* </table>
*
* @param code value in the CRS or AUTO(2) code space.
* @return a description of the object.
* @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
* @throws FactoryException if an error occurred while fetching the description.
*/
@Override
public InternationalString getDescriptionText(final String code) throws FactoryException {
final int s = skipNamespace(code) & ~LEGACY_MASK;
final String localCode = code.substring(s, CharSequences.skipTrailingWhitespaces(code, s, code.length()));
if (localCode.indexOf(SEPARATOR) < 0) {
/*
* For codes in the "AUTO(2)" namespace without parameters, we can not rely on the default implementation
* since it would fail to create the ProjectedCRS instance. Instead we return a generic description.
* Note that we do not execute this block if parametes were specified. If there is parameters,
* then we instead rely on the default implementation for a more accurate description text.
*/
final int codeValue;
try {
codeValue = Integer.parseInt(localCode);
} catch (NumberFormatException exception) {
throw noSuchAuthorityCode(localCode, code, exception);
}
final int i = codeValue - FIRST_PROJECTION_CODE;
if (i >= 0 && i < PROJECTION_NAMES.length) {
return new SimpleInternationalString(PROJECTION_NAMES[i]);
}
}
return new SimpleInternationalString(createCoordinateReferenceSystem(localCode).getName().getCode());
}
use of org.opengis.util.InternationalString in project sis by apache.
the class InverseOperationMethod method properties.
/**
* Infers the properties to give to an inverse coordinate operation.
* The returned map will contains three kind of information:
*
* <ul>
* <li>Metadata (domain of validity, accuracy)</li>
* <li>Parameter values, if possible</li>
* </ul>
*
* This method copies accuracy and domain of validity metadata from the given operation.
* We presume that the inverse operation has the same accuracy than the direct operation.
*
* <div class="note"><b>Note:</b>
* in many cases, the inverse operation is numerically less accurate than the direct operation because it
* uses approximations like series expansions or iterative methods. However the numerical errors caused by
* those approximations are not of interest here, because they are usually much smaller than the inaccuracy
* due to the stochastic nature of coordinate transformations (not to be confused with coordinate conversions;
* see ISO 19111 for more information).</div>
*
* If the inverse of the given operation can be represented by inverting the sign of all numerical
* parameter values, then this method copies also those parameters in a {@code "parameters"} entry.
*
* @param source the operation for which to get the inverse parameters.
* @param target where to store the inverse parameters.
*/
static void properties(final SingleOperation source, final Map<String, Object> target) {
target.put(SingleOperation.DOMAIN_OF_VALIDITY_KEY, source.getDomainOfValidity());
final Collection<PositionalAccuracy> accuracy = source.getCoordinateOperationAccuracy();
if (!Containers.isNullOrEmpty(accuracy)) {
target.put(SingleOperation.COORDINATE_OPERATION_ACCURACY_KEY, accuracy.toArray(new PositionalAccuracy[accuracy.size()]));
}
/*
* If the inverse of the given operation can be represented by inverting the sign of all numerical
* parameter values, copies those parameters in a "parameters" entry in the properties map.
* Otherwise does nothing.
*/
final ParameterValueGroup parameters = source.getParameterValues();
final ParameterValueGroup copy = parameters.getDescriptor().createValue();
for (final GeneralParameterValue gp : parameters.values()) {
if (gp instanceof ParameterValue<?>) {
final ParameterValue<?> src = (ParameterValue<?>) gp;
final Object value = src.getValue();
if (value instanceof Number) {
final ParameterDescriptor<?> descriptor = src.getDescriptor();
final InternationalString remarks = descriptor.getRemarks();
if (remarks != SignReversalComment.SAME) {
if (remarks != SignReversalComment.OPPOSITE) {
/*
* The parameter descriptor does not specify whether the values for the inverse operation
* have the same sign or opposite sign. We could heuristically presume that we can invert
* the sign if the minimum value has the opposite sign than the maximum value (as in the
* [-10 … 10] range), but such assumption is dangerous. For example the values in a matrix
* could be bounded to a range like [-1 … 1], which would mislead above heuristic rule.
*
* Note that abandoning here does not mean that we will never know the parameter values.
* As a fallback, AbstractCoordinateOperation will try to get the parameter values from
* the MathTransform. This is the appropriate thing to do at least for Affine operation.
*/
return;
}
/*
* The parameter value of the inverse operation is (or is presumed to be) the negative of
* the parameter value of the source operation. We need to preserve units of measurement
* if they were specified.
*/
final ParameterValue<?> tgt = copy.parameter(descriptor.getName().getCode());
final Unit<?> unit = src.getUnit();
if (unit != null) {
tgt.setValue(-src.doubleValue(), unit);
} else if (value instanceof Integer || value instanceof Short || value instanceof Byte) {
tgt.setValue(-src.intValue());
} else {
tgt.setValue(-src.doubleValue());
}
// No need to add 'tgt' to 'copy' since it was done by the call to copy.parameter(…).
continue;
}
}
}
copy.values().add(gp);
}
target.put(ReferencingServices.PARAMETERS_KEY, copy);
}
Aggregations