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 2021/08/31 17:25:22 UTC

[sis] 07/07: Parse some more metadata from Landsat.

This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit bd56303e6be910ab67a19c06b35fa18e57633f9e
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Aug 31 19:15:42 2021 +0200

    Parse some more metadata from Landsat.
---
 .../storage/earthobservation/LandsatReader.java    | 135 ++++++++++++++-------
 .../sis/storage/earthobservation/LandsatStore.java |   2 +-
 .../earthobservation/LandsatStoreProvider.java     |   8 +-
 .../sis/storage/earthobservation/package-info.java |   3 +-
 .../sis/internal/storage/wkt/FirstKeywordPeek.java |   6 +-
 5 files changed, 106 insertions(+), 48 deletions(-)

diff --git a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatReader.java b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatReader.java
index 4fe53b6..479696e 100644
--- a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatReader.java
+++ b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatReader.java
@@ -147,6 +147,11 @@ final class LandsatReader extends MetadataBuilder {
     static final int DIM = 2;
 
     /**
+     * Suffix of groups that describe the processing done on the data instead of the data themselves.
+     */
+    private static final String LINEAGE_SUFFIX = "_RECORD";
+
+    /**
      * The {@value} suffix added to attribute names that are followed by a band number.
      * This band suffix is itself followed by the {@code '_'} character, then the band number.
      * Example: {@code "REFLECTANCE_ADD_BAND_1"}.
@@ -220,6 +225,14 @@ final class LandsatReader extends MetadataBuilder {
     private String group;
 
     /**
+     * The last group where we fetched lineage (history) information.
+     * The same file may contain many lineage group.
+     *
+     * @see #parseLineage(String, int, String)
+     */
+    private String lineageGroup;
+
+    /**
      * The acquisition time, or {@code null} if not yet known. This needs to be parsed in two steps:
      * first by parsing the {@code "DATE_ACQUIRED"} attribute, then {@code "SCENE_CENTER_TIME"}.
      *
@@ -365,8 +378,17 @@ final class LandsatReader extends MetadataBuilder {
                     start = CharSequences.skipLeadingWhitespaces(line, start + 1, --end);
                     end = CharSequences.skipTrailingWhitespaces(line, start, end);
                 }
+                final String value = line.substring(start, end);
                 try {
-                    parseKeyValuePair(key, band, line.substring(start, end));
+                    if (group == null || !group.endsWith(LINEAGE_SUFFIX)) {
+                        parseKeyValuePair(key, band, value);
+                    } else {
+                        if (!group.equals(lineageGroup)) {
+                            lineageGroup = group;
+                            newLineage();
+                        }
+                        parseLineage(key, band, value);
+                    }
                 } catch (IllegalArgumentException | DateTimeException e) {
                     warning(key, reader, e);
                 }
@@ -407,8 +429,9 @@ final class LandsatReader extends MetadataBuilder {
     }
 
     /**
-     * Invoked for every key-value pairs found in the file.
-     * Leading and trailing spaces, if any, have been removed.
+     * Invoked for every key-value pairs found in the file for the main groups.
+     * A mean group is any group other than {@linkplain #parseLineage lineage}.
+     * Leading and trailing spaces, if any, have been removed from given argument.
      *
      * @param  key    the key in upper cases.
      * @param  band   the band number, or 0 if none.
@@ -430,12 +453,10 @@ final class LandsatReader extends MetadataBuilder {
                 group = null;
                 break;
             }
-
-            ////
-            //// GROUP = METADATA_FILE_INFO
-            ////
-
-            /*
+            /* ┌────────────────────────────────────┐
+             * │ GROUP = METADATA_FILE_INFO     (L1)│
+             * │         PRODUCT_CONTENTS       (L2)│
+             * └────────────────────────────────────┘
              * Origin of the product.
              * Value is "Image courtesy of the U.S. Geological Survey".
              */
@@ -449,6 +470,10 @@ final class LandsatReader extends MetadataBuilder {
                 break;
             }
             /*
+             * Example: "https://doi.org/10.5066/P9OGBGM6"
+             */
+//          case "DIGITAL_OBJECT_IDENTIFIER":
+            /*
              * Product Request ID. NNNNNNNNNNNNN_UUUUU, where NNNNNNNNNNNNN = 13-digit Tracking,
              * Routing, and Metrics (TRAM) order number and UUUUU = 5-digit TRAM unit number.
              * Example: "0501403126384_00011"
@@ -458,10 +483,11 @@ final class LandsatReader extends MetadataBuilder {
                 break;
             }
             /*
-             * The unique Landsat scene identifier.
-             * Format is {@code Ls8ppprrrYYYYDDDGGGVV}.
-             * Example: "LC81230522014071LGN00".
+             * Product: the filename prefix. Example: "LC08_L2SP_197030_20210812_20210819_02_T1"
+             * Scene:   the unique Landsat scene identifier. Example: "LC81230522014071LGN00".
+             * Format:  Ls8ppprrrYYYYDDDGGGVV
              */
+            case "LANDSAT_PRODUCT_ID":
             case "LANDSAT_SCENE_ID": {
                 addTitleOrIdentifier(value, MetadataBuilder.Scope.ALL);
                 break;
@@ -488,15 +514,14 @@ final class LandsatReader extends MetadataBuilder {
              * Example: "LPGS_2.3.0".
              */
 // TODO     case "PROCESSING_SOFTWARE_VERSION":
-
-            ////
-            //// GROUP = PRODUCT_METADATA
-            ////
-
-            /*
+            /* ┌────────────────────────────────────┐
+             * │ GROUP = PRODUCT_METADATA       (L1)│
+             * │         PRODUCT_CONTENTS       (L2)│
+             * └────────────────────────────────────┘
              * The identifier to inform the user of the product type.
              * Value can be "L1T" or "L1GT".
              */
+            case "PROCESSING_LEVEL":
             case "DATA_TYPE": {
                 setProcessingLevelCode("Landsat", value);
                 break;
@@ -628,6 +653,22 @@ final class LandsatReader extends MetadataBuilder {
                 break;
             }
             /*
+             * Examples: UINT8, UINT16, INT16.
+             */
+            case "DATA_TYPE_BAND_": {
+                final int s = value.lastIndexOf("INT");
+                if (s >= 0) try {
+                    final Integer n = Integer.valueOf(value.substring(s + 3));
+                    final DefaultBand db = band(key, band);
+                    if (db != null) {
+                        db.setBitsPerValue(n);
+                    }
+                } catch (NumberFormatException e) {
+                    warning(key, null, e);
+                }
+                break;
+            }
+            /*
              * The file name for L1 metadata.
              * Exemple: "LC81230522014071LGN00_MTL.txt".
              */
@@ -637,12 +678,9 @@ final class LandsatReader extends MetadataBuilder {
                 }
                 break;
             }
-
-            ////
-            //// GROUP = IMAGE_ATTRIBUTES
-            ////
-
-            /*
+            /* ┌────────────────────────────────────┐
+             * │ GROUP = IMAGE_ATTRIBUTES           │
+             * └────────────────────────────────────┘
              * The overall cloud coverage (percent) of the WRS-2 scene as a value between 0 and 100 inclusive.
              * -1 indicates that the score was not calculated.
              */
@@ -671,12 +709,9 @@ final class LandsatReader extends MetadataBuilder {
                 setIlluminationElevationAngle(Double.parseDouble(value));
                 break;
             }
-
-            ////
-            //// GROUP = MIN_MAX_PIXEL_VALUE
-            ////
-
-            /*
+            /* ┌────────────────────────────────────┐
+             * │ GROUP = MIN_MAX_PIXEL_VALUE        │
+             * └────────────────────────────────────┘
              * Minimum achievable spectral radiance value for a band 1.
              * This parameter is only present if this band is included in the product.
              */
@@ -700,12 +735,9 @@ final class LandsatReader extends MetadataBuilder {
                 }
                 break;
             }
-
-            ////
-            //// GROUP = RADIOMETRIC_RESCALING
-            ////
-
-            /*
+            /* ┌────────────────────────────────────┐
+             * │ GROUP = RADIOMETRIC_RESCALING      │
+             * └────────────────────────────────────┘
              * The multiplicative rescaling factor used to convert calibrated DN to Radiance units for a band.
              * Unit is W/(m² sr um)/DN.
              */
@@ -721,12 +753,9 @@ final class LandsatReader extends MetadataBuilder {
                 setTransferFunction(key, band, false, value);
                 break;
             }
-
-            ////
-            //// GROUP = PROJECTION_PARAMETERS
-            ////
-
-            /*
+            /* ┌────────────────────────────────────┐
+             * │ GROUP = PROJECTION_PARAMETERS      │
+             * └────────────────────────────────────┘
              * The map projection used in creating the image.
              * Universal Transverse Mercator (UTM) or Polar Stereographic (PS).
              */
@@ -775,6 +804,28 @@ final class LandsatReader extends MetadataBuilder {
     }
 
     /**
+     * Invoked for every key-value pairs found in a lineage group. This method does a work similar to
+     * {@link #parseKeyValuePair(String, int, String)} except that the result is stored in a section
+     * about data history. Note that the file can contains many distinct lineage sections.
+     */
+    private void parseLineage(final String key, final int band, String value) {
+        switch (key) {
+            case "LANDSAT_PRODUCT_ID": {
+                addSource(value, null, null);
+                break;
+            }
+            case "PROCESSING_LEVEL": {
+                addProcessing(null, value);
+                break;
+            }
+            case "PROCESSING_SOFTWARE_VERSION": {
+                addSoftwareReference(value);
+                break;
+            }
+        }
+    }
+
+    /**
      * Sets a component of the linear transfer function.
      *
      * @param  key      the key without its band number. Used only for formatting warning messages.
diff --git a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStore.java b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStore.java
index 770b330..6932a16 100644
--- a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStore.java
+++ b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStore.java
@@ -69,7 +69,7 @@ import org.apache.sis.setup.OptionKey;
  *
  * @author  Thi Phuong Hao Nguyen (VNSC)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   0.8
  * @module
  */
diff --git a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStoreProvider.java b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStoreProvider.java
index b54e628..6db3495 100644
--- a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStoreProvider.java
+++ b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStoreProvider.java
@@ -37,7 +37,7 @@ import org.apache.sis.internal.storage.wkt.FirstKeywordPeek;
  * the part of the caller. However the {@link LandsatStore} instances created by this factory are not thread-safe.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   0.8
  * @module
  */
@@ -62,7 +62,8 @@ public class LandsatStoreProvider extends DataStoreProvider {
         /**
          * The expected keyword after spaces removal.
          */
-        private static final String KEYWORD = "GROUP=L1_METADATA_FILE";
+        private static final String KEYWORD = "GROUP=LANDSAT_METADATA_FILE",
+                                 L1_KEYWORD = "GROUP=L1_METADATA_FILE";
 
         /**
          * The part in process of being parsed:
@@ -126,7 +127,8 @@ public class LandsatStoreProvider extends DataStoreProvider {
          */
         @Override
         protected ProbeResult forKeyword(final char[] keyword, final int length) {
-            if (length == maxLength && KEYWORD.equalsIgnoreCase(new String(keyword))) {
+            final String ks = new String(keyword, 0, length);
+            if (KEYWORD.equalsIgnoreCase(ks) || L1_KEYWORD.equalsIgnoreCase(ks)) {
                 return ProbeResult.SUPPORTED;
             }
             return ProbeResult.UNSUPPORTED_STORAGE;
diff --git a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/package-info.java b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/package-info.java
index a7278a9..b84a034 100644
--- a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/package-info.java
+++ b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/package-info.java
@@ -34,7 +34,8 @@
  *
  * @author  Thi Phuong Hao Nguyen (VNSC)
  * @author  Minh Chinh Vu (VNSC)
- * @version 0.8
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
  * @since   0.8
  * @module
  */
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/FirstKeywordPeek.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/FirstKeywordPeek.java
index 8387c7f..d83319d 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/FirstKeywordPeek.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/FirstKeywordPeek.java
@@ -161,7 +161,11 @@ public abstract class FirstKeywordPeek {
                         }
                         keyword[pos++] = (char) c;
                     }
-                    c = (buffer == null) ? IOUtilities.readCodePoint(reader) : buffer.hasRemaining() ? (char) buffer.get() : -1;
+                    if (buffer != null) {
+                        c = buffer.hasRemaining() ? (char) buffer.get() : -1;
+                    } else {
+                        c = IOUtilities.readCodePoint(reader);
+                    }
                 } while ((s = isKeywordChar(c)) >= ACCEPT);
                 /*
                  * At this point we finished to read and store the keyword.