use of org.apache.sis.metadata.iso.ImmutableIdentifier in project sis by apache.
the class FrenchProfileTest method testReferenceSystemToAFNOR.
/**
* Tests {@link FrenchProfile#toAFNOR(ReferenceSystem, boolean)}.
*/
@Test
public void testReferenceSystemToAFNOR() {
ReferenceSystem std, fra;
std = new ReferenceSystemMetadata(new ImmutableIdentifier(null, "EPSG", "4326"));
fra = FrenchProfile.toAFNOR(std, false);
assertInstanceOf("Expected AFNOR instance.", DirectReferenceSystem.class, fra);
assertSame("Already an AFNOR instance.", fra, FrenchProfile.toAFNOR(fra));
fra = FrenchProfile.toAFNOR(std, true);
assertInstanceOf("Expected AFNOR instance.", IndirectReferenceSystem.class, fra);
assertSame("Already an AFNOR instance.", fra, FrenchProfile.toAFNOR(fra));
}
use of org.apache.sis.metadata.iso.ImmutableIdentifier in project sis by apache.
the class GeodeticObjectParser method parseMetadataAndClose.
/**
* Parses an <strong>optional</strong> metadata elements and close.
* This include elements like {@code "SCOPE"}, {@code "ID"} (WKT 2) or {@code "AUTHORITY"} (WKT 1).
* This WKT 1 element has the following pattern:
*
* {@preformat wkt
* AUTHORITY["<name>", "<code>"]
* }
*
* <div class="section">Fallback</div>
* The name is a mandatory property, but some invalid WKT with an empty string exist. In such case,
* we will use the name of the enclosed datum. Indeed, it is not uncommon to have the same name for
* a geographic CRS and its geodetic datum.
*
* @param parent the parent element.
* @param name the name of the parent object being parsed.
* @param fallback the fallback to use if {@code name} is empty.
* @return a properties map with the parent name and the optional authority code.
* @throws ParseException if an element can not be parsed.
*/
@SuppressWarnings("ReturnOfCollectionOrArrayField")
private Map<String, Object> parseMetadataAndClose(final Element parent, final String name, final IdentifiedObject fallback) throws ParseException {
properties.clear();
properties.put(IdentifiedObject.NAME_KEY, (name.isEmpty() && fallback != null) ? fallback.getName() : name);
Element element;
while ((element = parent.pullElement(OPTIONAL, ID_KEYWORDS)) != null) {
final String codeSpace = element.pullString("codeSpace");
// Accepts Integer as well as String.
final String code = element.pullObject("code").toString();
// Accepts Number as well as String.
final Object version = element.pullOptional(Object.class);
final Element citation = element.pullElement(OPTIONAL, WKTKeywords.Citation);
final String authority;
if (citation != null) {
authority = citation.pullString("authority");
citation.close(ignoredElements);
} else {
authority = codeSpace;
}
final Element uri = element.pullElement(OPTIONAL, WKTKeywords.URI);
if (uri != null) {
// TODO: not yet stored, since often redundant with other informations.
uri.pullString("URI");
uri.close(ignoredElements);
}
element.close(ignoredElements);
/*
* Note: we could be tempted to assign the authority to the name as well, like below:
*
* if (name instanceof String) {
* name = new NamedIdentifier(authority, (String) name);
* }
* properties.put(IdentifiedObject.NAME_KEY, name);
*
* However experience shows that it is often wrong in practice, because peoples often
* declare EPSG codes but still use WKT names much shorter than the EPSG names
* (for example "WGS84" for the datum instead than "World Geodetic System 1984"),
* so the name in WKT is often not compliant with the name actually defined by the authority.
*/
final ImmutableIdentifier id = new ImmutableIdentifier(Citations.fromName(authority), codeSpace, code, (version != null) ? version.toString() : null, null);
final Object previous = properties.put(IdentifiedObject.IDENTIFIERS_KEY, id);
if (previous != null) {
Identifier[] identifiers;
if (previous instanceof Identifier) {
identifiers = new Identifier[] { (Identifier) previous, id };
} else {
identifiers = (Identifier[]) previous;
final int n = identifiers.length;
identifiers = Arrays.copyOf(identifiers, n + 1);
identifiers[n] = id;
}
properties.put(IdentifiedObject.IDENTIFIERS_KEY, identifiers);
// REMINDER: values associated to IDENTIFIERS_KEY shall be recognized by 'toIdentifier(Object)'.
}
}
/*
* Other metadata (SCOPE, AREA, etc.). ISO 19162 said that at most one of each type shall be present,
* but our parser accepts an arbitrary amount of some kinds of metadata. They can be recognized by the
* 'while' loop.
*
* Most WKT do not contain any of those metadata, so we perform an 'isEmpty()' check as an optimization
* for those common cases.
*/
if (!parent.isEmpty()) {
/*
* Example: SCOPE["Large scale topographic mapping and cadastre."]
*/
element = parent.pullElement(OPTIONAL, WKTKeywords.Scope);
if (element != null) {
// Other types like Datum use the same key.
properties.put(ReferenceSystem.SCOPE_KEY, element.pullString("scope"));
element.close(ignoredElements);
}
/*
* Example: AREA["Netherlands offshore."]
*/
DefaultExtent extent = null;
while ((element = parent.pullElement(OPTIONAL, WKTKeywords.Area)) != null) {
final String area = element.pullString("area");
element.close(ignoredElements);
if (extent == null) {
extent = new DefaultExtent(area, null, null, null);
} else {
extent.getGeographicElements().add(new DefaultGeographicDescription(area));
}
}
/*
* Example: BBOX[51.43, 2.54, 55.77, 6.40]
*/
while ((element = parent.pullElement(OPTIONAL, WKTKeywords.BBox)) != null) {
final double southBoundLatitude = element.pullDouble("southBoundLatitude");
final double westBoundLongitude = element.pullDouble("westBoundLongitude");
final double northBoundLatitude = element.pullDouble("northBoundLatitude");
final double eastBoundLongitude = element.pullDouble("eastBoundLongitude");
element.close(ignoredElements);
if (extent == null)
extent = new DefaultExtent();
extent.getGeographicElements().add(new DefaultGeographicBoundingBox(westBoundLongitude, eastBoundLongitude, southBoundLatitude, northBoundLatitude));
}
/*
* Example: VERTICALEXTENT[-1000, 0, LENGTHUNIT[“metre”, 1]]
*
* Units are optional, default to metres (no "contextual units" here).
*/
while ((element = parent.pullElement(OPTIONAL, WKTKeywords.VerticalExtent)) != null) {
final double minimum = element.pullDouble("minimum");
final double maximum = element.pullDouble("maximum");
Unit<Length> unit = parseScaledUnit(element, WKTKeywords.LengthUnit, Units.METRE);
element.close(ignoredElements);
if (unit == null)
unit = Units.METRE;
if (extent == null)
extent = new DefaultExtent();
verticalElements = new VerticalInfo(verticalElements, extent, minimum, maximum, unit).resolve(verticalCRS);
}
/*
* Example: TIMEEXTENT[2013-01-01, 2013-12-31]
*
* TODO: syntax like TIMEEXTENT[“Jurassic”, “Quaternary”] is not yet supported.
* See https://issues.apache.org/jira/browse/SIS-163
*
* This operation requires the the sis-temporal module. If not available,
* we will report a warning and leave the temporal extent missing.
*/
while ((element = parent.pullElement(OPTIONAL, WKTKeywords.TimeExtent)) != null) {
if (element.peekValue() instanceof String) {
element.pullString("startTime");
element.pullString("endTime");
element.close(ignoredElements);
warning(parent, element, Errors.formatInternational(Errors.Keys.UnsupportedType_1, "TimeExtent[String,String]"), null);
} else {
final Date startTime = element.pullDate("startTime");
final Date endTime = element.pullDate("endTime");
element.close(ignoredElements);
try {
final DefaultTemporalExtent t = new DefaultTemporalExtent();
t.setBounds(startTime, endTime);
if (extent == null)
extent = new DefaultExtent();
extent.getTemporalElements().add(t);
} catch (UnsupportedOperationException e) {
warning(parent, element, null, e);
}
}
}
if (extent != null) {
properties.put(ReferenceSystem.DOMAIN_OF_VALIDITY_KEY, extent);
}
/*
* Example: REMARK["Замечание на русском языке"]
*/
element = parent.pullElement(OPTIONAL, WKTKeywords.Remark);
if (element != null) {
properties.put(IdentifiedObject.REMARKS_KEY, element.pullString("remarks"));
element.close(ignoredElements);
}
}
parent.close(ignoredElements);
return properties;
}
use of org.apache.sis.metadata.iso.ImmutableIdentifier 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.apache.sis.metadata.iso.ImmutableIdentifier in project sis by apache.
the class ServicesForUtility method createCitation.
/**
* Returns the build-in citation for the given primary key, or {@code null}.
*
* @param key the primary key of the desired citation.
* @return the requested citation, or {@code null} if unknown.
*
* @todo The content is hard-coded for now. But the plan for a future version is to fetch richer information
* from a database, including for example the responsible party and the URL. However that method would
* need to make sure that the given key is present in the alternate titles, since we rely on that when
* checking for code spaces.
*/
public static Citation createCitation(final String key) {
CharSequence title;
CharSequence alternateTitle = null;
CharSequence edition = null;
String code = null;
String codeSpace = null;
String version = null;
Identifier[] alternateIdentifiers = null;
CharSequence citedResponsibleParty = null;
PresentationForm presentationForm = null;
// Copy citedResponsibleParty from those citations.
Citation[] copyFrom = null;
switch(key) {
case "ISO 19115-1":
{
title = "Geographic Information — Metadata Part 1: Fundamentals";
edition = "ISO 19115-1:2014(E)";
code = "19115-1";
codeSpace = "ISO";
version = "2014(E)";
citedResponsibleParty = "International Organization for Standardization";
presentationForm = PresentationForm.DOCUMENT_DIGITAL;
break;
}
case "ISO 19115-2":
{
title = "Geographic Information — Metadata Part 2: Extensions for imagery and gridded data";
edition = "ISO 19115-2:2009(E)";
code = "19115-2";
codeSpace = "ISO";
version = "2009(E)";
copyFrom = new Citation[] { Citations.ISO_19115.get(0) };
presentationForm = PresentationForm.DOCUMENT_DIGITAL;
break;
}
case "WMS":
{
// OGC title
title = "Web Map Server";
// ISO title
alternateTitle = "Geographic Information — Web map server interface";
alternateIdentifiers = new Identifier[] { new ImmutableIdentifier(null, "OGC", "06-042", null, null), new ImmutableIdentifier(null, "ISO", "19128", "2005", null) };
edition = "1.3";
code = "WMS";
codeSpace = "OGC";
copyFrom = new Citation[] { Citations.OGC, Citations.ISO_19115.get(0) };
presentationForm = PresentationForm.DOCUMENT_DIGITAL;
break;
}
case Constants.OGC:
{
title = "Identifiers in OGC namespace";
code = Constants.OGC;
citedResponsibleParty = "Open Geospatial Consortium";
presentationForm = PresentationForm.DOCUMENT_DIGITAL;
break;
}
case Constants.IOGP:
{
// Not in public API (see Citations.IOGP javadoc)
// Geomatics Guidance Note number 7, part 1
title = "Using the EPSG Geodetic Parameter Dataset";
code = Constants.IOGP;
copyFrom = new Citation[] { Citations.EPSG };
presentationForm = PresentationForm.DOCUMENT_DIGITAL;
break;
}
case Constants.EPSG:
{
title = "EPSG Geodetic Parameter Dataset";
code = Constants.EPSG;
codeSpace = Constants.IOGP;
citedResponsibleParty = "International Association of Oil & Gas producers";
presentationForm = PresentationForm.TABLE_DIGITAL;
/*
* More complete information is provided as an ISO 19115 structure
* in EPSG Surveying and Positioning Guidance Note Number 7, part 1.
* EPSGDataAccess.getAuthority() also add more information.
* After we moved the content of this citation in a database,
* EPSGDataAccess.getAuthority() should use this citation as a template.
*/
break;
}
case Constants.SIS:
{
title = "Apache Spatial Information System";
code = key;
break;
}
case "ISBN":
{
title = "International Standard Book Number";
alternateTitle = key;
break;
}
case "ISSN":
{
title = "International Standard Serial Number";
alternateTitle = key;
break;
}
case Constants.PROJ4:
{
title = "Proj.4";
break;
}
case "S57":
{
title = "S-57";
break;
}
default:
return null;
}
/*
* Do not use the 'c.getFoo().add(foo)' pattern below. Use the 'c.setFoo(singleton(foo))' pattern instead.
* This is because this method may be invoked during XML serialization, in which case some getter methods
* may return null (for preventing JAXB to marshal some empty elements).
*/
final DefaultCitation c = new DefaultCitation(title);
if (alternateTitle != null)
c.setAlternateTitles(singleton(Types.toInternationalString(alternateTitle)));
if (edition != null)
c.setEdition(Types.toInternationalString(edition));
if (code != null)
c.setIdentifiers(singleton(new ImmutableIdentifier(null, codeSpace, code, version, null)));
if (presentationForm != null)
c.setPresentationForms(singleton(presentationForm));
if (citedResponsibleParty != null) {
final DefaultResponsibleParty r = new DefaultResponsibleParty(Role.PRINCIPAL_INVESTIGATOR);
r.setParties(singleton(new DefaultOrganisation(citedResponsibleParty, null, null, null)));
c.setCitedResponsibleParties(singleton(r));
}
if (copyFrom != null) {
for (final Citation other : copyFrom) {
final Collection<? extends ResponsibleParty> parties = other.getCitedResponsibleParties();
final Collection<ResponsibleParty> current = c.getCitedResponsibleParties();
if (current != null) {
current.addAll(parties);
} else {
c.setCitedResponsibleParties(parties);
}
}
}
if (alternateIdentifiers != null) {
// getIdentifiers() should not return null at this point.
c.getIdentifiers().addAll(Arrays.asList(alternateIdentifiers));
}
c.freeze();
return c;
}
use of org.apache.sis.metadata.iso.ImmutableIdentifier in project sis by apache.
the class CodeTest method testLegacyCodeSpace.
/**
* Tests {@link Code#forIdentifiedObject(Class, Iterable)} with the legacy "OGP" codespace
* (instead of "IOGP").
*/
@Test
@DependsOnMethod("testForIdentifiedObject")
public void testLegacyCodeSpace() {
final DefaultCitation authority = new DefaultCitation("EPSG");
authority.getIdentifiers().add(new ImmutableIdentifier(null, "OGP", "EPSG"));
final ReferenceIdentifier id = new ImmutableIdentifier(authority, "EPSG", "4326", "8.2", null);
final Code value = Code.forIdentifiedObject(GeographicCRS.class, Collections.singleton(id));
assertNotNull(value);
assertEquals("codeSpace", "OGP", value.codeSpace);
assertEquals("code", "urn:ogc:def:crs:EPSG:8.2:4326", value.code);
}
Aggregations