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 0e983e5d1ed879a8d94dcf4bf6450679065ea199
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Fri Apr 22 19:04:07 2022 +0200

    Move in a separated class some `AsciiGridStore` code which will be shared 
by BIL/BIP/BSQ data store.
    Add some convenience static methods will will be needed as well.
---
 .../main/java/org/apache/sis/image/DataType.java   |  55 ++++++++-
 .../org/apache/sis/internal/feature/Resources.java |   5 +
 .../sis/internal/feature/Resources.properties      |   1 +
 .../sis/internal/feature/Resources_fr.properties   |   1 +
 .../java/org/apache/sis/image/DataTypeTest.java    |  25 +++-
 .../org/apache/sis/internal/util/Numerics.java     |  13 ++
 .../java/org/apache/sis/util/resources/Errors.java |   5 +
 .../apache/sis/util/resources/Errors.properties    |   1 +
 .../apache/sis/util/resources/Errors_fr.properties |   1 +
 .../sis/internal/storage/esri/AsciiGridStore.java  |  70 ++---------
 .../sis/internal/storage/esri/RasterStore.java     | 131 +++++++++++++++++++++
 .../sis/internal/storage/esri/package-info.java    |   6 +-
 .../sis/internal/storage/image/WorldFileStore.java |   1 +
 13 files changed, 249 insertions(+), 66 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/DataType.java 
b/core/sis-feature/src/main/java/org/apache/sis/image/DataType.java
index f4c1c197e1..9c18299b04 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/DataType.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/DataType.java
@@ -24,6 +24,7 @@ import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.measure.NumberRange;
 import org.apache.sis.internal.coverage.j2d.ImageUtilities;
 import org.apache.sis.internal.feature.Resources;
+import org.apache.sis.util.resources.Errors;
 
 import static 
org.apache.sis.internal.util.Numerics.MAX_INTEGER_CONVERTIBLE_TO_FLOAT;
 
@@ -33,7 +34,7 @@ import static 
org.apache.sis.internal.util.Numerics.MAX_INTEGER_CONVERTIBLE_TO_F
  * This is a type-safe version of the {@code TYPE_*} constants defined in 
{@link DataBuffer}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.2
  * @since   1.1
  * @module
  */
@@ -195,6 +196,58 @@ public enum DataType {
         }
     }
 
+    /**
+     * Returns the enumeration value for the given number of bits.
+     * Only the following combinations of argument values are accepted:
+     *
+     * <table class="sis">
+     *   <caption>Valid combinations of argument values</caption>
+     *   <tr><th>size</th>                  <th>real</th>           
<th>signed</th></tr>
+     *   <tr><td>1</td>                     <td>{@code false}</td>  <td>{@code 
false}</td></tr>
+     *   <tr><td>2</td>                     <td>{@code false}</td>  <td>{@code 
false}</td></tr>
+     *   <tr><td>4</td>                     <td>{@code false}</td>  <td>{@code 
false}</td></tr>
+     *   <tr><td>{@value Byte#SIZE}</td>    <td>{@code false}</td>  <td>{@code 
false}</td></tr>
+     *   <tr><td>{@value Short#SIZE}</td>   <td>{@code false}</td>  <td>{@code 
false} or {@code true}</td></tr>
+     *   <tr><td>{@value Integer#SIZE}</td> <td>{@code false}</td>  <td>{@code 
true}</td></tr>
+     *   <tr><td>{@value Float#SIZE}</td>   <td>{@code true}</td>   
<td>ignored</td></tr>
+     *   <tr><td>{@value Double#SIZE}</td>  <td>{@code true}</td>   
<td>ignored</td></tr>
+     * </table>
+     *
+     * @param  size    number of bits as a power of 2 between 1 and {@value 
Double#SIZE} inclusive.
+     * @param  real    {@code true} for floating point numbers or {@code 
false} for integers.
+     * @param  signed  {@code true} for signed numbers of {@code false} for 
unsigned numbers.
+     * @return the data type (never {@code null}) for the given number of bits.
+     * @throws RasterFormatException if the combination of argument values is 
invalid.
+     *
+     * @since 1.2
+     */
+    public static DataType forNumberOfBits(final int size, final boolean real, 
final boolean signed) {
+        if (size < 1 || size > Double.SIZE || Integer.lowestOneBit(size) != 
size) {
+            throw new 
RasterFormatException(Errors.format(Errors.Keys.IllegalArgumentValue_2, "size", 
size));
+        }
+        String argument = "real";       // For reporting which argument is 
inconsistent with sample size.
+        boolean value   =  real;
+        if (real) {
+            switch (size) {
+                case Float.SIZE:  return FLOAT;
+                case Double.SIZE: return DOUBLE;
+            }
+        } else {
+            if (size == Short.SIZE) {
+                return signed ? SHORT : USHORT;
+            }
+            final boolean isInt = (size == Integer.SIZE);
+            if (isInt || size <= Byte.SIZE) {
+                if (isInt == signed) {
+                    return isInt ? INT : BYTE;
+                }
+                argument = "signed";
+                value    =  signed;
+            }
+        }
+        throw new 
RasterFormatException(Resources.format(Resources.Keys.UnsupportedSampleType_3, 
size, argument, value));
+    }
+
     /**
      * Returns the size in bits of this data type.
      *
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
index 3a82bb6a71..71d22be341 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
@@ -454,6 +454,11 @@ public final class Resources extends IndexedResourceBundle 
{
          * Unsupported geometry {0}D object.
          */
         public static final short UnsupportedGeometryObject_1 = 76;
+
+        /**
+         * Sample type with a size of {0} bits can not have ‘{1}’ = “{2}” 
characteristic.
+         */
+        public static final short UnsupportedSampleType_3 = 78;
     }
 
     /**
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
index 018e03ecb7..b2a78dd762 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
@@ -95,4 +95,5 @@ UnspecifiedCRS                    = Coordinate reference 
system is unspecified.
 UnspecifiedGridExtent             = Grid extent is unspecified.
 UnspecifiedRasterData             = Raster data are unspecified.
 UnspecifiedTransform              = Coordinates transform is unspecified.
+UnsupportedSampleType_3           = Sample type with a size of {0} bits can 
not have \u2018{1}\u2019\u202f=\u202f\u201c{2}\u201d characteristic.
 UnsupportedGeometryObject_1       = Unsupported geometry {0}D object.
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
index 1f08c3fd38..40d9f79027 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
@@ -100,4 +100,5 @@ UnspecifiedCRS                    = Le syst\u00e8me de 
r\u00e9f\u00e9rence des c
 UnspecifiedGridExtent             = L\u2019\u00e9tendue de la grille n\u2019a 
pas \u00e9t\u00e9 sp\u00e9cifi\u00e9e.
 UnspecifiedRasterData             = Les donn\u00e9es du raster n\u2019ont pas 
\u00e9t\u00e9 sp\u00e9cifi\u00e9es.
 UnspecifiedTransform              = La transformation de coordonn\u00e9es 
n\u2019a pas \u00e9t\u00e9 sp\u00e9cifi\u00e9e.
+UnsupportedSampleType_3           = Les types de valeurs sur {0} bits ne 
peuvent pas avoir la caract\u00e9ristique 
\u2018{1}\u2019\u202f=\u202f\u00ab\u202f{2}\u202f\u00bb.
 UnsupportedGeometryObject_1       = Object g\u00e9om\u00e9trique {0}D 
non-support\u00e9.
diff --git 
a/core/sis-feature/src/test/java/org/apache/sis/image/DataTypeTest.java 
b/core/sis-feature/src/test/java/org/apache/sis/image/DataTypeTest.java
index a0b6f60d0d..b427c1c649 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/image/DataTypeTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/image/DataTypeTest.java
@@ -17,6 +17,7 @@
 package org.apache.sis.image;
 
 import java.awt.image.DataBuffer;
+import java.awt.image.RasterFormatException;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
@@ -27,7 +28,7 @@ import static org.junit.Assert.*;
  * Verifies {@link DataType}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.2
  * @since   1.1
  * @module
  */
@@ -96,4 +97,26 @@ public final strictfp class DataTypeTest extends TestCase {
         assertEquals(DataType.FLOAT,  DataType.FLOAT .toFloat());
         assertEquals(DataType.DOUBLE, DataType.DOUBLE.toFloat());
     }
+
+    /**
+     * Tests {@link DataType#forNumberOfBits(int, boolean, boolean)}.
+     */
+    @Test
+    public void testForNumberOfBits() {
+        assertEquals(DataType.BYTE,   DataType.forNumberOfBits(1,            
false, false));
+        assertEquals(DataType.BYTE,   DataType.forNumberOfBits(Byte.SIZE,    
false, false));
+        assertEquals(DataType.USHORT, DataType.forNumberOfBits(Short.SIZE,   
false, false));
+        assertEquals(DataType.SHORT,  DataType.forNumberOfBits(Short.SIZE,   
false, true));
+        assertEquals(DataType.INT,    DataType.forNumberOfBits(Integer.SIZE, 
false, true));
+        assertEquals(DataType.FLOAT,  DataType.forNumberOfBits(Float.SIZE,   
true,  true));
+        assertEquals(DataType.DOUBLE, DataType.forNumberOfBits(Double.SIZE,  
true,  true));
+        try {
+            DataType.forNumberOfBits(Byte.SIZE, false, true);
+            fail("Signed bytes should be invalid.");
+        } catch (RasterFormatException e) {
+            final String message = e.getMessage();
+            assertTrue(message, message.contains("signed"));
+            assertTrue(message, message.contains("true"));
+        }
+    }
 }
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java 
b/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java
index bb68a2b420..6f7bea5f17 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java
@@ -224,6 +224,19 @@ public final class Numerics extends Static {
         return r;
     }
 
+    /**
+     * Returns x/y with the requirement that the division must be integer.
+     *
+     * @param  x  the dividend.
+     * @param  y  the divisor.
+     * @return x/y.
+     * @throws ArithmeticException if y is zero of if the result of x/y is not 
an integer.
+     */
+    public static int wholeDiv(final int x, final int y) {
+        if ((x % y) != 0) throw new ArithmeticException();
+        return x / y;       // TODO: use Math.divideExact with JDK18.
+    }
+
     /**
      * Returns {@code value} × {@code multiplier} / {@code divisor} with 
control against overflow.
      * The result is rounded toward zero.
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java 
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
index 4cf445a241..739c255be8 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
@@ -425,6 +425,11 @@ public final class Errors extends IndexedResourceBundle {
          */
         public static final short IllegalPropertyValueClass_3 = 59;
 
+        /**
+         * Property “{0}” can not take the “{1}” value.
+         */
+        public static final short IllegalPropertyValue_2 = 198;
+
         /**
          * Range [{0} … {1}] is not valid.
          */
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
 
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
index c052d47030..c0b3168576 100644
--- 
a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
@@ -94,6 +94,7 @@ IllegalLanguageCode_1             = The \u201c{0}\u201d 
language is not recogniz
 IllegalMapping_2                  = Illegal mapping: {0} \u2192 {1}.
 IllegalMemberType_2               = Member \u201c{0}\u201d can not be 
associated to type \u201c{1}\u201d.
 IllegalOptionValue_2              = Option \u2018{0}\u2019 can not take the 
\u201c{1}\u201d value.
+IllegalPropertyValue_2            = Property \u201c{0}\u201d can not take the 
\u201c{1}\u201d value.
 IllegalPropertyValueClass_2       = Property \u201c{0}\u201d does not accept 
instances of \u2018{1}\u2019.
 IllegalPropertyValueClass_3       = Expected an instance of \u2018{1}\u2019 
for the \u201c{0}\u201d property, but got an instance of \u2018{2}\u2019.
 IllegalRange_2                    = Range [{0} \u2026 {1}] is not valid.
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
 
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
index 9c1e1db682..b93ab77358 100644
--- 
a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
@@ -91,6 +91,7 @@ IllegalLanguageCode_1             = Le code de langue 
\u00ab\u202f{0}\u202f\u00b
 IllegalMapping_2                  = Correspondance ill\u00e9gale: {0} \u2192 
{1}.
 IllegalMemberType_2               = Le membre \u00ab\u202f{0}\u202f\u00bb ne 
peut pas \u00eatre associ\u00e9 au type \u00ab\u202f{1}\u202f\u00bb.
 IllegalOptionValue_2              = L\u2019option \u2018{0}\u2019 
n\u2019accepte pas la valeur \u00ab\u202f{1}\u202f\u00bb.
+IllegalPropertyValue_2            = La propri\u00e9t\u00e9 
\u00ab\u202f{0}\u202f\u00bb n\u2019accepte pas la valeur 
\u00ab\u202f{1}\u202f\u00bb.
 IllegalPropertyValueClass_2       = La propri\u00e9t\u00e9 
\u00ab\u202f{0}\u202f\u00bb n\u2019accepte pas les valeurs de type 
\u2018{1}\u2019.
 IllegalPropertyValueClass_3       = Une instance \u2018{1}\u2019 \u00e9tait 
attendue pour la propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb, mais la 
valeur donn\u00e9e est une instance de \u2018{2}\u2019.
 IllegalRange_2                    = La plage [{0} \u2026 {1}] n\u2019est pas 
valide.
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/AsciiGridStore.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/AsciiGridStore.java
index 9fa01da1cc..19d803778f 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/AsciiGridStore.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/AsciiGridStore.java
@@ -18,16 +18,12 @@ package org.apache.sis.internal.storage.esri;
 
 import java.util.Map;
 import java.util.List;
-import java.util.Optional;
 import java.util.StringJoiner;
 import java.io.IOException;
 import java.nio.file.StandardOpenOption;
 import java.awt.image.RenderedImage;
 import java.awt.image.DataBufferFloat;
-import org.opengis.geometry.Envelope;
 import org.opengis.metadata.Metadata;
-import org.opengis.metadata.maintenance.ScopeCode;
-import org.opengis.referencing.operation.TransformException;
 import org.opengis.referencing.datum.PixelInCell;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.coverage.grid.GridCoverage;
@@ -40,15 +36,10 @@ import org.apache.sis.storage.StorageConnector;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.DataStoreClosedException;
 import org.apache.sis.storage.DataStoreContentException;
-import org.apache.sis.storage.DataStoreReferencingException;
-import org.apache.sis.storage.GridCoverageResource;
-import org.apache.sis.internal.storage.MetadataBuilder;
-import org.apache.sis.internal.storage.PRJDataStore;
 import org.apache.sis.internal.storage.RangeArgument;
 import org.apache.sis.internal.storage.Resources;
 import org.apache.sis.internal.storage.io.ChannelDataInput;
 import org.apache.sis.measure.NumberRange;
-import org.apache.sis.metadata.sql.MetadataStoreException;
 import org.apache.sis.referencing.operation.matrix.Matrix3;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.util.resources.Errors;
@@ -153,12 +144,11 @@ import org.apache.sis.util.resources.Errors;
  * @since   1.2
  * @module
  */
-class AsciiGridStore extends PRJDataStore implements GridCoverageResource {
+class AsciiGridStore extends RasterStore {
     /**
      * Keys of elements expected in the header. Must be in upper-case letters.
      */
     static final String
-            NCOLS     = "NCOLS",      NROWS        = "NROWS",
             XLLCORNER = "XLLCORNER",  YLLCORNER    = "YLLCORNER",
             XLLCENTER = "XLLCENTER",  YLLCENTER    = "YLLCENTER",
             CELLSIZE  = "CELLSIZE",   NODATA_VALUE = "NODATA_VALUE";
@@ -208,18 +198,6 @@ class AsciiGridStore extends PRJDataStore implements 
GridCoverageResource {
      */
     private String nodataText;
 
-    /**
-     * The image size together with the "grid to CRS" transform.
-     * This is also used as a flag for checking whether the
-     * {@code "*.prj"} file and the header have been read.
-     */
-    private GridGeometry gridGeometry;
-
-    /**
-     * The metadata object, or {@code null} if not yet created.
-     */
-    private Metadata metadata;
-
     /**
      * The full coverage, read when first requested then cached.
      * We cache the full coverage on the assumption that the
@@ -254,7 +232,6 @@ class AsciiGridStore extends PRJDataStore implements 
GridCoverageResource {
         if (channel != null) {
             input = new CharactersView(channel, channel.buffer);
         }
-        listeners.useWarningEventsOnly();
     }
 
     /**
@@ -406,45 +383,11 @@ cellsize:       if (value != null) {
     @Override
     public synchronized Metadata getMetadata() throws DataStoreException {
         if (metadata == null) {
-            readHeader();
-            final MetadataBuilder builder = new MetadataBuilder();
-            try {
-                builder.setPredefinedFormat("ASCGRD");
-            } catch (MetadataStoreException e) {
-                builder.addFormatName(AsciiGridStoreProvider.NAME);
-                listeners.warning(e);
-            }
-            builder.addEncoding(encoding, MetadataBuilder.Scope.METADATA);
-            builder.addResourceScope(ScopeCode.COVERAGE, null);
-            try {
-                builder.addExtent(gridGeometry.getEnvelope());
-            } catch (TransformException e) {
-                throw new DataStoreReferencingException(getLocale(), 
AsciiGridStoreProvider.NAME, getDisplayName(), null).initCause(e);
-            }
-            /*
-             * Do not add the sample dimension, because in current version 
computing the sample dimension
-             * requires loading the full image. Even if the `band` field is 
already computed and could be
-             * used opportunistically, we do not use it in order to keep a 
deterministic behavior
-             * (we do not want the metadata to vary depending on the order in 
which methods are invoked).
-             */
-            addTitleOrIdentifier(builder);
-            builder.setISOStandards(false);
-            metadata = builder.buildAndFreeze();
+            createMetadata(AsciiGridStoreProvider.NAME, "ASCGRD");
         }
         return metadata;
     }
 
-    /**
-     * Returns the spatiotemporal extent of the ASCII grid file.
-     *
-     * @return the spatiotemporal resource extent.
-     * @throws DataStoreException if an error occurred while computing the 
envelope.
-     */
-    @Override
-    public Optional<Envelope> getEnvelope() throws DataStoreException {
-        return Optional.ofNullable(getGridGeometry().getEnvelope());
-    }
-
     /**
      * Returns the valid extent of grid coordinates together with the 
conversion from those grid
      * coordinates to real world coordinates.
@@ -475,13 +418,14 @@ cellsize:       if (value != null) {
     }
 
     /**
-     * Loads the data if not already done and closes the channel. In current 
implementation the image is always
-     * fully loaded and cached. The given domain is ignored. We do that in 
order to have determinist and stable
-     * values for the sample range and for the data type. Loading the full 
image is reasonable if ASCII Grid
+     * Loads the data if not already done and closes the channel if read-only.
+     * In current implementation the image is always fully loaded and cached.
+     * The given domain is ignored. We do that in order to have determinist 
and stable values
+     * for the sample range and for the data type. Loading the full image is 
reasonable if ASCII Grid
      * files contain only small images, which is usually the case given how 
inefficient this format is.
      *
      * @param  domain  desired grid extent and resolution, or {@code null} for 
reading the whole domain.
-     * @param  range   shall be either 0 or an containing only 0.
+     * @param  range   shall be either 0 or a range containing only 0.
      * @return the grid coverage for the specified domain.
      * @throws DataStoreException if an error occurred while reading the grid 
coverage data.
      */
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/RasterStore.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/RasterStore.java
new file mode 100644
index 0000000000..0d7338b556
--- /dev/null
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/RasterStore.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.storage.esri;
+
+import java.util.Optional;
+import org.opengis.geometry.Envelope;
+import org.opengis.metadata.Metadata;
+import org.opengis.metadata.maintenance.ScopeCode;
+import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.storage.GridCoverageResource;
+import org.apache.sis.storage.DataStoreProvider;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.DataStoreReferencingException;
+import org.apache.sis.storage.StorageConnector;
+import org.apache.sis.internal.storage.PRJDataStore;
+import org.apache.sis.internal.storage.MetadataBuilder;
+import org.apache.sis.metadata.sql.MetadataStoreException;
+
+
+/**
+ * Base class for the implementation of ASCII Grid or raw binary store.
+ * This base class manages the reading of following auxiliary files:
+ *
+ * <ul>
+ *   <li>{@code *.stx} for statistics about bands.</li>
+ *   <li>{@code *.clr} for the image color map.</li>
+ *   <li>{@code *.prj} for the CRS definition.</li>
+ * </ul>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.2
+ * @since   1.2
+ * @module
+ */
+abstract class RasterStore extends PRJDataStore implements 
GridCoverageResource {
+    /**
+     * Keyword for the number of rows in the image.
+     */
+    static final String NROWS = "NROWS";
+
+    /**
+     * Keyword for the number of columns in the image.
+     */
+    static final String NCOLS = "NCOLS";
+
+    /**
+     * The image size together with the "grid to CRS" transform.
+     * This is also used as a flag for checking whether the
+     * {@code "*.prj"} file and the header have been read.
+     */
+    GridGeometry gridGeometry;
+
+    /**
+     * The metadata object, or {@code null} if not yet created.
+     */
+    Metadata metadata;
+
+    /**
+     * Creates a new raster store from the given file, URL or stream.
+     *
+     * @param  provider   the factory that created this {@code DataStore} 
instance, or {@code null} if unspecified.
+     * @param  connector  information about the storage (file, URL, stream, 
<i>etc</i>).
+     * @throws DataStoreException if an error occurred while creating the data 
store for the given storage.
+     */
+    RasterStore(final DataStoreProvider provider, final StorageConnector 
connector) throws DataStoreException {
+        super(provider, connector);
+        listeners.useWarningEventsOnly();
+    }
+
+    /**
+     * Builds metadata and assigns the result to the {@link #metadata} field.
+     *
+     * @param  formatName  name of the raster format.
+     * @param  formatKey   key of format description in the {@code 
SpatialMetadata} database.
+     * @throws DataStoreException if an error occurred during the parsing 
process.
+     */
+    final void createMetadata(final String formatName, final String formatKey) 
throws DataStoreException {
+        final GridGeometry gridGeometry = getGridGeometry();        // May 
cause parsing of header.
+        final MetadataBuilder builder = new MetadataBuilder();
+        try {
+            builder.setPredefinedFormat(formatKey);
+        } catch (MetadataStoreException e) {
+            builder.addFormatName(formatName);
+            listeners.warning(e);
+        }
+        builder.addResourceScope(ScopeCode.COVERAGE, null);
+        builder.addEncoding(encoding, MetadataBuilder.Scope.METADATA);
+        builder.addSpatialRepresentation(null, gridGeometry, true);
+        try {
+            builder.addExtent(gridGeometry.getEnvelope());
+        } catch (TransformException e) {
+            throw new DataStoreReferencingException(getLocale(), formatName, 
getDisplayName(), null).initCause(e);
+        }
+        /*
+         * Do not add the sample dimensions because in current version 
computing the sample dimensions without
+         * statistics requires loading the full image. Even if 
`GridCoverage.getSampleDimensions()` exists and
+         * could be used opportunistically, we do not use it in order to keep 
a deterministic behavior
+         * (we do not want the metadata to vary depending on the order in 
which methods are invoked).
+         */
+        addTitleOrIdentifier(builder);
+        builder.setISOStandards(false);
+        metadata = builder.buildAndFreeze();
+    }
+
+    /**
+     * Returns the spatiotemporal extent of the raster file.
+     *
+     * @return the spatiotemporal resource extent.
+     * @throws DataStoreException if an error occurred while computing the 
envelope.
+     * @hidden
+     */
+    @Override
+    public Optional<Envelope> getEnvelope() throws DataStoreException {
+        return Optional.ofNullable(getGridGeometry().getEnvelope());
+    }
+}
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/package-info.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/package-info.java
index c32a629d97..e03933fae7 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/package-info.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/esri/package-info.java
@@ -34,7 +34,11 @@
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.2
- * @since   1.2
+ *
+ * @see <a 
href="https://desktop.arcgis.com/en/arcmap/latest/manage-data/raster-and-images/esri-ascii-raster-format.htm";>Esri
 ASCII raster format</a>
+ * @see <a 
href="https://desktop.arcgis.com/en/arcmap/latest/manage-data/raster-and-images/bil-bip-and-bsq-raster-files.htm";>BIL,
 BIP, and BSQ raster files</a>
+ *
+ * @since 1.2
  * @module
  */
 package org.apache.sis.internal.storage.esri;
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WorldFileStore.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WorldFileStore.java
index 62052b1731..9dc93e9363 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WorldFileStore.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/image/WorldFileStore.java
@@ -226,6 +226,7 @@ class WorldFileStore extends PRJDataStore implements 
Aggregate {
     {
         super(provider, connector);
         identifiers = new HashMap<>();
+        listeners.useWarningEventsOnly();
         final Object storage = connector.getStorage();
         if (storage instanceof ImageReader) {
             reader = (ImageReader) storage;

Reply via email to