You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2023/11/21 14:19:48 UTC
(sis-site) 02/02: Better documentation about the handling of NilReason and the view as TreeTableMap.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/sis-site.git
commit 6c4b10acff054f46d21caf4bd7e115061664aa82
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Nov 21 14:25:06 2023 +0100
Better documentation about the handling of NilReason and the view as TreeTableMap.
---
.../developer-guide/introduction/Conventions.html | 4 +-
.../developer-guide/introduction/DataAccess.html | 3 +-
.../developer-guide/metadata/NavigateMetadata.html | 168 ++++++++++++++++++---
content/developer-guide/metadata/NilReason.html | 60 +++++++-
content/developer-guide/metadata/index.html | 3 +-
static/book/book.css | 14 ++
6 files changed, 224 insertions(+), 28 deletions(-)
diff --git a/content/developer-guide/introduction/Conventions.html b/content/developer-guide/introduction/Conventions.html
index 9202e257..420016e8 100644
--- a/content/developer-guide/introduction/Conventions.html
+++ b/content/developer-guide/introduction/Conventions.html
@@ -33,7 +33,7 @@
-->
<section>
<header>
- <h2 id="TypographicConventions">Typographic conventions</h2>
+ <h2 id="TypographicConventions">Typographic conventions in this document</h2>
</header>
<p>
The elements defined in a computer language, such as classes and methods in Java or elements in an <abbr>XML</abbr> document,
@@ -66,7 +66,7 @@
</section>
<section>
<header>
- <h2 id="CodingConventions">Coding conventions</h2>
+ <h2 id="CodingConventions">Coding conventions in Apache <abbr>SIS</abbr></h2>
</header>
<p>
Apache SIS implements most GeoAPI interfaces by a classes of the same name than the interface
diff --git a/content/developer-guide/introduction/DataAccess.html b/content/developer-guide/introduction/DataAccess.html
index f7039a12..fc9ce044 100644
--- a/content/developer-guide/introduction/DataAccess.html
+++ b/content/developer-guide/introduction/DataAccess.html
@@ -70,8 +70,7 @@
For example, the following code read a metadata file from a Landsat-8 image and prints the declared geographic bounding box:
</p>
-<pre><code>
-import org.opengis.metadata.Metadata;
+<pre><code>import org.opengis.metadata.Metadata;
import org.opengis.metadata.extent.GeographicBoundingBox;
import org.apache.sis.storage.DataStore;
import org.apache.sis.storage.DataStores;
diff --git a/content/developer-guide/metadata/NavigateMetadata.html b/content/developer-guide/metadata/NavigateMetadata.html
index a8883b06..578ccdfe 100644
--- a/content/developer-guide/metadata/NavigateMetadata.html
+++ b/content/developer-guide/metadata/NavigateMetadata.html
@@ -44,7 +44,7 @@
</p>
<ul>
<li>As key-value pairs in a <code>Map</code> (from <code>java.util</code>).</li>
- <li>As a <code>TreeTable</code> (from <code>org.apache.sis.util.collection</code>).</li>
+ <li>As a <code class="SIS">TreeTable</code> (from <code>org.apache.sis.util.collection</code>).</li>
<li>As a table record in a database (using <code>org.apache.sis.metadata.sql</code>).</li>
<li>As an <abbr>XML</abbr> document conforms to <abbr>ISO</abbr> standard schema.</li>
</ul>
@@ -56,13 +56,22 @@
<h3 id="GetMetadataElement">Direct access via getter methods</h3>
<p>
All metadata classes provide getter, and sometime setter, methods for their properties.
- Some properties accept many values, in which case the property type is a collection.
- The following example prints the range of latitudes of all data descriptions
- found in a given root <code>Metadata</code> object:
+ The following example prints all ranges of latitudes found in a metadata.
+ It may be, for example, a metadata describing the extent of a raster file.
+ Fetching that information requires navigating through the following steps:
+ </p><p style="text-align:center">
+ <code class="API">Metadata</code>
+ ⟶ <code class="OGC">identificationInfo</code>
+ ⟶ <code class="OGC">extent</code>
+ ⟶ <code class="OGC">geographicElement</code>
+ ⟶ (<code class="OGC">southBoundLatitude</code>, <code class="OGC">northBoundLatitude</code>)
+ </p><p>
+ Some properties accept many values, in which case the code must iterate over the elements of a collection.
+ Furthermore, some properties are available only in a sub-interface, in which case <code>instanceof</code>
+ checks are required. The resulting code is as below:
</p>
-<pre><code>
-import org.opengis.metadata.metadata;
+<pre><code>import org.opengis.metadata.metadata;
import org.opengis.metadata.extent.Extent;
import org.opengis.metadata.extent.GeographicExtent;
import org.opengis.metadata.extent.GeographicBoundingBox;
@@ -95,14 +104,16 @@ void main() {
For example the following method navigates through different branches where North, South, East and West data bounds may be found:
</p>
-<pre><code>
-import org.opengis.metadata.metadata;
+<pre><code>import org.opengis.metadata.metadata;
import org.opengis.metadata.extent.GeographicBoundingBox;
import org.apache.sis.metadata.iso.extent.Extents;
void main() {
Metadata metadata = ...; // For example, metadata read from a data store.
GeographicBoundingBox bbox = Extents.getGeographicBoundingBox(extent);
+ if (box != null) {
+ // Same as in the previous example.
+ }
}</code></pre>
<p>
@@ -111,6 +122,88 @@ void main() {
<code>Citations</code>, <code>Envelopes</code>, <code>Matrices</code> and <code>MathTransforms</code>.
</p>
+
+
+
+ <h3 id="SetMetadataElement">Direct construction via setter methods</h3>
+ <p>
+ For every getter method, there is a corresponding setter method.
+ However the setter methods are not defined in the GeoAPI interfaces, which are read-only.
+ For invoking setter methods, it is necessary to use the Apache <abbr>SIS</abbr> implementation classes directly.
+ It is straightforward when the metadata objects are created and populated in the same code, as there is nothing special to do.
+ The following example creates a citation using three different ways to specify a value:
+ at construction time using convenience constructors,
+ using setter methods,
+ and by adding elements directly in the collections, which are modifiable.
+ </p>
+<pre><code>import org.apache.sis.metadata.iso.DefaultIdentifier;
+import org.apache.sis.metadata.iso.citation.DefaultCitation;
+import org.opengis.metadata.citation.PresentationForm; // A code list.
+
+void main() {
+ // Following constructor is a convenience for setting the `title` property at construction time.
+ var citation = new DefaultCitation("Climate Change 2022: Impacts, Adaptation, and Vulnerability");
+ citation.getPresentationForms().add(PresentationForm.DOCUMENT_DIGITAL);
+
+ var identifier = new DefaultIdentifier();
+ identifier.setCode("10.1017/9781009325844");
+ identifier.setCodeSpace("DOI");
+ citation.getIdentifiers().add(identifier);
+
+ // Do not allow the citation to be modified after this point (optional).
+ citation.transitionTo(DefaultCitation.State.FINAL);
+ System.out.println(citation);
+}</code></pre>
+
+ <p>
+ Output is as below:
+ </p>
+
+<pre><samp>Citation……………………………………… Climate Change 2022: Impacts, Adaptation, and Vulnerability
+ ├─Identifier……………………… 10.1017/9781009325844
+ │ └─Code space…………… DOI
+ └─Presentation form…… Document digital</samp></pre>
+
+ <p>
+ Often, the newly created metadata will be reused many times.
+ It may be for example a static final constant, or a cached value.
+ In such case, it may be desirable to declare the metadata as unmodifiable after its construction.
+ This is the purpose of the call to <code class="SIS">transitionTo(State.FINAL)</code> in above example.
+ After that call, any attempt to modify the metadata will cause an <code class="SIS">UnmodifiableMetadataException</code> to be thrown.
+ </p><p>
+ Setting the values of a metadata object may be more tricky when the implementation class is unknown.
+ Since there is no setter methods in the GeoAPI interfaces, the metadata may need to be converted to
+ an Apache <abbr>SIS</abbr> implementation class first, and that instance needs to be modifiable.
+ This is a two steps process.
+ The first step can be done by invoking the static <code class="SIS">castOrCopy(…)</code> method
+ which is defined in every Apache <abbr>SIS</abbr> implementation classes.
+ For example there is
+ a <code class="SIS">DefaultCitation.castOrCopy(<code class="OGC">Citation</code>)</code> method,
+ a <code class="SIS">DefaultExtent.castOrCopy(<code class="OGC">Extent</code>)</code> method, <i>etc.</i>
+ Those methods will either return the given instance directly when possible,
+ or otherwise create a <em>shallow</em> copy of that instance (i.e., without recursive copy of children).
+ The second step can be done in different ways. An easy way (while not the most efficient)
+ is to make an unconditional <em>deep</em> copy of the metadata,
+ in case that metadata was unmodifiable (<code class="SIS">State.FINAL</code>),
+ and also (if desired) for avoiding to change the original metadata.
+ Example:
+ </p>
+
+<pre><code>import org.opengis.metadata.citation.Citation;
+import org.apache.sis.metadata.iso.citation.DefaultCitation;
+import org.apache.sis.util.SimpleInternationalString;
+
+void main() {
+ Citation original = ...; // Some pre-existing citation.
+
+ // Get a modifiable copy and change its title.
+ DefaultCitation edit = DefaultCitation.castOrCopy(original);
+ edit = (DefaultCitation) copy.deepCopy(DefaultCitation.State.EDITABLE);
+ edit.setTitle(new SimpleInternationalString("A new title"));
+}</code></pre>
+
+
+
<h3 id="MetadataAsMap">View as key-value pairs</h3>
<p>
Above static methods explore fragments of metadata tree in search for requested information,
@@ -119,13 +212,13 @@ void main() {
In such cases, one can view the metadata as a <code>java.util.Map</code> like below:
</p>
-<pre><code>
-import java.util.Map;
+<pre><code>import java.util.Map;
import org.apache.sis.metadata.MetadataStandard;
import org.apache.sis.metadata.KeyNamePolicy;
import org.apache.sis.metadata.ValueExistencePolicy;
void main() {
+ Object metadata = ...;
Map<String,Object> elements = MetadataStandard.ISO_19115.asValueMap(
metadata, // Any instance from the org.opengis.metadata package or a sub-package.
null, // Used for resolving ambiguities. We can ignore for this example.
@@ -154,6 +247,9 @@ void main() {
package javadoc.
</p>
+
+
+
<h3 id="MetadataAsTreeTable">View as tree table</h3>
<p>
A richer alternative to the view as a map is the view as a tree table.
@@ -162,19 +258,53 @@ void main() {
</p>
<table>
<caption>Columns of a tree table view of a metadata</caption>
- <tr><th>Column</th> <th>Description</th></tr>
- <tr><td><code class="SIS">IDENTIFIER</code></td> <td>The UML identifier if any, or otherwise the Java Beans name, of the metadata property.</td></tr>
- <tr><td><code class="SIS">INDEX</code></td> <td>If the property is a collection, then the zero-based index of the element in that collection.</td></tr>
- <tr><td><code class="SIS">NAME</code></td> <td>A human-readable name for the node, derived from above identifier and index.</td></tr>
- <tr><td><code class="SIS">TYPE</code></td> <td>The base type of the value (usually a GeoAPI interface).</td></tr>
- <tr><td><code class="SIS">VALUE</code></td> <td>The metadata value for the node. This column may be writable.</td></tr>
- <tr><td><code class="SIS">NIL_REASON</code></td> <td>If the value is mandatory and nevertheless absent, the reason why.</td></tr>
- <tr><td><code class="SIS">REMARKS</code></td> <td>Remarks or warning on the property value.</td></tr>
+ <tr><th>Column</th> <th>Description</th> <th>Writable</th></tr>
+ <tr><td><code class="SIS">IDENTIFIER</code></td> <td>The UML identifier if any, or otherwise the Java Beans name, of the metadata property.</td><td></td></tr>
+ <tr><td><code class="SIS">INDEX</code></td> <td>If the property is a collection, then the zero-based index of the element in that collection.</td><td></td></tr>
+ <tr><td><code class="SIS">NAME</code></td> <td>A human-readable name for the node, derived from above identifier and index.</td><td></td></tr>
+ <tr><td><code class="SIS">TYPE</code></td> <td>The base type of the value (usually a GeoAPI interface).</td><td></td></tr>
+<!-- SIS 1.5
+ <tr><td><code class="SIS">OBLIGATION</code></td> <td>Whether the property is mandatory, optional or conditional.</td><td></td></tr>
+-->
+ <tr><td><code class="SIS">VALUE</code></td> <td>The metadata value for the node. This column may be writable.</td><td>✓</td></tr>
+<!-- SIS 1.5
+ <tr><td><code class="SIS">NIL_REASON</code></td> <td>If the value is mandatory and nevertheless absent, the reason why.</td><td>✓</td></tr>
+-->
+ <tr><td><code class="SIS">REMARKS</code></td> <td>Remarks or warning on the property value.</td><td></td></tr>
</table>
<p>
Tree table views are obtained in a way similar to <a href="#MetadataAsMap">map views</a>,
- but using the <code class="SIS">asTreeTable(Object)</code> method instead of <code class="SIS">asValueMap(Object)</code>.
+ but using the <code class="SIS">asTreeTable(…)</code> method instead of <code class="SIS">asValueMap(…)</code>.
+ Values can be obtained for each column, and can be set for the <code class="SIS">VALUE</code> column
+ if the underlying metadata object is modifiable.
+ The following example prints the values of the <code class="SIS">NAME</code> and <code class="SIS">TYPE</code>
+ columns for a given metadata and all its children:
</p>
+
+<pre><code>import org.apache.sis.metadata.MetadataStandard;
+import org.apache.sis.metadata.ValueExistencePolicy;
+import org.apache.sis.util.collection.TreeTable;
+import org.apache.sis.util.collection.TableColumn;
+
+class Explorer {
+ void main() {
+ Object metadata = ...;
+ TreeTable table = MetadataStandard.ISO_19115.asTreeTable(
+ metadata, // Any instance from the org.opengis.metadata package or a sub-package.
+ null, // Used for resolving ambiguities. We can ignore for this example.
+ ValueExistencePolicy.NON_EMPTY); // Entries with null or empty values will be omitted.
+
+ // Print the names and types of all metadata elements having a value.
+ printRecursively(table.getRoot());
+ }
+
+ private static void printRecursively(TreeTable.Node node) {
+ String name = node.getValue(TableColumn.NAME);
+ Class<?> type = node.getValue(TableColumn.TYPE);
+ System.out.println(name + " : " + type.getSimpleName());
+ node.getChildren().forEach(Explorer::printRecursively);
+ }
+}</code></pre>
</section>
</body>
</html>
diff --git a/content/developer-guide/metadata/NilReason.html b/content/developer-guide/metadata/NilReason.html
index 274da300..e910a6f7 100644
--- a/content/developer-guide/metadata/NilReason.html
+++ b/content/developer-guide/metadata/NilReason.html
@@ -53,16 +53,70 @@
<tr><td><code class="OGC">missing</code></td> <td>The value cannot exist.</td></tr>
<tr><td><code class="OGC">withheld</code></td> <td>The value cannot be revealed.</td></tr>
<tr><td><code class="OGC">template</code></td> <td>The value will be available later.</td></tr>
+ <tr><td><code class="OGC">other</code></td> <td>None of the above. Can be completed by user-suplied strings.</td></tr>
</table>
<p>
The transmission of this information requires the use of a non-null object, even when the value is missing.
- In such case, <abbr>SIS</abbr> will return an object that, besides implementing the desired GeoAPI interface,
- also implements the <code>org.apache.sis.xml.NilObject</code> interface.
+ In such case, Apache <abbr>SIS</abbr> will return an object that, besides implementing the desired GeoAPI interface,
+ also implements the <code class="SIS">NilObject</code> interface.
This interface flags the instances where all methods return an empty collection, an empty table, <code>null</code>,
<code>NaN</code>, <code>0</code> or <code>false</code>, in this preference order, as permitted by the return types of the methods.
- Each instance that implements <code>NilObject</code> provides a <code class="SIS">getNilReason()</code> method
+ Each instance that implements <code class="SIS">NilObject</code> provides a <code class="SIS">getNilReason()</code> method
indicating why the object is nil.
+ For example, the following code specifies that a citation title is missing
+ because this metadata is only a template to be completed by the user:
</p>
+
+<pre><code>import org.opengis.util.InternationalString;
+import org.apache.sis.metadata.iso.citation.DefaultCitation;
+import org.apache.sis.xml.NilReason;
+
+void main() {
+ InternationalString nil = NilReason.TEMPLATE.createNilObject(InternationalString.class);
+ var citation = new DefaultCitation(nil);
+
+ // Verify the reason that we have just set.
+ System.out.println("Title: " + citation.getTitle());
+ NilReason reason = NilReason.forObject(citation.getTitle());
+ System.out.println("Reason why the title is missing: " + reason);
+}</code></pre>
+
+ <p>
+ Output is as below (note that the title is empty, not null):
+ </p>
+
+<pre><samp>Title:
+Reason why the title is missing: template</samp></pre>
+
+<!-- SIS 1.5
+ <p>
+ Alternatively, if the metadata instance is known to be a subclass of <code class="SIS">AbstractMetadata</code>,
+ then the following substitutions can be done in the above example.
+ The advantage is that the <code class="SIS">nilReasons()</code> map works for a few more types
+ than what <code class="SIS">NilReason.createNilObject(Class)</code> can support, for example <code>Boolean</code>.
+ The inconvenient is that there is less compile-time checks, because properties are identified by character strings.
+ </p>
+ <div class="table">
+ <div class="row">
+ <div class="cell"><code><code class="SIS">NilReason.forObject</code>(citation.<code class="API">getTitle()</code>)</code></div>
+ <div class="cell">⟶</div>
+ <div class="cell"><code>citation.<code class="SIS">nilReasons()</code>.get(<code class="OGC">"title"</code>)</code></div>
+ </div>
+ <div class="row">
+ <div class="cell"><code>citation.<code class="SIS">setTitle</code>(nilReason.<code class="SIS">createNilObject</code>(<code class="OGC">InternationalString</code>.class))</code></div>
+ <div class="cell">⟶</div>
+ <div class="cell"><code>citation.<code class="SIS">nilReasons()</code>.put(<code class="OGC">"title"</code>, nilReason)</code></div>
+ </div>
+ </div>
+ <p>
+ The <code class="SIS">nilReasons()</code> is also useful for checking if there is any mandatory property without value.
+ That map will alway contain an entry for such cases, even if the associated nil reason is <code>null</code>.
+ By contrast, optional properties are contained in the map only if they are associated to a non-null nil reason.
+ Consequently, <code>metadata.<code class="SIS">nilReasons()</code>.contains(null)</code> is true if the metadata
+ contains at least one mandatory property with no value and no nil reason.
+ </p>
+-->
+
</section>
</body>
</html>
diff --git a/content/developer-guide/metadata/index.html b/content/developer-guide/metadata/index.html
index a6e811b0..16d559d5 100644
--- a/content/developer-guide/metadata/index.html
+++ b/content/developer-guide/metadata/index.html
@@ -103,8 +103,7 @@
A metadata may be created programmatically like below:
</p>
-<pre><code>
-import org.apache.sis.metadata.iso.citation.DefaultCitation;
+<pre><code>import org.apache.sis.metadata.iso.citation.DefaultCitation;
import org.opengis.metadata.citation.PresentationForm;
void main() {
diff --git a/static/book/book.css b/static/book/book.css
index 09751e9c..4b9f6e45 100644
--- a/static/book/book.css
+++ b/static/book/book.css
@@ -281,6 +281,20 @@ div.row-of-boxes > div {
padding: 6px 30px;
}
+div.table {
+ display: table;
+}
+
+div.row {
+ display: table-row;
+}
+
+div.cell {
+ display: table-cell;
+ padding-left: 21px;
+ padding-right: 21px;
+}
+
pre {
margin-top: 21px;
margin-bottom: 21px;