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 b43d5d4d36 Reimplement `getComponentFiles()` by fetching this
information from GDAL instead of guessing.
b43d5d4d36 is described below
commit b43d5d4d36f6995c0275994546b1da4cb5360871
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Mon Sep 16 17:55:15 2024 +0200
Reimplement `getComponentFiles()` by fetching this information from GDAL
instead of guessing.
---
.../main/org/apache/sis/storage/gdal/GDAL.java | 23 ++++-
.../org/apache/sis/storage/gdal/GDALStore.java | 97 +++++++++-------------
.../apache/sis/storage/gdal/SubdatasetList.java | 9 +-
.../org/apache/sis/storage/gdal/TiledResource.java | 1 +
.../org/apache/sis/storage/gdal/GDALStoreTest.java | 6 ++
5 files changed, 73 insertions(+), 63 deletions(-)
diff --git
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
index 1f7e6de681..1857780d47 100644
---
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
+++
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
@@ -114,6 +114,19 @@ final class GDAL extends NativeFunctions {
*/
final MethodHandle free;
+ /**
+ * <abbr>GDAL</abbr> {@code void CSLDestroy(char **papszStrList)}.
+ * Releases memory allocated by <abbr>GDAL</abbr> for a string list.
+ * It is safe to pass {@code NULL}.
+ */
+ final MethodHandle destroy;
+
+ /**
+ * <abbr>GDAL</abbr> {@code char **GDALGetFileList(GDALDatasetH)}.
+ * Fetch files forming dataset.
+ */
+ final MethodHandle getFileList;
+
/**
* <abbr>GDAL</abbr> {@code GDALDriverH
GDALGetDatasetDriver(GDALDatasetH)}.
* Fetches the driver to which a dataset relates.
@@ -270,7 +283,12 @@ final class GDAL extends NativeFunctions {
final var acceptTwoPtrsReturnPointer =
FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS,
ValueLayout.ADDRESS);
final Linker linker = Linker.nativeLinker();
+ // Memory management
+ free = lookup(linker, "VSIFree",
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS));
+ destroy = lookup(linker, "CSLDestroy",
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS));
+
// For Driver and/or all major objects
+ identifyDriver = lookup(linker, "GDALIdentifyDriver",
acceptTwoPtrsReturnPointer);
getName = lookup(linker, "GDALGetDriverLongName",
acceptPointerReturnPointer);
getIdentifier = lookup(linker, "GDALGetDriverShortName",
acceptPointerReturnPointer);
getMetadata = lookup(linker, "GDALGetMetadata",
acceptTwoPtrsReturnPointer);
@@ -281,8 +299,6 @@ final class GDAL extends NativeFunctions {
ValueLayout.ADDRESS)); // const char* domain
// For Opener
- identifyDriver = lookup(linker, "GDALIdentifyDriver",
acceptTwoPtrsReturnPointer);
- free = lookup(linker, "VSIFree",
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS));
close = lookup(linker, "GDALClose", acceptPointerReturnInt);
open = lookup(linker, "GDALOpenEx",
FunctionDescriptor.of(ValueLayout.ADDRESS,
ValueLayout.ADDRESS, // const char *pszFilename
@@ -291,7 +307,8 @@ final class GDAL extends NativeFunctions {
ValueLayout.ADDRESS, // const char *const
*papszOpenOptions
ValueLayout.ADDRESS)); // const char *const
*papszSiblingFiles
- // For all data set
+ // For all data sets
+ getFileList = lookup(linker, "GDALGetFileList",
acceptPointerReturnPointer);
getDatasetDriver = lookup(linker, "GDALGetDatasetDriver",
acceptPointerReturnPointer);
getSpatialRef = lookup(linker, "GDALGetSpatialRef",
acceptPointerReturnPointer);
getGCPSpatialRef = lookup(linker, "GDALGetGCPSpatialRef",
acceptPointerReturnPointer);
diff --git
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java
index 3e9382ba92..f5272fc1a7 100644
---
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java
+++
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java
@@ -17,16 +17,12 @@
package org.apache.sis.storage.gdal;
import java.util.List;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.net.URI;
-import java.io.IOException;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
import java.nio.file.Path;
import java.lang.ref.Cleaner;
import java.lang.foreign.Arena;
@@ -238,7 +234,7 @@ public class GDALStore extends DataStore implements
Aggregate, ResourceOnFileSys
* @return name of the <abbr>GDAL</abbr> driver used for opening the file.
* @throws DataStoreException if the driver name cannot be fetched.
*/
- final String getDriverName(final GDAL gdal) throws DataStoreException {
+ private String getDriverName(final GDAL gdal) throws DataStoreException {
try {
var result = (MemorySegment)
gdal.getDatasetDriver.invokeExact(handle());
if (!GDAL.isNull(result)) { // Paranoiac check.
@@ -272,6 +268,44 @@ public class GDALStore extends DataStore implements
Aggregate, ResourceOnFileSys
return Optional.ofNullable(param);
}
+ /**
+ * Returns an array of files believed to be part of this resource.
+ *
+ * @todo This method is often used for copying a resources from one
location to another.
+ * <abbr>GDAL</abbr> provides a {@code GDALCopyDatasetFiles}
function for this purpose.
+ * That function is not yet used by Apache <abbr>SIS</abbr>.
+ *
+ * @return files used by this resource, or an empty array if unknown.
+ * @throws DataStoreException if the list of files cannot be obtained.
+ */
+ @Override
+ public synchronized Path[] getComponentFiles() throws DataStoreException {
+ final GDAL gdal = getProvider().GDAL();
+ final List<String> files;
+ try {
+ final var list = (MemorySegment)
gdal.getFileList.invokeExact(handle());
+ try {
+ files = GDAL.fromNullTerminatedStrings(list);
+ } finally {
+ gdal.destroy.invokeExact(list);
+ }
+ } catch (Throwable e) {
+ throw GDAL.propagate(e);
+ }
+ if (files == null || files.isEmpty()) {
+ return (path != null) ? new Path[] {path} : new Path[0];
+ }
+ final var paths = new Path[files.size()];
+ for (int i=0; i < paths.length; i++) {
+ var item = Path.of(files.get(i));
+ if (path != null) {
+ item = path.resolveSibling(item);
+ }
+ paths[i] = item;
+ }
+ return paths;
+ }
+
/**
* Returns an identifier for the root resource of this data store, or an
empty value if none.
*
@@ -348,63 +382,12 @@ public class GDALStore extends DataStore implements
Aggregate, ResourceOnFileSys
if (all != null) {
final String driver = getDriverName(gdal);
if (driver != null) { // Should never be null.
- return new SubdatasetList(gdal, this, all);
+ return new SubdatasetList(gdal, this, driver, all);
}
}
return null;
}
- /**
- * Gets the paths to files potentially used by this resource.
- * This method returns the path to the main file opened by the
<abbr>GDAL</abbr> driver,
- * and applies heuristic rules for guessing which other paths may be part
of the format.
- * Heuristic rules are used because <abbr>GDAL</abbr> provides no
<abbr>API</abbr> for
- * listing which files are used.
- *
- * @return files used by this resource, or an empty array if unknown.
- * @throws DataStoreException if an error on the file system prevent the
creation of the list.
- */
- @Override
- public Path[] getComponentFiles() throws DataStoreException {
- @SuppressWarnings("LocalVariableHidesMemberVariable")
- final Path path = this.path;
- if (path == null) {
- return new Path[0];
- }
- final DirectoryStream.Filter<Path> filter;
- switch (getDriver().getIdentifier()) {
- default: {
- return new Path[] {path};
- }
- case "AIG": { // Arc/Info Binary Grid
- // Special case: we must take "metadata.xml" and all "*.adf"
in the folder.
- filter = (Path entry) ->
("metadata.xml".equalsIgnoreCase(entry.getFileName().toString())
- ||
"adf".equalsIgnoreCase(IOUtilities.extension(entry)));
- break;
- }
- // TODO: list more case where we want the same default as GeoTIFF.
- case "GTiff": {
- // List all existing paths with the same file name but
possibly with different suffixes.
- final String filename = path.getFileName().toString();
- final int s = filename.lastIndexOf('.');
- final String base = (s >= 0) ? filename.substring(0, s+1) :
filename;
- filter = (Path entry) ->
entry.getFileName().toString().startsWith(base);
- break;
- }
- }
- final var paths = new ArrayList<Path>();
- paths.addFirst(path);
- try (DirectoryStream<Path> stream =
Files.newDirectoryStream(path.getParent(),
- (entry) -> !entry.equals(path) && filter.accept(entry) &&
Files.isRegularFile(entry)))
- {
- stream.forEach(paths::add);
- } catch (IOException ex) {
- throw cannotExecute(ex);
- }
- // Ensure that the main path is first.
- return paths.toArray(Path[]::new);
- }
-
/**
* Returns the object to use for parsing and formatting <abbr>CRS</abbr>
definitions from/to <abbr>GDAL</abbr>.
* This object must be used in a block synchronized on {@code this}.
diff --git
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/SubdatasetList.java
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/SubdatasetList.java
index 20378a05e0..826aa56453 100644
---
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/SubdatasetList.java
+++
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/SubdatasetList.java
@@ -66,11 +66,14 @@ final class SubdatasetList extends AbstractList<Subdataset>
{
*
* @param gdal set of handles for invoking <abbr>GDAL</abbr>
functions.
* @param parent the data store which owns this list.
+ * @param driver name of the <abbr>GDAL</abbr> driver to use for
opening the sub-datasets.
* @param metadata the metadata items for the {@code "SUBDATASETS"}
domain.
*/
- SubdatasetList(final GDAL gdal, final GDALStore parent, final List<String>
metadata) throws DataStoreException {
- this.parent = parent;
- this.driver = parent.getDriverName(gdal);
+ SubdatasetList(final GDAL gdal, final GDALStore parent, final String
driver, final List<String> metadata)
+ throws DataStoreException
+ {
+ this.parent = parent;
+ this.driver = driver;
/*
* URLs of all sub-dataset, optionally associated to their
descriptions.
* Keys are metadata keys. Values at index 0 are the URLs. Values at
index 1 are descriptions.
diff --git
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledResource.java
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledResource.java
index a5ba1c4d6b..45e42ee8f6 100644
---
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledResource.java
+++
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledResource.java
@@ -322,6 +322,7 @@ final class TiledResource extends TiledGridResource {
m.getAtIndex(layout, 0),
m.getAtIndex(layout, 3));
}
+ // TODO: if above is not available, we could fallback on
`GDALGCPsToGeoTransform`.
}
/*
* The axis order used by GDAL is not the axis order in the
CRS definition.
diff --git
a/incubator/src/org.apache.sis.storage.gdal/test/org/apache/sis/storage/gdal/GDALStoreTest.java
b/incubator/src/org.apache.sis.storage.gdal/test/org/apache/sis/storage/gdal/GDALStoreTest.java
index 9353bdf4a5..d316abd1e4 100644
---
a/incubator/src/org.apache.sis.storage.gdal/test/org/apache/sis/storage/gdal/GDALStoreTest.java
+++
b/incubator/src/org.apache.sis.storage.gdal/test/org/apache/sis/storage/gdal/GDALStoreTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.sis.storage.gdal;
+import java.nio.file.Path;
import java.awt.image.DataBuffer;
import java.awt.image.RenderedImage;
import org.opengis.util.GenericName;
@@ -156,6 +157,11 @@ public final class GDALStoreTest {
assertEquals(5, data.getElem(2000)); // Check a random
value.
}
}
+
+ // Check the file components
+ final Path[] paths = store.getComponentFiles();
+ assertEquals(1, paths.length);
+ assertEquals("test.tiff", paths[0].getFileName().toString());
}
assertTrue(foundGrid);
assertTrue(foundBand);