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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new ab02e95  Add `InfoStatements.findSRID(CoordinateReferenceSystem)` 
method and use it in `RasterWriter`. Initial draft of test cases for WKB raster 
in the context of PostGIS database.
ab02e95 is described below

commit ab02e95fa78c22f6b979fb7a447b54cecd83799e
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Tue Dec 21 22:51:01 2021 +0100

    Add `InfoStatements.findSRID(CoordinateReferenceSystem)` method and use it 
in `RasterWriter`.
    Initial draft of test cases for WKB raster in the context of PostGIS 
database.
---
 .../apache/sis/internal/sql/feature/Database.java  |  11 +-
 .../sis/internal/sql/feature/InfoStatements.java   | 134 +++++++++++++++++++--
 .../apache/sis/internal/sql/feature/Resources.java |   5 +
 .../sis/internal/sql/feature/Resources.properties  |   1 +
 .../internal/sql/feature/Resources_fr.properties   |   1 +
 .../sis/internal/sql/postgis/RasterReader.java     |   3 +
 .../sis/internal/sql/postgis/RasterWriter.java     |  34 +++---
 .../sis/internal/sql/postgis/PostgresTest.java     |  82 ++++++++++++-
 .../sis/internal/sql/postgis/RasterReaderTest.java |  18 ++-
 .../sis/internal/sql/postgis/RasterWriterTest.java |   7 +-
 .../sis/internal/sql/postgis/TestRaster.java       |   2 +-
 .../org/apache/sis/storage/sql/SQLStoreTest.java   |   6 +-
 .../sis/internal/sql/postgis/SpatialFeatures.sql   |  21 ++++
 13 files changed, 281 insertions(+), 44 deletions(-)

diff --git 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Database.java
 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Database.java
index 533fc31..cf81859 100644
--- 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Database.java
+++ 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Database.java
@@ -20,6 +20,7 @@ import java.util.Set;
 import java.util.List;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
+import java.util.WeakHashMap;
 import java.util.ArrayList;
 import java.util.Locale;
 import java.util.logging.LogRecord;
@@ -90,7 +91,7 @@ import org.apache.sis.util.Debug;
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Alexis Manin (Geomatys)
- * @version 1.1
+ * @version 1.2
  * @since   1.1
  * @module
  */
@@ -181,6 +182,13 @@ public class Database<G> extends Syntax  {
     final Cache<Integer, CoordinateReferenceSystem> cacheOfCRS;
 
     /**
+     * Cache of SRID for a given Coordinate Reference System.
+     * This is the converse of {@link #cacheOfCRS}.
+     * Accesses to this map must be synchronized on the map itself.
+     */
+    final WeakHashMap<CoordinateReferenceSystem, Integer> cacheOfSRID;
+
+    /**
      * Creates a new handler for a spatial database.
      *
      * @param  source       provider of (pooled) connections to the database.
@@ -213,6 +221,7 @@ public class Database<G> extends Syntax  {
         this.geomLibrary   = geomLibrary;
         this.listeners     = listeners;
         this.cacheOfCRS    = new Cache<>(7, 2, false);
+        this.cacheOfSRID   = new WeakHashMap<>();
         this.tablesByNames = new FeatureNaming<>();
         supportsCatalogs   = metadata.supportsCatalogsInDataManipulation();
         supportsSchemas    = metadata.supportsSchemasInDataManipulation();
diff --git 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/InfoStatements.java
 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/InfoStatements.java
index 613724c..3b62c34 100644
--- 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/InfoStatements.java
+++ 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/InfoStatements.java
@@ -17,6 +17,10 @@
 package org.apache.sis.internal.sql.feature;
 
 import java.util.Map;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Iterator;
 import java.util.Locale;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
@@ -25,20 +29,27 @@ import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import org.opengis.metadata.Identifier;
+import org.opengis.referencing.IdentifiedObject;
 import org.opengis.referencing.crs.CRSAuthorityFactory;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.NoSuchAuthorityCodeException;
 import org.apache.sis.storage.DataStoreContentException;
+import org.apache.sis.storage.DataStoreReferencingException;
 import org.apache.sis.internal.referencing.DefinitionVerifier;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
 import org.apache.sis.internal.metadata.sql.SQLBuilder;
 import org.apache.sis.internal.feature.GeometryType;
 import org.apache.sis.internal.system.Modules;
+import org.apache.sis.internal.util.Constants;
 import org.apache.sis.io.wkt.Convention;
 import org.apache.sis.io.wkt.WKTFormat;
 import org.apache.sis.io.wkt.Warnings;
 import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.referencing.factory.IdentifiedObjectFinder;
 import org.apache.sis.util.Localized;
+import org.apache.sis.util.Utilities;
 
 
 /**
@@ -48,6 +59,7 @@ import org.apache.sis.util.Localized;
  * <ul>
  *   <li>Searching for geometric information using SQL queries specialized for 
Simple Feature table.</li>
  *   <li>Fetching a Coordinate Reference System (CRS) from a SRID.</li>
+ *   <li>Finding a SRID from a Coordinate Reference System (CRS).</li>
  * </ul>
  *
  * This class is <strong>not</strong> thread-safe. Each instance should be 
used in a single thread.
@@ -55,7 +67,7 @@ import org.apache.sis.util.Localized;
  *
  * @author Alexis Manin (Geomatys)
  * @author Martin Desruisseaux (Geomatys)
- * @since  1.1
+ * @since  1.2
  *
  * @see <a href="https://www.ogc.org/standards/sfs";>OGC Simple feature access 
— Part 2: SQL option</a>
  *
@@ -114,6 +126,11 @@ public class InfoStatements implements Localized, 
AutoCloseable {
     private PreparedStatement wktFromSrid;
 
     /**
+     * The statement for fetching a SRID from a CRS and its set of authority 
codes.
+     */
+    private PreparedStatement sridFromCRS;
+
+    /**
      * The object to use for parsing Well-Known Text (WKT), created when first 
needed.
      */
     private WKTFormat wktReader;
@@ -312,13 +329,9 @@ public class InfoStatements implements Localized, 
AutoCloseable {
                 CoordinateReferenceSystem fromWKT = null;
                 final String wkt = result.getString(3);
                 if (wkt != null && !wkt.isEmpty()) {
-                    if (wktReader == null) {
-                        wktReader = new WKTFormat(null, null);
-                        wktReader.setConvention(Convention.WKT1_COMMON_UNITS);
-                    }
                     final Object parsed;
                     try {
-                        parsed = wktReader.parseObject(wkt);
+                        parsed = wktReader().parseObject(wkt);
                     } catch (ParseException e) {
                         if (authorityError != null) {
                             e.addSuppressed(authorityError);
@@ -401,7 +414,110 @@ public class InfoStatements implements Localized, 
AutoCloseable {
     }
 
     /**
-     * Closes all prepared statements.This method does <strong>not</strong> 
close the connection.
+     * Finds a SRID code from the spatial reference systems table for the 
given CRS.
+     *
+     * @param  crs  the CRS for which to find a SRID, or {@code null}.
+     * @return SRID for the given CRS, or 0 if the given CRS was null.
+     * @throws Exception if an SQL error, parsing error or other error 
occurred.
+     */
+    public final int findSRID(final CoordinateReferenceSystem crs) throws 
Exception {
+        if (crs == null) {
+            return 0;
+        }
+        synchronized (database.cacheOfSRID) {
+            final Integer srid = database.cacheOfSRID.get(crs);
+            if (srid != null) {
+                return srid;
+            }
+        }
+        final Set<SimpleImmutableEntry<String,String>> done = new HashSet<>();
+        Iterator<IdentifiedObject> alternatives = null;
+        IdentifiedObject candidate = crs;
+        Exception error = null;
+        for (;;) {
+            /*
+             * First, iterate over the identifiers declared in the CRS object.
+             * If we can not find an identifier that we can map to a SRID, 
then this loop may be
+             * executed more times with CRS from EPSG database that are equal, 
ignore axis order.
+             */
+            for (final Identifier id : candidate.getIdentifiers()) {
+                final String authority = id.getCodeSpace();
+                if (authority == null) continue;
+                final String code = id.getCode();
+                if (!done.add(new SimpleImmutableEntry<>(authority, code))) {
+                    continue;                           // Skip 
"authority:code" that we already tried.
+                }
+                final int codeValue;
+                try {
+                    codeValue = Integer.parseInt(code);
+                } catch (NumberFormatException e) {
+                    if (error == null) error = e;
+                    else error.addSuppressed(e);
+                    continue;                           // Ignore codes that 
are not integers.
+                }
+                /*
+                 * Found an "authority:code" pair that we did not tested 
before.
+                 * Get the WKT and verifies if the CRS is approximately equal.
+                 */
+                if (sridFromCRS == null) {
+                    final SQLBuilder sql = new SQLBuilder(database);
+                    sql.append("SELECT srtext, srid");
+                    appendFrom(sql, SPATIAL_REF_SYS);
+                    sql.append("auth_name=? AND auth_srid=?");
+                    sridFromCRS = connection.prepareStatement(sql.toString());
+                }
+                sridFromCRS.setString(1, authority);
+                sridFromCRS.setInt(2, codeValue);
+                try (ResultSet result = sridFromCRS.executeQuery()) {
+                    while (result.next()) {
+                        final String wkt = result.getString(1);
+                        if (wkt != null && !wkt.isEmpty()) try {
+                            final Object parsed = wktReader().parseObject(wkt);
+                            if (Utilities.equalsApproximately(parsed, crs)) {
+                                final int srid = result.getInt(2);
+                                synchronized (database.cacheOfSRID) {
+                                    database.cacheOfSRID.put(crs, srid);
+                                }
+                                return srid;
+                            }
+                        } catch (ParseException e) {
+                            if (error == null) error = e;
+                            else error.addSuppressed(e);
+                        }
+                    }
+                }
+            }
+            /*
+             * Tried all identifiers associated to the CRS and found no match.
+             * It may be because the CRS has no identifier at all. Search for
+             * possible identifiers in the EPSG database, then try them.
+             */
+            if (alternatives == null) {
+                final IdentifiedObjectFinder finder = 
IdentifiedObjects.newFinder(Constants.EPSG);
+                finder.setIgnoringAxes(true);
+                alternatives = finder.find(crs).iterator();
+            }
+            if (!alternatives.hasNext()) break;
+            candidate = alternatives.next();
+        }
+        throw new DataStoreReferencingException(Resources.format(
+                Resources.Keys.CanNotFindSRID_1, 
IdentifiedObjects.getDisplayName(crs, null)), error);
+    }
+
+    /**
+     * Returns the object to use for parsing Well Known Text (CRS).
+     * The parser is created when first needed.
+     */
+    private WKTFormat wktReader() {
+        if (wktReader == null) {
+            wktReader = new WKTFormat(null, null);
+            wktReader.setConvention(Convention.WKT1_COMMON_UNITS);
+        }
+        return wktReader;
+    }
+
+    /**
+     * Closes all prepared statements. This method does <strong>not</strong> 
close the connection.
      *
      * @throws SQLException if an error occurred while closing a connection.
      */
@@ -415,5 +531,9 @@ public class InfoStatements implements Localized, 
AutoCloseable {
             wktFromSrid.close();
             wktFromSrid = null;
         }
+        if (sridFromCRS != null) {
+            sridFromCRS.close();
+            sridFromCRS = null;
+        }
     }
 }
diff --git 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Resources.java
 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Resources.java
index 517a205..885dc3a 100644
--- 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Resources.java
+++ 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Resources.java
@@ -64,6 +64,11 @@ public final class Resources extends IndexedResourceBundle {
         public static final short CanNotFetchCRS_1 = 8;
 
         /**
+         * Can not find an identifier in the database for the reference system 
“{0}”.
+         */
+        public static final short CanNotFindSRID_1 = 15;
+
+        /**
          * Provider of connections to the database.
          */
         public static final short DataSource = 1;
diff --git 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Resources.properties
 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Resources.properties
index 83d294e..133fd1a 100644
--- 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Resources.properties
+++ 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Resources.properties
@@ -20,6 +20,7 @@
 # For resources shared by all modules in the Apache SIS project, see 
"org.apache.sis.util.resources" package.
 #
 CanNotFetchCRS_1                  = Can not fetch a Coordinate Reference 
System (CRS) for SRID code {0}.
+CanNotFindSRID_1                  = Can not find an identifier in the database 
for the reference system \u201c{0}\u201d.
 DataSource                        = Provider of connections to the database.
 DuplicatedColumn_1                = Unexpected duplication of column named 
\u201c{0}\u201d.
 DuplicatedSRID_2                  = Spatial Reference Identifier (SRID) {1} 
has more than one entry in \u201c{0}\u201d table.
diff --git 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Resources_fr.properties
 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Resources_fr.properties
index 6a83f5c..c46969e 100644
--- 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Resources_fr.properties
+++ 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Resources_fr.properties
@@ -25,6 +25,7 @@
 #   U+00A0 NO-BREAK SPACE         before  :
 #
 CanNotFetchCRS_1                  = Ne peut pas obtenir un syst\u00e8me de 
r\u00e9f\u00e9rence des coordonn\u00e9es pour le code SRID {0}.
+CanNotFindSRID_1                  = Ne peut pas trouver un identifiant dans la 
base de donn\u00e9es pour le syst\u00e8me de r\u00e9f\u00e9rence 
\u00ab\u202f{0}\u202f\u00bb.
 DataSource                        = Fournisseur de connexions \u00e0 la base 
de donn\u00e9es.
 DuplicatedColumn_1                = Doublon inattendu d\u2019une colonne 
nomm\u00e9e \u00ab\u202f{0}\u202f\u00bb.
 DuplicatedSRID_2                  = L\u2019identifiant de r\u00e9f\u00e9rence 
spatiale (SRID) {1} a plusieurs entr\u00e9s dans la table 
\u00ab\u202f{0}\u202f\u00bb.
diff --git 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/RasterReader.java
 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/RasterReader.java
index 244aa3e..693c6ed 100644
--- 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/RasterReader.java
+++ 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/RasterReader.java
@@ -314,6 +314,9 @@ public final class RasterReader extends RasterFormat {
                 final int sampleSize = ((MultiPixelPackedSampleModel) 
sm).getPixelBitStride();
                 maximum = (1 << sampleSize) - 1;
                 minimum = 0;
+            } else if (dataType == DataBuffer.TYPE_BYTE) {
+                minimum = 0;
+                maximum = 0xFF;
             } else {
                 final Band band = bands[visibleBand];
                 final NumberRange<?> range = Vector.create(band.data, 
band.isUnsigned()).range();
diff --git 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/RasterWriter.java
 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/RasterWriter.java
index 4188e0e..5ce3d89 100644
--- 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/RasterWriter.java
+++ 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/RasterWriter.java
@@ -35,18 +35,15 @@ import java.awt.image.SinglePixelPackedSampleModel;
 import java.awt.image.RenderedImage;
 import java.awt.image.Raster;
 import java.awt.image.RasterFormatException;
-import org.opengis.util.FactoryException;
-import org.opengis.referencing.IdentifiedObject;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.image.PixelIterator;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.coverage.grid.GridGeometry;
-import org.apache.sis.internal.util.Constants;
+import org.apache.sis.internal.sql.feature.Resources;
 import org.apache.sis.internal.sql.feature.InfoStatements;
 import org.apache.sis.internal.storage.io.ChannelDataOutput;
 import org.apache.sis.referencing.IdentifiedObjects;
-import org.apache.sis.referencing.factory.IdentifiedObjectFinder;
 import org.apache.sis.referencing.operation.matrix.AffineTransforms2D;
 import org.apache.sis.util.resources.Errors;
 
@@ -121,9 +118,10 @@ public final class RasterWriter extends RasterFormat {
      *
      * @param  gg  the grid to CRS conversion together with target CRS.
      * @throws IllegalArgumentException if the "grid to CRS" transform is not 
affine.
-     * @throws FactoryException if an error occurred during the search for 
SRID code.
+     * @throws Exception if an error occurred during the search for SRID code.
+     *         May be SQL error, WKT parsing error, factory error, <i>etc.</i>
      */
-    public void setGridToCRS(final GridGeometry gg) throws FactoryException {
+    public void setGridToCRS(final GridGeometry gg) throws Exception {
         if (gg.isDefined(GridGeometry.CRS)) {
             final CoordinateReferenceSystem crs = 
gg.getCoordinateReferenceSystem();
             /*
@@ -131,21 +129,16 @@ public final class RasterWriter extends RasterFormat {
              * Otherwise use EPSG code only as a starting point, ignoring axis 
order,
              * and search for the corresponding SRID.
              */
-            Integer epsg;
-            if (spatialRefSys == null) {
-                epsg = IdentifiedObjects.lookupEPSG(crs);
+            if (spatialRefSys != null) {
+                srid = spatialRefSys.findSRID(crs);
             } else {
-                epsg = null;
-                final IdentifiedObjectFinder finder = 
IdentifiedObjects.newFinder(Constants.EPSG);
-                finder.setIgnoringAxes(true);
-                for (final IdentifiedObject candidate : finder.find(crs)) {
-                    // TODO
+                final Integer epsg = IdentifiedObjects.lookupEPSG(crs);
+                if (epsg == null) {
+                    throw new IllegalArgumentException(Resources.format(
+                            Resources.Keys.CanNotFindSRID_1, 
IdentifiedObjects.getDisplayName(crs, null)));
                 }
+                srid = epsg;
             }
-            if (epsg == null) {
-                throw new IllegalArgumentException("Can not find an identifier 
in the database for the Coordinate Reference System.");
-            }
-            srid = epsg;
         } else {
             srid = 0;
         }
@@ -176,9 +169,10 @@ public final class RasterWriter extends RasterFormat {
      * @param  output    where to write the bytes.
      * @throws RasterFormatException if the raster to write is not supported.
      * @throws IOException in an error occurred while writing to the given 
output.
-     * @throws FactoryException if an error occurred during the search for 
SRID code.
+     * @throws Exception if an error occurred during the search for SRID code.
+     *         May be SQL error, WKT parsing error, factory error, <i>etc.</i>
      */
-    public void write(final GridCoverage coverage, final ChannelDataOutput 
output) throws IOException, FactoryException {
+    public void write(final GridCoverage coverage, final ChannelDataOutput 
output) throws Exception {
         setGridToCRS(coverage.getGridGeometry());
         setNodataValues(coverage.getSampleDimensions());
         write(coverage.render(null), output);
diff --git 
a/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/PostgresTest.java
 
b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/PostgresTest.java
index 7a43465..a473b64 100644
--- 
a/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/PostgresTest.java
+++ 
b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/PostgresTest.java
@@ -16,30 +16,106 @@
  */
 package org.apache.sis.internal.sql.postgis;
 
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.lang.reflect.Method;
+import org.opengis.referencing.crs.ProjectedCRS;
+import org.apache.sis.setup.OptionKey;
+import org.apache.sis.setup.GeometryLibrary;
+import org.apache.sis.storage.sql.SQLStore;
+import org.apache.sis.storage.sql.SQLStoreProvider;
+import org.apache.sis.storage.sql.ResourceDefinition;
+import org.apache.sis.storage.StorageConnector;
+import org.apache.sis.storage.sql.SQLStoreTest;
+import org.apache.sis.internal.storage.io.ChannelDataInput;
+import org.apache.sis.referencing.crs.HardCodedCRS;
+import org.apache.sis.test.sql.TestDatabase;
+import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
 import org.apache.sis.util.Version;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
+import static org.opengis.test.Assert.*;
 
 
 /**
  * Tests {@link Postgres}.
  *
  * @author  Alexis Manin (Geomatys)
- * @version 1.1
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.2
  * @since   1.1
  * @module
  */
+@DependsOn({RasterReaderTest.class, RasterWriterTest.class})
 public final strictfp class PostgresTest extends TestCase {
     /**
      * Tests {@link Postgres#parseVersion(String)}.
      */
     @Test
-    public void parse_postgis_version() {
+    public void testParseVersion() {
         final Version version = Postgres.parseVersion("3.1 USE_GEOS=1 
USE_PROJ=1 USE_STATS=1");
         assertEquals(3, version.getMajor());
         assertEquals(1, version.getMinor());
         assertNull  (   version.getRevision());
     }
+
+    /**
+     * Tests reading and writing features and rasters.
+     *
+     * @throws Exception if an error occurred while testing the database.
+     */
+    @Test
+    public void testSpatialFeatures() throws Exception {
+        try (TestDatabase database = 
TestDatabase.createOnPostgreSQL(SQLStoreTest.SCHEMA, true)) {
+            database.executeSQL(PostgresTest.class, 
"file:SpatialFeatures.sql");
+            final StorageConnector connector = new 
StorageConnector(database.source);
+            connector.setOption(OptionKey.GEOMETRY_LIBRARY, 
GeometryLibrary.JTS);
+            final ResourceDefinition table = ResourceDefinition.table(null, 
SQLStoreTest.SCHEMA, "SpatialData");
+            try (SQLStore store = new SQLStore(new SQLStoreProvider(), 
connector, table)) {
+                /*
+                 * Invoke the private `model()` method. We have to use 
reflection because the class
+                 * is not in the same package and we do not want to expose the 
method in public API.
+                 */
+                final Method modelAccessor = 
SQLStore.class.getDeclaredMethod("model");
+                modelAccessor.setAccessible(true);
+                final Postgres<?> pg = (Postgres<?>) 
modelAccessor.invoke(store);
+                try (Connection connection = database.source.getConnection();
+                     ExtendedInfo info = new ExtendedInfo(pg, connection))
+                {
+                    testInfoStatements(info);
+//                  testRasterReader(TestRaster.USHORT, info, connection);
+                }
+            }
+        }
+    }
+
+    /**
+     * Tests {@link org.apache.sis.internal.sql.feature.InfoStatements}.
+     *
+     * @throws Exception if an error occurred while testing the database.
+     */
+    private void testInfoStatements(final ExtendedInfo info) throws Exception {
+        assertEquals("findSRID", 4326, info.findSRID(HardCodedCRS.WGS84));
+        assertInstanceOf("fetchCRS", ProjectedCRS.class, info.fetchCRS(3395));
+    }
+
+    /**
+     * Tests {@link RasterReader}.
+     */
+    private void testRasterReader(final TestRaster test, final ExtendedInfo 
info, final Connection connection) throws Exception {
+        final RasterReader reader = new RasterReader(info);
+        try (PreparedStatement stmt = connection.prepareStatement("SELECT 
image FROM features.\"SpatialData\" WHERE filename=?")) {
+            stmt.setString(1, test.filename);
+            final ResultSet r = stmt.executeQuery();
+            assertTrue(r.next());
+            final ChannelDataInput input = new ChannelDataInput(test.filename,
+                    Channels.newChannel(r.getBinaryStream(1)), 
ByteBuffer.allocate(50), false);
+            RasterReaderTest.compareReadResult(test, reader, input);
+            assertFalse(r.next());
+        }
+    }
 }
diff --git 
a/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/RasterReaderTest.java
 
b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/RasterReaderTest.java
index 7b4a1d6..fc7595a 100644
--- 
a/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/RasterReaderTest.java
+++ 
b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/RasterReaderTest.java
@@ -20,6 +20,7 @@ import java.util.Arrays;
 import java.awt.image.RenderedImage;
 import java.awt.image.DataBufferUShort;
 import org.apache.sis.coverage.grid.GridCoverage;
+import org.apache.sis.internal.storage.io.ChannelDataInput;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
@@ -49,12 +50,19 @@ public final strictfp class RasterReaderTest extends 
TestCase {
     }
 
     /**
-     * Reads the file for the given test enumeration
-     * and compares with the expected raster.
+     * Reads the file for the given test enumeration and compares with the 
expected raster.
      */
-    private void compareReadResult(final TestRaster test) throws Exception {
-        final RasterReader reader = new RasterReader(null);
-        final GridCoverage coverage = reader.readAsCoverage(test.input());
+    private static void compareReadResult(final TestRaster test) throws 
Exception {
+        compareReadResult(test, new RasterReader(null), test.input());
+    }
+
+    /**
+     * Reads the file for the given test enumeration and compares with the 
expected raster.
+     * The given reader and input are used for reading the raster. The input 
will be closed.
+     */
+    static void compareReadResult(final TestRaster test, final RasterReader 
reader, final ChannelDataInput input) throws Exception {
+        final GridCoverage coverage = reader.readAsCoverage(input);
+        input.channel.close();
         final RenderedImage image = coverage.render(null);
         assertEquals(TestRaster.SRID, reader.getSRID());
         assertEquals(TestRaster.getGridToCRS(), reader.getGridToCRS());
diff --git 
a/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/RasterWriterTest.java
 
b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/RasterWriterTest.java
index b250230..33ffdf6 100644
--- 
a/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/RasterWriterTest.java
+++ 
b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/RasterWriterTest.java
@@ -19,7 +19,6 @@ package org.apache.sis.internal.sql.postgis;
 import java.awt.image.Raster;
 import java.io.IOException;
 import java.io.ByteArrayOutputStream;
-import org.opengis.util.FactoryException;
 import org.apache.sis.internal.storage.io.ChannelDataOutput;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
@@ -43,10 +42,10 @@ public final strictfp class RasterWriterTest extends 
TestCase {
      * the expected sequence of bytes provided by {@code "raster-ushort.wkb"} 
file.
      *
      * @throws IOException if an error occurred while writing the test file.
-     * @throws FactoryException if an error occurred during the search for 
SRID code.
+     * @throws Exception if an error occurred during the search for SRID code.
      */
     @Test
-    public void testUShort() throws IOException, FactoryException {
+    public void testUShort() throws Exception {
         compareWriteResult(TestRaster.USHORT);
     }
 
@@ -54,7 +53,7 @@ public final strictfp class RasterWriterTest extends TestCase 
{
      * Writes the raster for the given test enumeration
      * and compares with the expected sequence of bytes.
      */
-    private void compareWriteResult(final TestRaster test) throws IOException, 
FactoryException {
+    private static void compareWriteResult(final TestRaster test) throws 
Exception {
         final Raster raster = test.createRaster();
         final RasterWriter writer = new RasterWriter(null);
         final ByteArrayOutputStream buffer = new 
ByteArrayOutputStream(test.length);
diff --git 
a/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/TestRaster.java
 
b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/TestRaster.java
index afe8b83..8ba6aae 100644
--- 
a/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/TestRaster.java
+++ 
b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/TestRaster.java
@@ -69,7 +69,7 @@ enum TestRaster {
     /**
      * Name of the file where the WKB raster is stored.
      */
-    private final String filename;
+    final String filename;
 
     /**
      * Expected file size in bytes.
diff --git 
a/storage/sis-sqlstore/src/test/java/org/apache/sis/storage/sql/SQLStoreTest.java
 
b/storage/sis-sqlstore/src/test/java/org/apache/sis/storage/sql/SQLStoreTest.java
index 094ceb4..b4111b1 100644
--- 
a/storage/sis-sqlstore/src/test/java/org/apache/sis/storage/sql/SQLStoreTest.java
+++ 
b/storage/sis-sqlstore/src/test/java/org/apache/sis/storage/sql/SQLStoreTest.java
@@ -61,7 +61,7 @@ public final strictfp class SQLStoreTest extends TestCase {
     /**
      * The schema where will be stored the features to test.
      */
-    private static final String SCHEMA = "features";
+    public static final String SCHEMA = "features";
 
     /**
      * Data used in the {@code Features.sql} test file.
@@ -163,8 +163,8 @@ public final strictfp class SQLStoreTest extends TestCase {
             scripts[0] = null;      // Omit the "CREATE SCHEMA" statement if 
the schema already exists.
         }
         try (TestDatabase tmp = database) {                 // TODO: omit 
`tmp` with JDK16.
-            tmp.executeSQL(SQLStoreTest.class, scripts);
-            final StorageConnector connector = new 
StorageConnector(tmp.source);
+            database.executeSQL(SQLStoreTest.class, scripts);
+            final StorageConnector connector = new 
StorageConnector(database.source);
             final ResourceDefinition table = ResourceDefinition.table(null, 
inMemory ? null : SCHEMA, "Cities");
             testTableQuery(connector, table);
             /*
diff --git 
a/storage/sis-sqlstore/src/test/resources/org/apache/sis/internal/sql/postgis/SpatialFeatures.sql
 
b/storage/sis-sqlstore/src/test/resources/org/apache/sis/internal/sql/postgis/SpatialFeatures.sql
new file mode 100644
index 0000000..20a1bda
--- /dev/null
+++ 
b/storage/sis-sqlstore/src/test/resources/org/apache/sis/internal/sql/postgis/SpatialFeatures.sql
@@ -0,0 +1,21 @@
+-- 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.
+
+
+-- Create a temporary database on PostgreSQL for testing geometries and 
rasters.
+-- The "postgis_raster" extension must be installed before to execute this 
test.
+
+SET search_path TO public;
+
+CREATE TABLE features."SpatialData" (
+    "filename" VARCHAR(20) NOT NULL,
+    "image"    RASTER      NOT NULL,
+
+    CONSTRAINT "PK_SpatialData" PRIMARY KEY ("filename")
+);
+
+INSERT INTO features."SpatialData" ("filename", "image")
+  VALUES('raster-ushort.wkb', 
('0100000200000000000000F43F000000000000044000000000000054C00000000000004EC0000000'
+      || 
'00000000000000000000000000E6100000030004000600006F0079008300D300DD00E700370141014B019B01A501AF01060000'
+      || '70007A008400D400DE00E800380142014C019C01A601B001')::raster);

Reply via email to