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 2024/08/30 16:00:35 UTC
(sis) branch geoapi-4.0 updated: Initial version of a GDAL datastore using Panama for the binding to the native GDAL library.
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 d7187a26c0 Initial version of a GDAL datastore using Panama for the binding to the native GDAL library.
d7187a26c0 is described below
commit d7187a26c0b19d85903c1ce62a176c8ae62b192c
Author: qbialota <qu...@geomatys.com>
AuthorDate: Fri Aug 30 17:23:59 2024 +0200
Initial version of a GDAL datastore using Panama for the binding to the native GDAL library.
---
.../apache/sis/buildtools/gradle/Dependency.java | 1 +
.../org.apache.sis.feature/main/module-info.java | 1 +
.../main/module-info.java | 2 +
.../org.apache.sis.storage/main/module-info.java | 2 +
incubator/build.gradle.kts | 13 +
.../org.apache.sis.storage.DataStoreProvider | 4 +
.../main/module-info.java | 32 +
.../sis/storage/gdal/coverage/GdalProvider.java | 108 ++
.../sis/storage/gdal/coverage/GdalResource.java | 893 ++++++++++++
.../storage/gdal/coverage/GdalSampleDimension.java | 37 +
.../sis/storage/gdal/coverage/GdalStore.java | 209 +++
.../apache/sis/storage/gdal/internal/GdalImpl.java | 1428 ++++++++++++++++++++
.../apache/sis/storage/gdal/internal/GdalOgr.java | 149 ++
.../sis/storage/gdal/internal/panama/CPL.java | 13 +
.../sis/storage/gdal/internal/panama/CPLErr.java | 30 +
.../storage/gdal/internal/panama/GDALAccess.java | 18 +
.../gdal/internal/panama/GDALAsyncStatusType.java | 9 +
.../gdal/internal/panama/GDALColorEntry.java | 46 +
.../gdal/internal/panama/GDALColorInterp.java | 38 +
.../storage/gdal/internal/panama/GDALDataType.java | 133 ++
.../gdal/internal/panama/GDALPaletteInterp.java | 12 +
.../storage/gdal/internal/panama/GDALRWFlag.java | 24 +
.../sis/storage/gdal/internal/panama/Gdal.java | 977 +++++++++++++
.../sis/storage/gdal/internal/panama/OGRErr.java | 33 +
.../storage/gdal/internal/panama/PanamaUtils.java | 93 ++
.../org/apache/sis/storage/gdal/package-info.java | 21 +
.../org/apache/sis/storage/gdal/GdalStoreTest.java | 142 ++
.../test/org/apache/sis/storage/gdal/test.tiff | Bin 0 -> 4009 bytes
netbeans-project/nbproject/project.properties | 1 +
29 files changed, 4469 insertions(+)
diff --git a/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Dependency.java b/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Dependency.java
index 45f821fdd7..d67105e2ed 100644
--- a/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Dependency.java
+++ b/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Dependency.java
@@ -74,6 +74,7 @@ public final class Dependency {
Map.entry("storage.shapefile", "core:sis-shapefile"),
Map.entry("storage.gimi", "core:sis-gimi"),
Map.entry("storage.gsf", "core:sis-gsf"),
+ Map.entry("storage.gdal", "core:sis-gdal"),
Map.entry("storage.geopackage", "core:sis-geopackage"),
Map.entry("storage.coveragejson", "core:sis-coveragejson"),
Map.entry("portrayal.map", "core:sis-portrayal-map"),
diff --git a/endorsed/src/org.apache.sis.feature/main/module-info.java b/endorsed/src/org.apache.sis.feature/main/module-info.java
index 6daf162d76..a99f398169 100644
--- a/endorsed/src/org.apache.sis.feature/main/module-info.java
+++ b/endorsed/src/org.apache.sis.feature/main/module-info.java
@@ -79,6 +79,7 @@ module org.apache.sis.feature {
org.apache.sis.storage.netcdf,
org.apache.sis.storage.geotiff,
org.apache.sis.portrayal,
+ org.apache.sis.storage.gdal, // In the "incubator" sub-project.
org.apache.sis.gui; // In the "optional" sub-project.
exports org.apache.sis.image.processing.isoline to
diff --git a/endorsed/src/org.apache.sis.referencing/main/module-info.java b/endorsed/src/org.apache.sis.referencing/main/module-info.java
index 93ad403d91..e9624507c4 100644
--- a/endorsed/src/org.apache.sis.referencing/main/module-info.java
+++ b/endorsed/src/org.apache.sis.referencing/main/module-info.java
@@ -182,6 +182,7 @@ module org.apache.sis.referencing {
org.apache.sis.storage.geotiff,
org.apache.sis.storage.earthobservation,
org.apache.sis.storage.gimi, // In the "incubator" sub-project.
+ org.apache.sis.storage.gdal, // In the "incubator" sub-project.
org.apache.sis.portrayal,
org.apache.sis.console,
org.apache.sis.openoffice,
@@ -191,6 +192,7 @@ module org.apache.sis.referencing {
org.apache.sis.referencing.gazetteer,
org.apache.sis.storage.geotiff,
org.apache.sis.storage.netcdf,
+ org.apache.sis.storage.gdal, // In the "incubator" sub-project.
org.apache.sis.storage.earthobservation,
org.apache.sis.profile.japan;
diff --git a/endorsed/src/org.apache.sis.storage/main/module-info.java b/endorsed/src/org.apache.sis.storage/main/module-info.java
index f407ccc84a..17d69b0ed1 100644
--- a/endorsed/src/org.apache.sis.storage/main/module-info.java
+++ b/endorsed/src/org.apache.sis.storage/main/module-info.java
@@ -57,6 +57,7 @@ module org.apache.sis.storage {
org.apache.sis.storage.gimi, // In the "incubator" sub-project.
org.apache.sis.storage.shapefile, // In the "incubator" sub-project.
org.apache.sis.storage.geopackage, // In the "incubator" sub-project.
+ org.apache.sis.storage.gdal, // In the "incubator" sub-project.
org.apache.sis.util, // For the "About" command.
org.apache.sis.console,
org.apache.sis.openoffice,
@@ -71,6 +72,7 @@ module org.apache.sis.storage {
org.apache.sis.storage.gimi, // In the "incubator" sub-project.
org.apache.sis.storage.shapefile, // In the "incubator" sub-project.
org.apache.sis.storage.geopackage, // In the "incubator" sub-project.
+ org.apache.sis.storage.gdal, // In the "incubator" sub-project.
org.apache.sis.cloud.aws,
org.apache.sis.console,
org.apache.sis.gui; // In the "optional" sub-project.
diff --git a/incubator/build.gradle.kts b/incubator/build.gradle.kts
index a60706f6ae..e82d5a14ef 100644
--- a/incubator/build.gradle.kts
+++ b/incubator/build.gradle.kts
@@ -204,6 +204,19 @@ publishing {
description = "Read and write files in the GeoPackage format."
}
}
+ create<MavenPublication>("storage.gdal") {
+ var module = "org.apache.sis.storage.gdal"
+ groupId = "org.apache.sis.storage"
+ artifactId = "sis-gdal"
+ artifact(layout.buildDirectory.file("libs/${module}.jar"))
+ artifact(layout.buildDirectory.file("docs/${module}-sources.jar")) {classifier = "sources"}
+ artifact(layout.buildDirectory.file("docs/${module}-javadoc.jar")) {classifier = "javadoc"}
+ pom {
+ name = "Apache SIS storage using GDAL through Panama"
+ description = "Read and write files using the GDAL library. " +
+ "This module assumes that GDAL is pre-installed."
+ }
+ }
create<MavenPublication>("storage.gsf") {
var module = "org.apache.sis.storage.gsf"
groupId = "org.apache.sis.storage"
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/META-INF.services/org.apache.sis.storage.DataStoreProvider b/incubator/src/org.apache.sis.storage.gdal/main/META-INF.services/org.apache.sis.storage.DataStoreProvider
new file mode 100644
index 0000000000..5dfc37877a
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/META-INF.services/org.apache.sis.storage.DataStoreProvider
@@ -0,0 +1,4 @@
+# Workaround for Maven bug https://issues.apache.org/jira/browse/MNG-7855
+# The content of this file is automatically derived from module-info.class file.
+# Should be used only if the JAR file was on class-path rather than module-path.
+org.apache.sis.storage.gdal.coverage.GdalProvider
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/module-info.java b/incubator/src/org.apache.sis.storage.gdal/main/module-info.java
new file mode 100644
index 0000000000..73451b6a9e
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/module-info.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+/**
+ * Read and write files using the GDAL library.
+ * This module assumes that GDAL is pre-installed.
+ */
+module org.apache.sis.storage.gdal {
+ // Dependencies used in public API.
+ requires transitive org.apache.sis.referencing;
+ requires transitive org.apache.sis.storage;
+
+ exports org.apache.sis.storage.gdal.coverage;
+ exports org.apache.sis.storage.gdal.internal;
+
+ provides org.apache.sis.storage.DataStoreProvider
+ with org.apache.sis.storage.gdal.coverage.GdalProvider;
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/coverage/GdalProvider.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/coverage/GdalProvider.java
new file mode 100644
index 0000000000..898e74715d
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/coverage/GdalProvider.java
@@ -0,0 +1,108 @@
+package org.apache.sis.storage.gdal.coverage;
+
+import org.apache.sis.io.stream.IOUtilities;
+import org.apache.sis.parameter.ParameterBuilder;
+import org.apache.sis.storage.DataStore;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.DataStoreProvider;
+import org.apache.sis.storage.ProbeResult;
+import org.apache.sis.storage.StorageConnector;
+import org.apache.sis.storage.gdal.internal.GdalOgr;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.parameter.ParameterValueGroup;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Path;
+
+/**
+ * DataStoreProvider for Gdal Library (use Gdal to load files / data)
+ *
+ * @author Quentin BIALOTA (Geomatys)
+ */
+public class GdalProvider extends DataStoreProvider {
+
+ /**
+ * Name of the provider
+ */
+ public static final String NAME = "gdal";
+
+ /**
+ * Path parameter (file path to load)
+ */
+ public static final ParameterDescriptor<URI> PATH = new ParameterBuilder()
+ .addName(LOCATION)
+ .addName("path")
+ .setRemarks("file path")
+ .setRequired(true)
+ .create(URI.class, null);
+
+ /**
+ * Searched drivers (used by gdal to load the file with correct drivers)
+ */
+ public static final ParameterDescriptor<String> SEARCHED_DRIVERS = new ParameterBuilder()
+ .addName("searchedDrivers")
+ .setRequired(false)
+ .create(String.class, null);
+
+ /**
+ * Global parameter description group
+ */
+ public static final ParameterDescriptorGroup PARAMETERS_DESCRIPTOR =
+ new ParameterBuilder().addName(NAME).createGroup(
+ PATH,
+ SEARCHED_DRIVERS);
+
+ @Override
+ public String getShortName() {
+ return NAME;
+ }
+
+ @Override
+ public ParameterDescriptorGroup getOpenParameters() {
+ return PARAMETERS_DESCRIPTOR;
+ }
+
+ @Override
+ public ProbeResult probeContent(StorageConnector connector) throws DataStoreException {
+ try {
+ GdalOgr.init();
+ } catch (DataStoreException ex) {
+ return ProbeResult.UNSUPPORTED_STORAGE;
+ }
+
+ final Path path = connector.getStorageAs(Path.class);
+ if (path == null) return ProbeResult.UNSUPPORTED_STORAGE;
+
+ String extension = IOUtilities.extension(path);
+ if (extension == null) return ProbeResult.UNSUPPORTED_STORAGE;
+ extension = extension.toLowerCase();
+
+ if (!GdalOgr.GDAL_EXTENSIONS.contains(extension)) return ProbeResult.UNSUPPORTED_STORAGE;
+
+ String mimeType = GdalOgr.EXTENSION_TO_MIMETYPE.get(extension);
+ if (mimeType == null) mimeType = "application/x-gdal+"+extension;
+ return new ProbeResult(true, mimeType, null);
+ }
+
+ @Override
+ public DataStore open(StorageConnector connector) throws DataStoreException {
+ final Path path = connector.getStorageAs(Path.class);
+ connector.closeAllExcept(path);
+ if (path == null) {
+ throw new DataStoreException("Unsupported parameters.");
+ }
+ try {
+ return new GdalStore(path);
+ } catch (IOException ex) {
+ throw new DataStoreException(ex.getMessage(), ex);
+ }
+ }
+
+ @Override
+ public DataStore open(ParameterValueGroup parameterValueGroup) throws DataStoreException {
+ GdalOgr.init();
+ return new GdalStore(parameterValueGroup);
+ }
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/coverage/GdalResource.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/coverage/GdalResource.java
new file mode 100644
index 0000000000..5d250d8371
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/coverage/GdalResource.java
@@ -0,0 +1,893 @@
+package org.apache.sis.storage.gdal.coverage;
+
+import org.apache.sis.coverage.SampleDimension;
+import org.apache.sis.coverage.grid.GridCoverage;
+import org.apache.sis.coverage.grid.GridCoverageBuilder;
+import org.apache.sis.coverage.grid.GridDerivation;
+import org.apache.sis.coverage.grid.GridExtent;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.coverage.grid.GridRoundingMode;
+import org.apache.sis.coverage.grid.IllegalGridGeometryException;
+import org.apache.sis.coverage.grid.PixelInCell;
+import org.apache.sis.coverage.privy.ColorModelFactory;
+import org.apache.sis.coverage.privy.ObservableImage;
+import org.apache.sis.io.stream.IOUtilities;
+import org.apache.sis.measure.Units;
+import org.apache.sis.metadata.iso.content.DefaultCoverageDescription;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.crs.AbstractCRS;
+import org.apache.sis.referencing.crs.DefaultDerivedCRS;
+import org.apache.sis.referencing.cs.AxesConvention;
+import org.apache.sis.referencing.operation.DefaultConversion;
+import org.apache.sis.referencing.operation.provider.Affine;
+import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
+import org.apache.sis.referencing.privy.AffineTransform2D;
+import org.apache.sis.storage.AbstractGridCoverageResource;
+import org.apache.sis.storage.Aggregate;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.Resource;
+import org.apache.sis.storage.base.ResourceOnFileSystem;
+import org.apache.sis.storage.gdal.internal.GdalOgr;
+import org.apache.sis.storage.gdal.internal.panama.*;
+import org.apache.sis.util.iso.DefaultNameFactory;
+import org.opengis.coverage.CannotEvaluateException;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.crs.SingleCRS;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.NoninvertibleTransformException;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
+import org.opengis.util.GenericName;
+
+import javax.measure.Unit;
+import java.awt.Color;
+import java.awt.Point;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.DataBufferDouble;
+import java.awt.image.DataBufferFloat;
+import java.awt.image.DataBufferInt;
+import java.awt.image.DataBufferShort;
+import java.awt.image.DataBufferUShort;
+import java.awt.image.RenderedImage;
+import java.awt.image.WritableRaster;
+import java.lang.foreign.Arena;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.ValueLayout;
+import java.net.URI;
+import java.nio.ByteOrder;
+import java.nio.DoubleBuffer;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import static org.apache.sis.storage.gdal.internal.GdalOgr.GDAL;
+
+/**
+ * GdalResource, represents a resource loaded by gdal lib
+ *
+ * @author Quentin BIALOTA (Geomatys)
+ * @author Johan Sorel (Geomatys)
+ */
+public class GdalResource extends AbstractGridCoverageResource implements ResourceOnFileSystem, Aggregate {
+
+ /**
+ * Synchronisation object
+ */
+ private final Object SYNC = new Object();
+
+ /**
+ * Associated Gdal DataStore
+ */
+ private final GdalStore store;
+
+ /**
+ * Resource indentifier
+ */
+ private final GenericName identifier;
+
+ /**
+ * File uri / path
+ */
+ private String gdalUri;
+ private Path fileTfw;
+
+ private String[] searchedDrivers = null;
+ private String[] searchedSideFiles = null;
+
+ // Caches
+ GridGeometry gridGeom;
+ CoordinateReferenceSystem coverageCRS;
+ CoordinateReferenceSystem imageCRS;
+ AffineTransform2D gridToCrs;
+ AffineTransform2D crsToGrid;
+ DefaultCoverageDescription coverageDescription;
+
+ boolean sampleDimensionsRead = false;
+ List<GdalSampleDimension> sampleDimensions = new ArrayList<>();
+
+ private List<GdalResource> subdatasets = null;
+
+ /**
+ * Constructor
+ */
+ public GdalResource(URI uri, GdalStore store) throws DataStoreException{
+ this(uri, store, store.getOpenParameters().get());
+ }
+
+ /**
+ * Constructor
+ */
+ public GdalResource(URI uri, GdalStore store, ParameterValueGroup configuration) throws DataStoreException{
+ super(null);
+ this.store = store;
+ identifier = DefaultNameFactory.provider().createGenericName(null, extractName(uri));
+ GdalOgr.init();
+ if (uri.toString().startsWith("file:")) {
+ gdalUri = Paths.get(uri).toFile().getAbsolutePath();
+ } else if (uri.toString().startsWith("http://")) {
+ gdalUri = "/vsicurl/"+uri.toString();
+ } else if (uri.toString().startsWith("s3://")) {
+ throw new DataStoreException("S3 resource not supported");
+ } else {
+ gdalUri = uri.toString();
+ }
+ }
+
+ /**
+ * Constructor
+ */
+ private GdalResource(String gdalUri, String name, GdalStore store, ParameterValueGroup configuration) throws DataStoreException{
+ super(null);
+ this.store = store;
+ identifier = DefaultNameFactory.provider().createGenericName(null, name);
+ GdalOgr.init();
+ this.gdalUri = gdalUri;
+ }
+
+ @Override
+ public Optional<GenericName> getIdentifier() throws DataStoreException {
+ return Optional.of(identifier);
+ }
+
+ public void setSearchedDrivers(String[] searchedDrivers) {
+ this.searchedDrivers = searchedDrivers;
+ }
+
+ public void setSearchedSideFiles(String[] searchedSideFiles) {
+ this.searchedSideFiles = searchedSideFiles;
+ }
+
+ /**
+ * Open data set using gdal
+ *
+ * @return MemorySegment representing the dataset loaded in gdal
+ * @throws DataStoreException If en error occurs in GDALOpenEx or if the dataset loaded is null
+ */
+ MemorySegment openDataSet() throws DataStoreException {
+ final int openMode = Gdal.GDAL_OF_RASTER | GDALAccess.GA_ReadOnly.getCode();
+ final MemorySegment pt; // limit used memory
+
+ try {
+ pt = GdalOgr.GDAL().GDALOpenEx(gdalUri, openMode, searchedDrivers, null, searchedSideFiles);
+ } catch (Throwable t) {
+ throw new DataStoreException(t.getMessage(), t);
+ }
+ if (pt == null || pt == MemorySegment.NULL) {
+ throw new DataStoreException("Gdal dataset is null for path : "+gdalUri);
+ }
+ return pt;
+ }
+
+ /**
+ * Get Grid Geometry of the dataset
+ * Set gridGeom var of this object if is not already set
+ *
+ * @return GridGeometry of the dataset
+ * @throws DataStoreException if an error occurs with Gdal
+ */
+ @Override
+ public GridGeometry getGridGeometry() throws DataStoreException {
+ if (gridGeom != null) return gridGeom;
+
+ try {
+ synchronized (SYNC) {
+ final MemorySegment datasetPt = openDataSet();
+ try {
+ loadMetadatas(datasetPt);
+ } finally {
+ GdalOgr.GDAL().GDALClose(datasetPt);
+ }
+ }
+ } catch (Throwable t) {
+ throw new DataStoreException(t.getMessage(), t);
+ }
+ return gridGeom;
+ }
+
+ /**
+ * Get Samples Dimensions (list) of the dataset
+ *
+ * @return List of SampleDimension of the dataset
+ * @throws DataStoreException if an error occurs with Gdal
+ */
+ @Override
+ public List<SampleDimension> getSampleDimensions() throws DataStoreException {
+ if (!isMetadatasLoaded()) {
+ try {
+ synchronized (SYNC) {
+ final MemorySegment datasetPt = openDataSet();
+ try {
+ loadMetadatas(datasetPt);
+ } finally {
+ GdalOgr.GDAL().GDALClose(datasetPt);
+ }
+ }
+ } catch (Throwable t) {
+ throw new DataStoreException(t.getMessage(), t);
+ }
+ }
+ return (List<SampleDimension>) Collections.unmodifiableList((List) sampleDimensions);
+ }
+
+ /**
+ * Read the GridCoverage of the dataset
+ *
+ * @param domain sub grid geometry if desired (optional, can be set to null)
+ * @param range range to read if desired (optional)
+ * @return GridCoverage
+ * @throws DataStoreException if an error occurs with Gdal, or with gridGeometry
+ */
+ @Override
+ public GridCoverage read(GridGeometry domain, int ... range) throws DataStoreException {
+ final GridGeometry gg = getGridGeometry();
+ final GridGeometry resultGrid;
+ final GridExtent extent;
+ final int[] subsampling;
+ try {
+ if (domain != null) {
+ GridDerivation derived = gg.derive()
+ .rounding(GridRoundingMode.ENCLOSING)
+ .subgrid(domain);
+ extent = derived.getIntersection();
+ subsampling = derived.getSubsampling();
+ resultGrid = derived.build();
+ } else {
+ GridDerivation derived = gg.derive();
+ extent = derived.getIntersection();
+ subsampling = derived.getSubsampling();
+ resultGrid = derived.build();
+ }
+ } catch (IllegalGridGeometryException ex) {
+ throw new DataStoreException(ex.getMessage(), ex);
+ }
+
+ try {
+ return readInGridCRS(resultGrid, extent, subsampling, range);
+ } catch (TransformException ex) {
+ throw new DataStoreException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Read a coverage with defined image area.
+ *
+ * @param extent Extent representing selected domain in source (full-resolution) grid. Never null.
+ * @param subsampling image subsampling in pixels
+ * @throws DataStoreException If an error occurs with Gdal, or with gridGeometry
+ * @throws TransformException If an error occurs with transformations
+ */
+ protected GridCoverage readInGridCRS(GridGeometry resultGrid, GridExtent extent, int[] subsampling, int... range) throws DataStoreException, TransformException {
+ final long unsqueezableDimensions = IntStream.range(0, extent.getDimension())
+ .mapToLong(idx -> extent.getSize(idx) / subsampling[idx])
+ .filter(size -> size > 1)
+ .count();
+
+ if (unsqueezableDimensions < 3) {
+ //read a single slice
+ return readGridSlice(resultGrid, getLow(extent), getHigh(extent), subsampling, range);
+ } else {
+ return new GridCoverageSelection(resultGrid, getSampleDimensions(), subsampling, range);
+ }
+ }
+
+ /**
+ * Get low values of a GridExtent
+ *
+ * @param extent source
+ * @return int[] array of low values
+ */
+ private static int[] getLow(GridExtent extent) {
+ long[] values = extent.getLow().getCoordinateValues();
+ final int[] array = new int[values.length];
+ for(int i=0;i<values.length;i++) array[i] = (int) values[i];
+ return array;
+ }
+
+ /**
+ * Get high values of a GridExtent
+ *
+ * @param extent source
+ * @return int[] array of high values
+ */
+ private static int[] getHigh(GridExtent extent) {
+ long[] values = extent.getHigh().getCoordinateValues();
+ final int[] array = new int[values.length];
+ for(int i=0;i<values.length;i++) array[i] = (int) (values[i]+1);
+ return array;
+ }
+
+ /**
+ * Check if metadatas are aleready loaded
+ *
+ * @return boolean if metadata already loaded
+ */
+ boolean isMetadatasLoaded() {
+ return gridGeom != null;
+ }
+
+ /**
+ * Load metadata of a dataset loaded in gdal
+ *
+ * @param datasetPt MemorySegment of the dataset loaded in gdal
+ * @throws DataStoreException if en error occurs in gdal
+ */
+ void loadMetadatas(MemorySegment datasetPt) throws DataStoreException {
+ if (gridGeom != null) return; //already loaded
+
+ final Gdal gdal = GdalOgr.GDAL();
+
+ // Read coordinate reference system ------------------------------------
+ try {
+
+ final String wkt = gdal.GDALGetProjectionRef(datasetPt);
+ if (wkt != null && !wkt.isEmpty()) {
+ // force longitude first
+ coverageCRS = convertGdalCrs(wkt);
+ } else {
+ coverageCRS = null; //IMAGECRS
+ }
+ } catch (Throwable ex) {
+ throw new DataStoreException(ex.getMessage());
+ }
+
+ // Read transform ------------------------------------------------------
+ final AffineTransform2D gridToCRS;
+ try (var arena = Arena.ofConfined()) {
+ MemorySegment affineBuffer = arena.allocate(6*8);
+
+ if (gdal.GDALGetGeoTransform(datasetPt,affineBuffer) == CPLErr.CE_None) {
+ final DoubleBuffer db = affineBuffer.asByteBuffer().order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer();
+ final double d0 = db.get(0);
+ final double d1 = db.get(1);
+ final double d2 = db.get(2);
+ final double d3 = db.get(3);
+ final double d4 = db.get(4);
+ final double d5 = db.get(5);
+ gridToCRS = new AffineTransform2D(d1,d4,d2,d5,d0,d3);
+ } else {
+ //no transform available
+ gridToCRS = new AffineTransform2D(1, 0, 0, 1, 0, 0);
+ }
+
+ // Read extent ---------------------------------------------------------
+ final int xSize = gdal.GDALGetRasterXSize(datasetPt);
+ final int ySize = gdal.GDALGetRasterYSize(datasetPt);
+ final GridExtent gridExtent = new GridExtent(xSize, ySize);
+
+ gridGeom = new GridGeometry(gridExtent, PixelInCell.CELL_CENTER, gridToCRS, coverageCRS);
+
+ // Read sample dimensions ----------------------------------------------
+ final int nbBand = gdal.GDALGetRasterCount(datasetPt);
+
+ for (int i=0;i<nbBand;i++) {
+
+ final MemorySegment hBand = gdal.GDALGetRasterBand(datasetPt,i+1);
+ final GDALDataType dataType = gdal.GDALGetRasterDataType(hBand);
+
+ // min,max are in the sample format, not in geophysic
+ final double min = gdal.GDALGetRasterMinimum(hBand, MemorySegment.NULL);
+ final double max = gdal.GDALGetRasterMaximum (hBand, MemorySegment.NULL);
+ final double offSet = gdal.GDALGetRasterOffset(hBand, MemorySegment.NULL);
+ final double scale = gdal.GDALGetRasterScale(hBand, MemorySegment.NULL);
+ final String unitType = gdal.GDALGetRasterUnitType(hBand);
+
+ final Unit<?> unit = Units.valueOf(unitType);
+
+ final MemorySegment colorTablePt = gdal.GDALGetRasterColorTable(hBand);
+
+ final SampleDimension.Builder sdb = new SampleDimension.Builder()
+ .setName("band"+i);
+
+ if (colorTablePt != null && colorTablePt != MemorySegment.NULL && colorTablePt.address() != 0) {
+ // build color model using palette
+ final int nbColor = gdal.GDALGetColorEntryCount(colorTablePt);
+ final Color[] colors = new Color[nbColor];
+
+ MemorySegment buffer = arena.allocate(GDALColorEntry.LAYOUT);
+ final GDALColorEntry entry = new GDALColorEntry(buffer);
+
+ for (int index=0;index<nbColor;index++) {
+ gdal.GDALGetColorEntryAsRGB(colorTablePt, index, entry);
+ colors[index] = new Color(entry.getC1(), entry.getC2(), entry.getC3(), entry.getC4());
+ }
+
+ sdb.addQuantitative("data", 0, colors.length, 1, 0, unit);
+ } else {
+ sdb.addQuantitative("data", (int)min, (int)max, scale, offSet, unit);
+ }
+
+ //TODO : find in gdal if no data values are in sample or geophysic
+ //final double noDataValue = GDAL.GDALGetRasterNoDataValue(hBand, null);
+ //if (!(Double.isNaN(noDataValue) || -1e-10==noDataValue)) {
+ // final Category noDataCat = new Category(Vocabulary.formatInternational(Vocabulary.Keys.Nodata), new Color(0, 0, 0, 0), noDataValue);
+ //}
+
+ GdalSampleDimension sd = new GdalSampleDimension(sdb.build(), dataType);
+ sampleDimensions.add(sd);
+ }
+
+ // get transforms in corner
+ gridToCrs = (AffineTransform2D) gridGeom.getGridToCRS(PixelInCell.CELL_CORNER);
+ try {
+ crsToGrid = (AffineTransform2D) gridToCrs.inverse();
+ } catch (NoninvertibleTransformException ex) {
+ throw new DataStoreException(ex.getMessage(), ex);
+ }
+ final Map<String,?> name = Collections.singletonMap(DefaultDerivedCRS.NAME_KEY, "Derived - "+coverageCRS.getName().toString());
+ imageCRS = DefaultDerivedCRS.create(name, (SingleCRS)coverageCRS,
+ new DefaultConversion(name, new Affine(), crsToGrid, null),
+ coverageCRS.getCoordinateSystem());
+
+ } catch (Throwable t) {
+ throw new DataStoreException(t);
+ }
+ }
+
+ /**
+ * Convert gdal wkt to CoordinateReferenceSystem
+ *
+ * @param wkt String given by gdal
+ * @return CoordinateReferenceSystem (CRS)
+ * @throws DataStoreException if en error occurs with wkt (wkt is not conforms)
+ */
+ private static CoordinateReferenceSystem convertGdalCrs(String wkt) throws DataStoreException {
+ if ("LOCAL_CS[\"MOLLWEIDE\",UNIT[\"METERS\",1]]".equalsIgnoreCase(wkt)) {
+ //case found with ADF datas
+ wkt =
+ "PROJCRS[\"WGS 84 / Mollweide\",\n" +
+ " BASEGEODCRS[\"WGS 84\",\n" +
+ " DATUM[\"World Geodetic System 1984\",\n" +
+ " ELLIPSOID[\"WGS 84\", 6378137.0, 298.257223563, LENGTHUNIT[\"metre\", 1]]],\n" +
+ " PRIMEM[\"Greenwich\", 0.0, ANGLEUNIT[\"degree\", 0.017453292519943295]]],\n" +
+ " CONVERSION[\"World Mollweide\",\n" +
+ " METHOD[\"Mollweide\"]],\n" +
+ " CS[Cartesian, 2],\n" +
+ " AXIS[\"Easting (E)\", east, ORDER[1]],\n" +
+ " AXIS[\"Northing (N)\", north, ORDER[2]],\n" +
+ " LENGTHUNIT[\"metre\", 1],\n" +
+ " SCOPE[\"Should not use.\"],\n" +
+ " AREA[\"World between 89°S and 89°N.\"],\n" +
+ " BBOX[-89.00, -180.00, 89.00, 180.00],\n" +
+ " REMARK[\"Mollweide CRS extrapolated from Gdal dataset WKT\"]]";
+ }
+
+ try {
+ return ((AbstractCRS) CRS.fromWKT(wkt)).forConvention(AxesConvention.RIGHT_HANDED);
+ } catch (FactoryException ex) {
+ throw new DataStoreException(ex.getMessage(),ex);
+ }
+ }
+
+ /**
+ * Extract simple name from URI.
+ *
+ * @param uri uri of the file
+ * @return name
+ */
+ private static String extractName(URI uri) {
+ String name = IOUtilities.filename(uri);
+ if (name==null) {
+ name = uri.toString();
+ final int idx = name.lastIndexOf('/');
+ if(idx>0) name = name.substring(idx+1);
+ }
+ final int sep = name.indexOf('.');
+ if (sep>0) {
+ name = name.substring(0, sep);
+ }
+
+ return name;
+ }
+
+ /**
+ * Get component files stored
+ * @return Array of Path of the component files stored
+ * @throws DataStoreException
+ */
+ public Path[] getComponentFiles() throws DataStoreException {
+ return store.getComponentFiles();
+ }
+
+ /**
+ * Read a coverage slice with defined image area.
+ * This method is synchronized internaly because gdal crash when used in a multithread context.
+ *
+ * @param resultGrid
+ * @param areaLower
+ * @param areaUpper
+ * @param subsampling
+ * @param range
+ * @return GridCoverage
+ * @throws DataStoreException
+ */
+ protected GridCoverage readGridSlice(GridGeometry resultGrid, int[] areaLower, int[] areaUpper, int[] subsampling, int ... range) throws DataStoreException {
+ long[] resultExtent = getResultExtent(areaLower, areaUpper, subsampling);
+ if (resultExtent[0] == 0 || resultExtent[1] == 0) throw new DataStoreException();
+
+ final int viewTLX = areaLower[0];
+ final int viewTLY = areaLower[1];
+ final int viewBRX = areaUpper[0];
+ final int viewBRY = areaUpper[1];
+ final int outWidth = (int) resultExtent[0];
+ final int outHeight = (int) resultExtent[1];
+
+ try {
+ synchronized (SYNC) {
+ final Gdal gdal = GdalOgr.GDAL();
+
+ //read datas
+ final MemorySegment datasetPt = openDataSet();
+ final BufferedImage image;
+ final List<SampleDimension> sampleDims = getSampleDimensions();
+
+ if (range != null && range.length > 0) {
+ //user selected bands
+ } else {
+ //generate default bands
+ range = new int[sampleDimensions.size()];
+ for (int i = 0; i < range.length; i++) range[i] = i;
+ }
+
+ try {
+ final int nbBand = sampleDimensions.size();
+ if (nbBand == 1) {
+
+ final GDALDataType gdalDataType = sampleDimensions.get(0).getGdalDataType();
+ final MemorySegment bandPt = gdal.GDALGetRasterBand(datasetPt, 1);
+ final int imageDataType = gdalDataType.getImageDataType();
+ final int nbBytePerSample = DataBuffer.getDataTypeSize(gdalDataType.getImageDataType()) / 8;
+ final int bandLength = outWidth * outHeight;
+ try (var arena = Arena.ofConfined()){
+ final MemorySegment bufferPt = arena.allocate(8L * outWidth * outHeight * nbBytePerSample);
+
+ final CPLErr err;
+ try {
+ err = gdal.GDALRasterIO(bandPt, GDALRWFlag.GF_Read,
+ viewTLX, viewTLY,
+ viewBRX - viewTLX, viewBRY - viewTLY,
+ bufferPt,
+ outWidth,
+ outHeight,
+ gdalDataType,
+ 0, 0);
+ } catch (Throwable t) {
+ throw new DataStoreException(t);
+ }
+
+ if (!CPLErr.CE_None.equals(err)) {
+ throw new DataStoreException("Failed to read coverage, error :" + err);
+ }
+
+ if (outWidth == 0 || outHeight == 0) {
+ throw new DataStoreException();
+ }
+
+ Point upperLeft = new Point(0, 0);
+ WritableRaster raster = createRaster(outWidth, outHeight, nbBand, imageDataType);
+ //TODO try to reuse java colormodel if possible
+ //create a temporary fallback colormodel which will always work
+ //extract grayscale min/max from sample dimension
+ ColorModel graycm = ColorModelFactory.createGrayScale(imageDataType, nbBand, 0, 0.0, 1.0);
+ image = new ObservableImage(graycm, raster, false, null);
+
+ final DataBuffer imageBuffer = image.getTile(0, 0).getDataBuffer();
+ switch (imageDataType) {
+ case DataBuffer.TYPE_BYTE:
+ bufferPt.asByteBuffer().get(0, ((DataBufferByte) imageBuffer).getData(), 0, bandLength);
+ break;
+ case DataBuffer.TYPE_USHORT:
+ bufferPt.asByteBuffer().asShortBuffer().get(0, ((DataBufferUShort) imageBuffer).getData(), 0, bandLength);
+ break;
+ case DataBuffer.TYPE_SHORT:
+ bufferPt.asByteBuffer().asShortBuffer().get(0, ((DataBufferShort) imageBuffer).getData(), 0, bandLength);
+ break;
+ case DataBuffer.TYPE_INT:
+ bufferPt.asByteBuffer().asIntBuffer().get(0, ((DataBufferInt) imageBuffer).getData(), 0, bandLength);
+ break;
+ case DataBuffer.TYPE_FLOAT:
+ bufferPt.asByteBuffer().asFloatBuffer().get(0, ((DataBufferFloat) imageBuffer).getData(), 0, bandLength);
+ break;
+ case DataBuffer.TYPE_DOUBLE:
+ bufferPt.asByteBuffer().asDoubleBuffer().get(0, ((DataBufferDouble) imageBuffer).getData(), 0, bandLength);
+ break;
+ default:
+ throw new DataStoreException("Unexpected image data type " + imageDataType);
+ }
+ } finally {
+ //clear gdal cache, and java direct buffer
+ gdal.GDALFlushRasterCache(bandPt);
+ }
+
+ } else {
+ //we expect each band type to be the same
+ final GDALDataType gdalDataType = sampleDimensions.getFirst().getGdalDataType();
+ final int imageDataType = gdalDataType.getImageDataType();
+ final int nbBytePerSample = DataBuffer.getDataTypeSize(gdalDataType.getImageDataType()) / 8;
+ final int bandLength = outWidth * outHeight;
+ try (var arena = Arena.ofConfined()) {
+ final MemorySegment bufferPt = arena.allocate(8L * bandLength * nbBytePerSample);
+
+ if (range != null && range.length > 0) {
+ //user selected bands
+ } else {
+ //generate default bands
+ range = new int[nbBand];
+ for (int i = 0; i < range.length; i++) range[i] = i;
+ }
+
+ final WritableRaster raster = createRaster(outWidth, outHeight, range.length, imageDataType);
+
+ final ColorModel colorModel;
+ if (range.length == 3 && GDALDataType.GDT_Byte.equals(gdalDataType)) {
+ colorModel = new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR).getColorModel();
+ } else {
+ colorModel = ColorModelFactory.createGrayScale(raster.getTransferType(), raster.getNumBands(), 0, 0, 255);
+ }
+ image = new BufferedImage(colorModel, raster, false, new Hashtable<>());
+ final DataBuffer imageBuffer = image.getTile(0, 0).getDataBuffer();
+
+ for (int i = 0; i < range.length; i++) {
+ final MemorySegment bandPt = gdal.GDALGetRasterBand(datasetPt, range[i] + 1);
+ try {
+ final CPLErr err = gdal.GDALRasterIO(bandPt, GDALRWFlag.GF_Read,
+ viewTLX, viewTLY,
+ viewBRX - viewTLX, viewBRY - viewTLY,
+ bufferPt, outWidth, outHeight,
+ gdalDataType,
+ 0, 0);
+
+ if (!CPLErr.CE_None.equals(err)) {
+ throw new DataStoreException("Failed to read coverage, error :" + err);
+ }
+
+ final int offset = i * bandLength;
+ switch (imageDataType) {
+ case DataBuffer.TYPE_BYTE:
+ bufferPt.asByteBuffer().get(0, ((DataBufferByte) imageBuffer).getData(), offset,
+ ((DataBufferByte) imageBuffer).getData().length);
+ break;
+ case DataBuffer.TYPE_USHORT:
+ bufferPt.asByteBuffer().asShortBuffer().get(0, ((DataBufferUShort) imageBuffer).getData(), offset, bandLength);
+ break;
+ case DataBuffer.TYPE_SHORT:
+ bufferPt.asByteBuffer().asShortBuffer().get(0, ((DataBufferShort) imageBuffer).getData(), offset, bandLength);
+ break;
+ case DataBuffer.TYPE_INT:
+ bufferPt.asByteBuffer().asIntBuffer().get(0, ((DataBufferInt) imageBuffer).getData(), offset, bandLength);
+ break;
+ case DataBuffer.TYPE_FLOAT:
+ bufferPt.asByteBuffer().asFloatBuffer().get(0, ((DataBufferFloat) imageBuffer).getData(), offset, bandLength);
+ break;
+ case DataBuffer.TYPE_DOUBLE:
+ bufferPt.asByteBuffer().asDoubleBuffer().get(0, ((DataBufferDouble) imageBuffer).getData(), offset, bandLength);
+ break;
+ default:
+ throw new DataStoreException("Unexpected image data type " + imageDataType);
+ }
+ } finally {
+ //clear gdal cache
+ gdal.GDALFlushRasterCache(bandPt);
+ }
+ }
+
+ } catch (Throwable t) {
+ throw new DataStoreException(t);
+ }
+ }
+ } finally {
+ //flush possible write caches
+ gdal.GDALFlushCache(datasetPt);
+ //close dataset
+ gdal.GDALClose(datasetPt);
+ //try to clear as many in memory cache blocks
+ while (gdal.GDALFlushCacheBlock() == 1) ;
+ }
+
+ //build coverage grid geometry
+ final GridExtent ext = new GridExtent(outWidth, outHeight);
+ final double scaleX = (double) (viewBRX - viewTLX) / outWidth;
+ final double scaleY = (double) (viewBRY - viewTLY) / outHeight;
+ final AffineTransform2D trs = new AffineTransform2D(scaleX, 0, 0, scaleY, viewTLX, viewTLY);
+
+ final MathTransform gridToCRS;
+ try {
+ gridToCRS = DefaultMathTransformFactory.provider().createConcatenatedTransform(new AffineTransform2D(trs), getGridGeometry().getGridToCRS(PixelInCell.CELL_CENTER));
+ } catch (FactoryException ex) {
+ throw new DataStoreException(ex.getMessage(), ex);
+ }
+ final GridGeometry outGridGeom = new GridGeometry(ext, PixelInCell.CELL_CENTER, gridToCRS, gridGeom.getCoordinateReferenceSystem());
+
+ //build coverage
+ final GridCoverageBuilder gcb = new GridCoverageBuilder();
+ gcb.setValues(image);
+ gcb.setDomain(outGridGeom);
+ final SampleDimension[] selection = new SampleDimension[range.length];
+ for (int i = 0; i < range.length; i++) {
+ selection[i] = sampleDims.get(range[i]);
+ }
+ gcb.setRanges(selection);
+ return gcb.build();
+ }
+ } catch (Throwable t) {
+ throw new DataStoreException(t);
+ }
+ }
+
+ /**
+ * Calculate the final size of each dimension.
+ *
+ * @param areaLower image features lower corner
+ * @param areaUpper image features upper corner
+ * @param subsampling image subsampling
+ */
+ public static long[] getResultExtent(int[] areaLower, int[] areaUpper, int[] subsampling) {
+ //calculate output size
+ final long[] outExtent = new long[areaLower.length];
+ for(int i=0;i<outExtent.length;i++){
+ outExtent[i] = (areaUpper[i]-areaLower[i]+subsampling[i]-1) / subsampling[i];
+ }
+
+ return outExtent;
+ }
+
+ /**
+ * Create WritableRaster
+ *
+ * @param width of the raster
+ * @param height of the raster
+ * @param nbBand number of bands of the raster
+ * @param dataType of the raster
+ * @return WritableRaster
+ * @throws IllegalArgumentException if parameters are not correct / supported
+ */
+ private static WritableRaster createRaster(int width, int height, int nbBand, int dataType) throws IllegalArgumentException {
+ final Point upperLeft = new Point(0, 0);
+ final WritableRaster raster;
+ if (nbBand == 1) {
+ if (dataType == DataBuffer.TYPE_BYTE || dataType == DataBuffer.TYPE_USHORT || dataType == DataBuffer.TYPE_INT) {
+ raster = WritableRaster.createBandedRaster(dataType, width, height, nbBand, upperLeft);
+ } else {
+ //create it ourselves
+ final DataBuffer buffer;
+ if (dataType == DataBuffer.TYPE_SHORT) buffer = new DataBufferShort(width*height);
+ else if (dataType == DataBuffer.TYPE_FLOAT) buffer = new DataBufferFloat(width*height);
+ else if (dataType == DataBuffer.TYPE_DOUBLE) buffer = new DataBufferDouble(width*height);
+ else throw new IllegalArgumentException("Type not supported "+dataType);
+ final int[] zero = new int[1];
+ raster = WritableRaster.createBandedRaster(buffer, width, height, width, zero, zero, upperLeft);
+ //raster = org.apache.sis.coverage.privy.RasterFactory.createRaster(buffer, width, height, 1, width, zero, zero, upperLeft);
+ }
+
+ } else {
+ //create it ourselves
+ final DataBuffer buffer;
+ if (dataType == DataBuffer.TYPE_BYTE) buffer = new DataBufferByte(width*height*nbBand);
+ else if (dataType == DataBuffer.TYPE_SHORT) buffer = new DataBufferShort(width*height*nbBand);
+ else if (dataType == DataBuffer.TYPE_USHORT) buffer = new DataBufferUShort(width*height*nbBand);
+ else if (dataType == DataBuffer.TYPE_FLOAT) buffer = new DataBufferFloat(width*height*nbBand);
+ else if (dataType == DataBuffer.TYPE_DOUBLE) buffer = new DataBufferDouble(width*height*nbBand);
+ else throw new IllegalArgumentException("Type not supported "+dataType);
+ final int[] bankIndices = new int[nbBand];
+ final int[] bandOffsets = new int[nbBand];
+ for (int i=1;i<nbBand;i++) {
+ bandOffsets[i] = bandOffsets[i-1] + width*height;
+ }
+ raster = WritableRaster.createBandedRaster(buffer, width, height, width, bankIndices, bandOffsets, upperLeft);
+ }
+ return raster;
+ }
+
+ @Override
+ public Collection<? extends Resource> components() throws DataStoreException {
+ if (subdatasets == null) {
+ try {
+ synchronized (SYNC) {
+ final Gdal gdal = GdalOgr.GDAL();
+ final MemorySegment datasetPt = openDataSet();
+ final List<GdalResource> subdatasets = new ArrayList<>();
+ try {
+ /*
+ Contains a list like :
+ SUBDATASET_1_NAME=NETCDF:"/run/media/.../C1_2020.nc":elevation
+ SUBDATASET_1_DESC=[9004x9482] geoid_height_above_reference_ellipsoid (32-bit floating-point)
+ SUBDATASET_2_NAME=NETCDF:"/run/media/.../C1_2020.nc":value_count
+ SUBDATASET_2_DESC=[9004x9482] value_count (32-bit integer)
+ ...
+ */
+ final MemorySegment pt = gdal.GDALGetMetadata(datasetPt, "SUBDATASETS");
+ if (pt != null && pt != MemorySegment.NULL) {
+
+ List<String> parts = new ArrayList<>();
+ for (int i = 0; ; i++) {
+ MemorySegment stringPtr = pt.getAtIndex(ValueLayout.ADDRESS, i);
+ if (stringPtr.equals(MemorySegment.NULL)) {
+ break;
+ }
+ String string = stringPtr.getString(0);
+ parts.add(string);
+ }
+
+ for (String part : parts) {
+ int idx = part.indexOf('=');
+ if (idx > 0) {
+ String key = part.substring(0, idx);
+ String value = part.substring(idx + 1, part.length());
+ if (key.endsWith("_NAME")) {
+ String name = value.substring(value.lastIndexOf(":") + 1);
+ GdalResource r = new GdalResource(value, name, store, null);
+ subdatasets.add(r);
+ }
+ }
+ }
+ }
+ } finally {
+ GdalOgr.GDAL().GDALClose(datasetPt);
+ }
+ this.subdatasets = Collections.unmodifiableList(subdatasets);
+ }
+ } catch (Throwable t) {
+ throw new DataStoreException(t);
+ }
+ }
+ return subdatasets;
+ }
+
+ private final class GridCoverageSelection extends GridCoverage {
+
+ final int[] subsampling;
+ final int[] bands;
+
+ private GridCoverageSelection(GridGeometry domain, List<? extends SampleDimension> ranges, int[] subsampling, int[] bands) {
+ super(domain, (bands == null || bands.length < 1) ? ranges : IntStream.of(bands).mapToObj(ranges::get).collect(Collectors.toList()));
+ this.subsampling = subsampling;
+ this.bands = bands;
+ }
+
+ @Override
+ public RenderedImage render(GridExtent sliceExtent) throws CannotEvaluateException {
+ final GridGeometry targetGeometry = getGridGeometry().derive().subgrid(sliceExtent).build();
+ try {
+ final GridExtent fullDomainIntersection = GdalResource.this.getGridGeometry().derive()
+ .subgrid(targetGeometry)
+ .getIntersection();
+
+ final GridCoverage coverage = readGridSlice(targetGeometry, getLow(fullDomainIntersection), getHigh(fullDomainIntersection), subsampling, bands);
+ return coverage.render(null);
+ } catch (DataStoreException e) {
+ throw new CannotEvaluateException("Error while loading slice", e);
+ }
+ }
+ }
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/coverage/GdalSampleDimension.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/coverage/GdalSampleDimension.java
new file mode 100644
index 0000000000..954b289e27
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/coverage/GdalSampleDimension.java
@@ -0,0 +1,37 @@
+package org.apache.sis.storage.gdal.coverage;
+
+import org.apache.sis.coverage.SampleDimension;
+import org.apache.sis.storage.gdal.internal.panama.GDALDataType;
+
+/**
+ * Representation of a SampleDimension with Gdal
+ * Add GdalDataType to the sample dimension
+ *
+ * @author Johann Sorel (Geomatys)
+ * @author Quentin BIALOTA (Geomatys)
+ */
+final class GdalSampleDimension extends SampleDimension {
+
+ /**
+ * Gdal data type
+ */
+ private final GDALDataType dataType;
+
+ /**
+ * Constructor
+ * @param base sample dimension
+ * @param dataType of the file loaded by gdal
+ */
+ public GdalSampleDimension(SampleDimension base, GDALDataType dataType) {
+ super(base.getName(), base.getBackground().orElse(null), base.getCategories());
+ this.dataType = dataType;
+ }
+
+ /**
+ * Get gdal data type
+ * @return GdalDataType associated with the sample dimension
+ */
+ public GDALDataType getGdalDataType() {
+ return dataType;
+ }
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/coverage/GdalStore.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/coverage/GdalStore.java
new file mode 100644
index 0000000000..43c55f17bc
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/coverage/GdalStore.java
@@ -0,0 +1,209 @@
+package org.apache.sis.storage.gdal.coverage;
+
+import org.apache.sis.io.stream.IOUtilities;
+import org.apache.sis.metadata.iso.DefaultIdentifier;
+import org.apache.sis.metadata.iso.DefaultMetadata;
+import org.apache.sis.metadata.iso.citation.DefaultCitation;
+import org.apache.sis.metadata.iso.identification.DefaultDataIdentification;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.referencing.NamedIdentifier;
+import org.apache.sis.storage.Aggregate;
+import org.apache.sis.storage.DataStore;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.Resource;
+import org.apache.sis.storage.base.ResourceOnFileSystem;
+import org.apache.sis.storage.gdal.internal.GdalOgr;
+import org.opengis.metadata.Metadata;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.util.GenericName;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * DataStore for GDAL library (uses gdal lib to load files / data)
+ *
+ * @author Quentin BIALOTA (Geomatys)
+ */
+public class GdalStore extends DataStore implements Aggregate, ResourceOnFileSystem {
+
+ /**
+ * List of resources in this data store
+ */
+ private final List<Resource> resources = new ArrayList<>();
+
+ /**
+ * List of searched drivers for the gdal lib
+ */
+ private String[] searchedDrivers = null;
+
+ /**
+ * Parameters of the data store
+ */
+ private final Parameters parameters;
+
+ /**
+ * Metadata of the data store
+ */
+ private final DefaultMetadata metadata;
+
+ /**
+ * Path of the data to load / stored in the data store
+ */
+ private final Path path;
+
+ /**
+ * Constructor
+ */
+ public GdalStore(final Path path) throws IOException, DataStoreException{
+ this(path.toUri());
+ }
+
+ /**
+ * Constructor
+ */
+ public GdalStore(final URI uri) throws IOException, DataStoreException{
+ this(toParameters(uri));
+ }
+
+ /**
+ * Transforms an URI into a ParameterValueGroup
+ * (Get the path of the URI and store it in PATH of the ParameterValueGroup
+ *
+ * @param uri URI of the file to transforms into parameters
+ * @return ParameterValueGroup
+ */
+ private static ParameterValueGroup toParameters(final URI uri) {
+ final ParameterValueGroup params = GdalProvider.PARAMETERS_DESCRIPTOR.createValue();
+ Parameters.castOrWrap(params).getOrCreate(GdalProvider.PATH).setValue(uri);
+ return params;
+ }
+
+ /**
+ * Constructor
+ */
+ public GdalStore(ParameterValueGroup params) throws DataStoreException {
+ parameters = Parameters.castOrWrap(params);
+ final URI uri = parameters.getValue(GdalProvider.PATH);
+ path = Paths.get(uri);
+ String searched = parameters.getValue(GdalProvider.SEARCHED_DRIVERS);
+ if (searched!=null) {
+ searched = searched.trim();
+ if (!searched.isEmpty()) searchedDrivers = searched.split(",|;");
+ }
+
+ Resource ref;
+ try {
+ ref = visit(path);
+ } catch (IOException ex) {
+ throw new DataStoreException(ex);
+ }
+ if (ref != null) resources.add(ref);
+
+ final String name = "dataset_"+ IOUtilities.filenameWithoutExtension(this.path.toString());
+ metadata = new DefaultMetadata();
+ final DefaultDataIdentification identification = new DefaultDataIdentification();
+ final NamedIdentifier identifier = new NamedIdentifier(new DefaultIdentifier(name));
+ final DefaultCitation citation = new DefaultCitation(name);
+ citation.setIdentifiers(Collections.singleton(identifier));
+ identification.setCitation(citation);
+ metadata.setIdentificationInfo(Collections.singleton(identification));
+ metadata.transitionTo(DefaultMetadata.State.FINAL);
+ }
+
+ @Override
+ public Collection<? extends Resource> components() throws DataStoreException {
+ return Collections.unmodifiableList(resources);
+ }
+
+ @Override
+ public Optional<GenericName> getIdentifier() {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional<ParameterValueGroup> getOpenParameters() {
+ return Optional.of(parameters);
+ }
+
+ @Override
+ public Metadata getMetadata() throws DataStoreException {
+ return metadata;
+ }
+
+ @Override
+ public void close() throws DataStoreException {
+ }
+
+ /**
+ * This method return the list of all files with the same name.
+ * It is not perfect but gdal does not provide any file list we could use.
+ *
+ * @return files used by this resource
+ * @throws DataStoreException
+ */
+ public Path[] getComponentFiles() throws DataStoreException {
+ final String extension = IOUtilities.extension(path);
+ if (!Files.isDirectory(path) && "adf".equalsIgnoreCase(extension)) {
+ //special case for ADF, we must take all *.adf in the folder and metadata.xml
+ final Path parent = path.getParent();
+ final DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() {
+ @Override
+ public boolean accept(Path entry) throws IOException {
+ String name = entry.getFileName().toString();
+ if ("metadata.xml".equalsIgnoreCase(name)) return true;
+ return "adf".equalsIgnoreCase(IOUtilities.extension(entry));
+ }
+ };
+
+ try (DirectoryStream<Path> stream = Files.newDirectoryStream(parent, filter)) {
+ final List<Path> paths = new ArrayList<>();
+ stream.forEach(paths::add);
+ paths.remove(path);
+ paths.addFirst(path);
+ return paths.toArray(new Path[0]);
+ } catch (IOException ex) {
+ throw new DataStoreException(ex.getMessage(), ex);
+ }
+ }
+
+ try {
+ return GdalOgr.listSiblings(path);
+ } catch (IOException ex) {
+ throw new DataStoreException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Search and create coverage references.
+ *
+ * @param path File to visit
+ * @return a GdalResource
+ * @throws DataStoreException
+ * @throws IOException
+ */
+ private Resource visit(Path path) throws DataStoreException, IOException {
+ final String name = path.getFileName().toString();
+ if (!name.startsWith(".")) {
+ final int index = name.lastIndexOf('.');
+ if (index >= 0) {
+ final String suffix = name.substring(index+1).toLowerCase();
+ if (GdalOgr.GDAL_EXTENSIONS.contains(suffix)) {
+ final GdalResource ref = new GdalResource(path.toUri(),this);
+ ref.setSearchedDrivers(searchedDrivers);
+ return ref;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/GdalImpl.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/GdalImpl.java
new file mode 100644
index 0000000000..cb901e88c1
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/GdalImpl.java
@@ -0,0 +1,1428 @@
+package org.apache.sis.storage.gdal.internal;
+
+import org.apache.sis.storage.gdal.internal.panama.*;
+
+import java.lang.foreign.Arena;
+import java.lang.foreign.FunctionDescriptor;
+import java.lang.foreign.Linker;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.ValueLayout;
+import java.lang.invoke.MethodHandle;
+
+import static org.apache.sis.storage.gdal.internal.panama.PanamaUtils.lookup;
+
+/**
+ * Implementation of the GDAL library using Java Panama.
+ *
+ * @author Quentin BIALOTA (Geomatys)
+ */
+public class GdalImpl implements Gdal {
+
+ @Override
+ public int GDALGetDataTypeSize(GDALDataType eDataType) {
+ return 0;
+ }
+
+ @Override
+ public int GDALGetDataTypeSizeBits(GDALDataType eDataType) {
+ return 0;
+ }
+
+ @Override
+ public int GDALGetDataTypeSizeBytes(GDALDataType eDataType) {
+ return 0;
+ }
+
+ @Override
+ public int GDALDataTypeIsComplex(GDALDataType eDataType) {
+ return 0;
+ }
+
+ @Override
+ public int GDALDataTypeIsInteger(GDALDataType eDataType) {
+ return 0;
+ }
+
+ @Override
+ public int GDALDataTypeIsFloating(GDALDataType eDataType) {
+ return 0;
+ }
+
+ @Override
+ public int GDALDataTypeIsSigned(GDALDataType eDataType) {
+ return 0;
+ }
+
+ @Override
+ public String GDALGetDataTypeName(GDALDataType eDataType) {
+ return "";
+ }
+
+ @Override
+ public GDALDataType GDALGetDataTypeByName(String pszName) {
+ return null;
+ }
+
+ @Override
+ public GDALDataType GDALDataTypeUnion(GDALDataType eType1, GDALDataType eType2) {
+ return null;
+ }
+
+ @Override
+ public GDALDataType GDALDataTypeUnionWithValue(GDALDataType eDT, double dValue, int bComplex) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public GDALDataType GDALFindDataType(int nBits, int bSigned, int bFloating, int bComplex) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public GDALDataType GDALFindDataTypeForValue(double dValue, int bComplex) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public double GDALAdjustValueToDataType(GDALDataType eDT, double dfValue, MemorySegment pbClamped, MemorySegment pbRounded) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public GDALDataType GDALGetNonComplexDataType(GDALDataType eDataType) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public int GDALDataTypeIsConversionLossy(GDALDataType eTypeFrom, GDALDataType eTypeTo) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public String GDALGetAsyncStatusTypeName(GDALAsyncStatusType eAsyncStatusType) throws Throwable {
+ return "";
+ }
+
+ @Override
+ public GDALAsyncStatusType GDALGetAsyncStatusTypeByName(String pszName) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public String GDALGetColorInterpretationName(GDALColorInterp eInterp) throws Throwable {
+ return "";
+ }
+
+ @Override
+ public GDALColorInterp GDALGetColorInterpretationByName(String pszName) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public String GDALGetPaletteInterpretationName(GDALPaletteInterp eInterp) throws Throwable {
+ return "";
+ }
+
+ private static MethodHandle GDALAllRegister = Linker.nativeLinker().downcallHandle(
+ lookup("GDALAllRegister"),
+ FunctionDescriptor.ofVoid());
+
+ @Override
+ public void GDALAllRegister() throws Throwable {
+ GDALAllRegister.invokeExact();
+ }
+
+ @Override
+ public MemorySegment GDALCreate(MemorySegment hDriver, String pszFilename, int nXSize, int nYSize, int nBands, GDALDataType eBandType, MemorySegment papszOptions) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALCreateCopy(MemorySegment hDriver, String pszFilename, MemorySegment hSrcDS, int bStrict, MemorySegment papszOptions, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALIdentifyDriver(String pszFilename, MemorySegment papszFileList) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALIdentifyDriverEx(String pszFilename, int nIdentifyFlags, MemorySegment papszAllowedDrivers, MemorySegment papszFileList) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALOpen(String pszFilename, GDALAccess eAccess) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALOpenShared(String pszFilename, GDALAccess eAccess) throws Throwable {
+ return null;
+ }
+
+ private static MethodHandle GDALOpenEx = Linker.nativeLinker().downcallHandle(
+ lookup("GDALOpenEx"),
+ FunctionDescriptor.of(ValueLayout.ADDRESS,
+ ValueLayout.ADDRESS,
+ ValueLayout.JAVA_INT,
+ ValueLayout.ADDRESS,
+ ValueLayout.ADDRESS,
+ ValueLayout.ADDRESS));
+
+ @Override
+ public MemorySegment GDALOpenEx(String pszFilename, int nOpenFlags, String[] papszAllowedDrivers, String[] papszOpenOptions, String[] papszSiblingFiles) throws Throwable {
+ try (var arena = Arena.ofConfined()) {
+
+ if (papszAllowedDrivers == null) {
+ papszAllowedDrivers = new String[0];
+ }
+ MemorySegment allowedDrivers = PanamaUtils.allocateStringArray(papszAllowedDrivers, arena);
+
+ /////////////////////////////////////
+
+ if (papszOpenOptions == null) {
+ papszOpenOptions = new String[0];
+ }
+ MemorySegment openOptions = PanamaUtils.allocateStringArray(papszOpenOptions, arena);
+
+ /////////////////////////////////////
+
+ if (papszSiblingFiles == null) {
+ papszSiblingFiles = new String[0];
+ }
+ MemorySegment siblingFiles = PanamaUtils.allocateStringArray(papszSiblingFiles, arena);
+
+ return (MemorySegment) GDALOpenEx.invokeExact(
+ arena.allocateFrom(pszFilename),
+ nOpenFlags,
+ allowedDrivers,
+ openOptions,
+ siblingFiles
+ );
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+
+ @Override
+ public int GDALDumpOpenDatasets(MemorySegment fp) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public MemorySegment GDALGetDriverByName(String name) throws Throwable {
+ return null;
+ }
+
+ private static MethodHandle GDALGetDriverCount = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetDriverCount"),
+ FunctionDescriptor.of(ValueLayout.JAVA_INT));
+
+ @Override
+ public int GDALGetDriverCount() throws Throwable {
+ return (int) GDALGetDriverCount.invokeExact();
+ }
+
+ private static MethodHandle GDALGetDriver = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetDriver"),
+ FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.JAVA_INT));
+
+ @Override
+ public MemorySegment GDALGetDriver(int iDriver) throws Throwable {
+ return (MemorySegment) GDALGetDriver.invokeExact(iDriver);
+ }
+
+ @Override
+ public MemorySegment GDALCreateDriver() throws Throwable {
+ return null;
+ }
+
+ @Override
+ public void GDALDestroyDriver(MemorySegment hDriver) throws Throwable {
+
+ }
+
+ @Override
+ public int GDALRegisterDriver(MemorySegment hDriver) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public void GDALDeregisterDriver(MemorySegment hDriver) throws Throwable {
+
+ }
+
+ @Override
+ public void GDALDestroyDriverManager() throws Throwable {
+
+ }
+
+ @Override
+ public CPLErr GDALDeleteDataset(MemorySegment hDriver, String pszFilename) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALRenameDataset(MemorySegment hDriver, String pszNewName, String pszOldName) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALCopyDatasetFiles(MemorySegment hDriver, String pszNewName, String pszOldName) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public int GDALValidateCreationOptions(MemorySegment hDriver, MemorySegment papszCreationOptions) throws Throwable {
+ return 0;
+ }
+
+ private static MethodHandle GDALGetDriverShortName = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetDriverShortName"),
+ FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS));
+
+ @Override
+ public String GDALGetDriverShortName(MemorySegment hDriver) throws Throwable {
+ MemorySegment resultSegment = (MemorySegment) GDALGetDriverShortName.invokeExact(hDriver);
+
+ resultSegment = resultSegment.reinterpret(Long.MAX_VALUE); //TODO : Remove if we can find another way to read Strings
+
+ if (resultSegment == MemorySegment.NULL || resultSegment.byteSize() == 0) {
+ return null;
+ }
+
+ try {
+ return resultSegment.getString(0);
+ } catch (IndexOutOfBoundsException e) {
+ return null;
+ }
+ }
+
+ private static MethodHandle GDALGetDriverLongName = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetDriverLongName"),
+ FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS));
+
+ @Override
+ public String GDALGetDriverLongName(MemorySegment hDriver) throws Throwable {
+ MemorySegment resultSegment = (MemorySegment) GDALGetDriverLongName.invokeExact(hDriver);
+
+ resultSegment = resultSegment.reinterpret(Long.MAX_VALUE); //TODO : Remove if we can find another way to read Strings
+
+ if (resultSegment == MemorySegment.NULL || resultSegment.byteSize() == 0) {
+ return null;
+ }
+
+ try {
+ return resultSegment.getString(0);
+ } catch (IndexOutOfBoundsException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public String GDALGetDriverHelpTopic(MemorySegment hDriver) throws Throwable {
+ return "";
+ }
+
+ @Override
+ public String GDALGetDriverCreationOptionList(MemorySegment hDriver) throws Throwable {
+ return "";
+ }
+
+ @Override
+ public void GDALInitGCPs(int nCount, MemorySegment psGCP) throws Throwable {
+
+ }
+
+ @Override
+ public void GDALDeinitGCPs(int nCount, MemorySegment psGCP) throws Throwable {
+
+ }
+
+ @Override
+ public MemorySegment GDALDuplicateGCPs(int nCount, MemorySegment pasGCPList) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public int GDALGCPsToGeoTransform(int nGCPCount, MemorySegment pasGCPs, MemorySegment padfGeoTransform, int bApproxOK) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public int GDALInvGeoTransform(MemorySegment padfGeoTransformIn, MemorySegment padfInvGeoTransformOut) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public void GDALApplyGeoTransform(MemorySegment padfGeoTransform, double dfPixel, double dfLine, MemorySegment pdfGeoX, MemorySegment pdfGeoY) throws Throwable {
+
+ }
+
+ @Override
+ public void GDALComposeGeoTransforms(MemorySegment padfGeoTransform1, MemorySegment padfGeoTransform2, MemorySegment padfGeoTransformOut) throws Throwable {
+
+ }
+
+ @Override
+ public MemorySegment GDALGetMetadataDomainList(MemorySegment hObject) throws Throwable {
+ return null;
+ }
+
+ private static MethodHandle GDALGetMetadata = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetMetadata"),
+ FunctionDescriptor.of(ValueLayout.ADDRESS,
+ ValueLayout.ADDRESS, ValueLayout.ADDRESS));
+
+ @Override
+ public MemorySegment GDALGetMetadata(MemorySegment hObject, String pszDomain) throws Throwable {
+ try (var arena = Arena.ofConfined()) {
+ return (MemorySegment) GDALGetMetadata.invokeExact(
+ hObject,
+ arena.allocateFrom(pszDomain)
+ );
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+
+ @Override
+ public CPLErr GDALSetMetadata(MemorySegment hObject, MemorySegment papszMD, String pszDomain) throws Throwable {
+ return null;
+ }
+
+ private static MethodHandle GDALGetMetadataItem = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetMetadataItem"),
+ FunctionDescriptor.of(ValueLayout.ADDRESS,
+ ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.ADDRESS));
+
+ @Override
+ public String GDALGetMetadataItem(MemorySegment hObject, String pszName, String pszDomain) throws Throwable {
+ try (var arena = Arena.ofConfined()) {
+ MemorySegment nameSegment = (pszName == null) ? MemorySegment.NULL : arena.allocateFrom(pszName);
+ MemorySegment domainSegment = (pszDomain == null) ? MemorySegment.NULL : arena.allocateFrom(pszDomain);
+
+ MemorySegment resultSegment = (MemorySegment) GDALGetMetadataItem.invokeExact(hObject, nameSegment, domainSegment);
+
+ resultSegment = resultSegment.reinterpret(Long.MAX_VALUE); //TODO : Remove if we can find another way to read Strings
+
+ if (resultSegment == MemorySegment.NULL || resultSegment.byteSize() == 0 || resultSegment.address() == 0) {
+ return null;
+ }
+
+ try {
+ return resultSegment.getString(0);
+ } catch (IndexOutOfBoundsException e) {
+ return null;
+ }
+
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+
+ @Override
+ public CPLErr GDALSetMetadataItem(MemorySegment hObject, String pszName, String pszValue, String pszDomain) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public String GDALGetDescription(MemorySegment hObject) throws Throwable {
+ return "";
+ }
+
+ @Override
+ public void GDALSetDescription(MemorySegment hObject, String pszNewDesc) throws Throwable {
+
+ }
+
+ @Override
+ public MemorySegment GDALGetDatasetDriver(MemorySegment hDataset) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALGetFileList(MemorySegment hDS) throws Throwable {
+ return null;
+ }
+
+ private static MethodHandle GDALClose = Linker.nativeLinker().downcallHandle(
+ lookup("GDALClose"),
+ FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS));
+
+ @Override
+ public CPLErr GDALClose(MemorySegment hDS) throws Throwable {
+ int errCode = (int) GDALClose.invoke(hDS);
+
+ return CPLErr.getByValue(errCode);
+ }
+
+ private static MethodHandle GDALGetRasterXSize = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetRasterXSize"),
+ FunctionDescriptor.of(ValueLayout.JAVA_INT,
+ ValueLayout.ADDRESS));
+
+ @Override
+ public int GDALGetRasterXSize(MemorySegment hDataset) throws Throwable {
+ return (int) GDALGetRasterXSize.invokeExact(hDataset);
+ }
+
+ private static MethodHandle GDALGetRasterYSize = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetRasterYSize"),
+ FunctionDescriptor.of(ValueLayout.JAVA_INT,
+ ValueLayout.ADDRESS));
+
+ @Override
+ public int GDALGetRasterYSize(MemorySegment hDataset) throws Throwable {
+ return (int) GDALGetRasterYSize.invokeExact(hDataset);
+ }
+
+ private static MethodHandle GDALGetRasterCount = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetRasterCount"),
+ FunctionDescriptor.of(ValueLayout.JAVA_INT,
+ ValueLayout.ADDRESS));
+
+ @Override
+ public int GDALGetRasterCount(MemorySegment hDataset) throws Throwable {
+ return (int) GDALGetRasterCount.invokeExact(hDataset);
+ }
+
+ private static MethodHandle GDALGetRasterBand = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetRasterBand"),
+ FunctionDescriptor.of(ValueLayout.ADDRESS,
+ ValueLayout.ADDRESS, ValueLayout.JAVA_INT));
+
+ @Override
+ public MemorySegment GDALGetRasterBand(MemorySegment hDataset, int nBand) throws Throwable {
+ return (MemorySegment) GDALGetRasterBand.invokeExact(hDataset, nBand);
+ }
+
+ @Override
+ public CPLErr GDALAddBand(MemorySegment hDS, GDALDataType eType, MemorySegment papszOptions) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALBeginAsyncReader(MemorySegment hDS, int nXOff, int nYOff, int nXSize, int nYSize, MemorySegment pBuf, int nBufXSize, int nBufYSize, GDALDataType eBufType, int nBandCount, MemorySegment panBandMap, int nPixelSpace, int nLineSpace, int nBandSpace, MemorySegment papszOptions) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public void GDALEndAsyncReader(MemorySegment hDS, MemorySegment hAsynchReaderH) throws Throwable {
+
+ }
+
+ @Override
+ public CPLErr GDALDatasetRasterIO(MemorySegment hDS, GDALRWFlag eRWFlag, int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, MemorySegment pBuffer, int nBXSize, int nBYSize, GDALDataType eBDataType, int nBandCount, MemorySegment panBandCount, int nPixelSpace, int nLineSpace, int nBandSpace) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALDatasetRasterIOEx(MemorySegment hDS, GDALRWFlag eRWFlag, int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, MemorySegment pBuffer, int nBXSize, int nBYSize, GDALDataType eBDataType, int nBandCount, MemorySegment panBandCount, long nPixelSpace, long nLineSpace, long nBandSpace, MemorySegment psExtraArg) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALDatasetAdviseRead(MemorySegment hDS, int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, int nBXSize, int nBYSize, GDALDataType eBDataType, int nBandCount, MemorySegment panBandCount, MemorySegment papszOptions) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALDatasetGetCompressionFormats(MemorySegment hDS, int nXOff, int nYOff, int nXSize, int nYSize, int nBandCount, MemorySegment panBandList) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALDatasetReadCompressedData(MemorySegment hDS, String pszFormat, int nXOff, int nYOff, int nXSize, int nYSize, int nBandCount, MemorySegment panBandList, MemorySegment ppBuffer, MemorySegment pnBufferSize, MemorySegment ppszDetailedFormat) throws Throwable {
+ return null;
+ }
+
+ private static MethodHandle GDALGetProjectionRef = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetProjectionRef"),
+ FunctionDescriptor.of(ValueLayout.ADDRESS,
+ ValueLayout.ADDRESS));
+
+ @Override
+ public String GDALGetProjectionRef(MemorySegment hDS) throws Throwable {
+ MemorySegment stringOuput = (MemorySegment) GDALGetProjectionRef.invokeExact(hDS);
+
+ stringOuput = stringOuput.reinterpret(Long.MAX_VALUE); //TODO : Remove if we can find another way to read Strings
+
+ if (stringOuput != MemorySegment.NULL && stringOuput.byteSize() > 0) {
+ return stringOuput.getString(0);
+ }
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALGetSpatialRef(MemorySegment hDS) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALSetProjection(MemorySegment hDS, String pszProjection) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALSetSpatialRef(MemorySegment hDS, MemorySegment hSRS) throws Throwable {
+ return null;
+ }
+
+ private static MethodHandle GDALGetGeoTransform = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetGeoTransform"),
+ FunctionDescriptor.of(ValueLayout.JAVA_INT,
+ ValueLayout.ADDRESS, ValueLayout.ADDRESS));
+
+ @Override
+ public CPLErr GDALGetGeoTransform(MemorySegment hDS, MemorySegment padfGeoTransform) throws Throwable {
+ int errCode = (int) GDALGetGeoTransform.invokeExact(hDS, padfGeoTransform);
+
+ return CPLErr.getByValue(errCode);
+ }
+
+ @Override
+ public CPLErr GDALSetGeoTransform(MemorySegment hDS, MemorySegment padfGeoTransform) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public int GDALGetGCPCount(MemorySegment hDS) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public String GDALGetGCPProjection(MemorySegment hDS) throws Throwable {
+ return "";
+ }
+
+ @Override
+ public MemorySegment GDALGetGCPSpatialRef(MemorySegment hDS) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALGetGCPs(MemorySegment hDS) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALSetGCPs(MemorySegment hDS, int nGCPCount, MemorySegment pasGCPs, String pszGCPProjection) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALSetGCPs2(MemorySegment hDS, int nGCPCount, MemorySegment pasGCPs, MemorySegment hSRS) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALGetInternalHandle(MemorySegment hDS, String pszHandleName) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public int GDALReferenceDataset(MemorySegment hDS) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public int GDALDereferenceDataset(MemorySegment hDS) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public int GDALReleaseDataset(MemorySegment hDS) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public CPLErr GDALBuildOverviews(MemorySegment hDS, String pszResampling, int nOverviews, MemorySegment panOverviewList, int nListBands, MemorySegment panBandList, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALBuildOverviewsEx(MemorySegment hDS, String pszResampling, int nOverviews, MemorySegment panOverviewList, int nListBands, MemorySegment panBandList, MemorySegment pfnProgress, MemorySegment pProgressData, MemorySegment papszOptions) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public void GDALGetOpenDatasets(MemorySegment phDS, MemorySegment pnCount) throws Throwable {
+
+ }
+
+ @Override
+ public int GDALGetAccess(MemorySegment hDS) throws Throwable {
+ return 0;
+ }
+
+ private static MethodHandle GDALFlushCache = Linker.nativeLinker().downcallHandle(
+ lookup("GDALFlushCache"),
+ FunctionDescriptor.of(ValueLayout.JAVA_INT,
+ ValueLayout.ADDRESS));
+
+ @Override
+ public CPLErr GDALFlushCache(MemorySegment hDS) throws Throwable {
+ int errCode = (int) GDALFlushCache.invokeExact(hDS);
+
+ return CPLErr.getByValue(errCode);
+ }
+
+ @Override
+ public CPLErr GDALCreateDatasetMaskBand(MemorySegment hDS, int nFlags) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALDatasetCopyWholeRaster(MemorySegment hSrcDS, MemorySegment hDstDS, MemorySegment papszOptions, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALRasterBandCopyWholeRaster(MemorySegment hSrcBand, MemorySegment hDstBand, MemorySegment papszOptions, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALRegenerateOverviews(MemorySegment hSrcBand, int nOverviewCount, MemorySegment pahOverviewBands, String pszResampling, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALRegenerateOverviewsEx(MemorySegment hSrcBand, int nOverviewCount, MemorySegment pahOverviewBands, String pszResampling, MemorySegment pfnProgress, MemorySegment pProgressData, MemorySegment papszOptions) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public int GDALDatasetGetLayerCount(MemorySegment hDS) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public MemorySegment GDALDatasetGetLayer(MemorySegment hDS, int iLayer) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALDatasetGetLayerByName(MemorySegment hDS, String pszName) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public int GDALDatasetIsLayerPrivate(MemorySegment hDS, int iLayer) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public OGRErr GDALDatasetDeleteLayer(MemorySegment hDS, int iLayer) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALDatasetCreateLayer(MemorySegment hDS, String pszName, MemorySegment hSRS, MemorySegment eType, MemorySegment papszOptions) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALDatasetCopyLayer(MemorySegment hDS, MemorySegment hSrcLayer, String pszNewName, MemorySegment papszOptions) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public void GDALDatasetResetReading(MemorySegment hDS) throws Throwable {
+
+ }
+
+ @Override
+ public MemorySegment GDALDatasetGetNextFeature(MemorySegment hDS, MemorySegment phBelongingLayer, MemorySegment pdfProgressPct, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public int GDALDatasetTestCapability(MemorySegment hDS, String pszCapability) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public MemorySegment GDALDatasetExecuteSQL(MemorySegment hDS, String pszSQL, MemorySegment hSpatialFilter, String pszDialect) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public OGRErr GDALDatasetAbortSQL(MemorySegment hDS) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public void GDALDatasetReleaseResultSet(MemorySegment hDS, MemorySegment hLayer) throws Throwable {
+
+ }
+
+ @Override
+ public MemorySegment GDALDatasetGetStyleTable(MemorySegment hDS) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public void GDALDatasetSetStyleTableDirectly(MemorySegment hDS, MemorySegment hStyleTable) throws Throwable {
+
+ }
+
+ @Override
+ public void GDALDatasetSetStyleTable(MemorySegment hDS, MemorySegment hStyleTable) throws Throwable {
+
+ }
+
+ @Override
+ public OGRErr GDALDatasetStartTransaction(MemorySegment hDS, int bForce) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public OGRErr GDALDatasetCommitTransaction(MemorySegment hDS) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public OGRErr GDALDatasetRollbackTransaction(MemorySegment hDS) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public void GDALDatasetClearStatistics(MemorySegment hDS) throws Throwable {
+
+ }
+
+ @Override
+ public MemorySegment GDALDatasetGetFieldDomainNames(MemorySegment hDS, MemorySegment papszOptions) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALDatasetGetFieldDomain(MemorySegment hDS, String pszName) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public boolean GDALDatasetAddFieldDomain(MemorySegment hDS, MemorySegment hFieldDomain, MemorySegment ppszFailureReason) throws Throwable {
+ return false;
+ }
+
+ @Override
+ public boolean GDALDatasetDeleteFieldDomain(MemorySegment hDS, String pszName, MemorySegment ppszFailureReason) throws Throwable {
+ return false;
+ }
+
+ @Override
+ public boolean GDALDatasetUpdateFieldDomain(MemorySegment hDS, MemorySegment hFieldDomain, MemorySegment ppszFailureReason) throws Throwable {
+ return false;
+ }
+
+ @Override
+ public MemorySegment GDALDatasetGetRelationshipNames(MemorySegment hDS, MemorySegment papszOptions) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALDatasetGetRelationship(MemorySegment hDS, String pszName) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public boolean GDALDatasetAddRelationship(MemorySegment hDS, MemorySegment hRelationship, MemorySegment ppszFailureReason) throws Throwable {
+ return false;
+ }
+
+ @Override
+ public boolean GDALDatasetDeleteRelationship(MemorySegment hDS, String pszName, MemorySegment ppszFailureReason) throws Throwable {
+ return false;
+ }
+
+ @Override
+ public boolean GDALDatasetUpdateRelationship(MemorySegment hDS, MemorySegment hRelationship, MemorySegment ppszFailureReason) throws Throwable {
+ return false;
+ }
+
+ private static MethodHandle GDALGetRasterDataType = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetRasterDataType"),
+ FunctionDescriptor.of(ValueLayout.JAVA_INT,
+ ValueLayout.ADDRESS));
+
+ @Override
+ public GDALDataType GDALGetRasterDataType(MemorySegment hRBand) throws Throwable {
+ int dataType = (int) GDALGetRasterDataType.invokeExact(hRBand);
+ return GDALDataType.fromValue(dataType);
+ }
+
+ @Override
+ public void GDALGetBlockSize(MemorySegment hRBand, MemorySegment pnXSize, MemorySegment pnYSize) throws Throwable {
+
+ }
+
+ @Override
+ public CPLErr GDALGetActualBlockSize(MemorySegment hRBand, int nXBlockOff, int nYBlockOff, MemorySegment pnXValid, MemorySegment pnYValid) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALRasterAdviseRead(MemorySegment hRB, int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, int nBXSize, int nBYSize, GDALDataType eBDataType, MemorySegment papszOptions) throws Throwable {
+ return null;
+ }
+
+ private static MethodHandle GDALRasterIO = Linker.nativeLinker().downcallHandle(
+ lookup("GDALRasterIO"),
+ FunctionDescriptor.of(ValueLayout.JAVA_INT,
+ ValueLayout.ADDRESS,
+ ValueLayout.JAVA_INT,
+ ValueLayout.JAVA_INT,
+ ValueLayout.JAVA_INT,
+ ValueLayout.JAVA_INT,
+ ValueLayout.JAVA_INT,
+ ValueLayout.ADDRESS,
+ ValueLayout.JAVA_INT,
+ ValueLayout.JAVA_INT,
+ ValueLayout.JAVA_INT,
+ ValueLayout.JAVA_INT,
+ ValueLayout.JAVA_INT
+ ));
+
+ @Override
+ public CPLErr GDALRasterIO(MemorySegment hRBand, GDALRWFlag eRWFlag, int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize,
+ MemorySegment pBuffer, int nBXSize, int nBYSize, GDALDataType eBDataType, int nPixelSpace, int nLineSpace)
+ throws Throwable {
+
+ int errCode = (int) GDALRasterIO.invokeExact(hRBand, eRWFlag.value(), nDSXOff, nDSYOff, nBXSize, nDSYSize,
+ pBuffer, nBXSize, nBYSize, eBDataType.getValue(), nPixelSpace, nLineSpace);
+
+ return CPLErr.getByValue(errCode);
+ }
+
+ @Override
+ public CPLErr GDALRasterIOEx(MemorySegment hRBand, GDALRWFlag eRWFlag, int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, MemorySegment pBuffer, int nBXSize, int nBYSize, GDALDataType eBDataType, long nPixelSpace, long nLineSpace, MemorySegment psExtraArg) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALReadBlock(MemorySegment hRBand, int nBlockXOff, int nBlockYOff, MemorySegment pData) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALWriteBlock(MemorySegment hRBand, int nBlockXOff, int nBlockYOff, MemorySegment pData) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public int GDALGetRasterBandXSize(MemorySegment hRBand) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public int GDALGetRasterBandYSize(MemorySegment hRBand) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public GDALAccess GDALGetRasterAccess(MemorySegment hRBand) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public int GDALGetBandNumber(MemorySegment hRBand) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public MemorySegment GDALGetBandDataset(MemorySegment hRBand) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public GDALColorInterp GDALGetRasterColorInterpretation(MemorySegment hRBand) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALSetRasterColorInterpretation(MemorySegment hRBand, GDALColorInterp eColorInterp) throws Throwable {
+ return null;
+ }
+
+ private static MethodHandle GDALGetRasterColorTable = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetRasterColorTable"),
+ FunctionDescriptor.of(ValueLayout.ADDRESS,
+ ValueLayout.ADDRESS));
+
+ @Override
+ public MemorySegment GDALGetRasterColorTable(MemorySegment hRBand) throws Throwable {
+ return (MemorySegment) GDALGetRasterColorTable.invokeExact(hRBand);
+ }
+
+ @Override
+ public CPLErr GDALSetRasterColorTable(MemorySegment hRBand, MemorySegment hColorTable) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public int GDALHasArbitraryOverviews(MemorySegment hRBand) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public int GDALGetOverviewCount(MemorySegment hRBand) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public MemorySegment GDALGetOverview(MemorySegment hRBand, int iOverview) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public double GDALGetRasterNoDataValue(MemorySegment hRBand, MemorySegment pbSuccess) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public long GDALGetRasterNoDataValueAsInt64(MemorySegment hRBand, MemorySegment pbSuccess) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public long GDALGetRasterNoDataValueAsUInt64(MemorySegment hRBand, MemorySegment pbSuccess) throws Throwable {
+ return 0;
+ }
+
+ @Override
+ public CPLErr GDALSetRasterNoDataValue(MemorySegment hRBand, double dfNoDataValue) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALSetRasterNoDataValueAsInt64(MemorySegment hRBand, long nNoDataValue) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALSetRasterNoDataValueAsUInt64(MemorySegment hRBand, long nNoDataValue) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALDeleteRasterNoDataValue(MemorySegment hRBand) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALGetRasterCategoryNames(MemorySegment hRBand) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALSetRasterCategoryNames(MemorySegment hRBand, MemorySegment papszCategoryNames) throws Throwable {
+ return null;
+ }
+
+ private static MethodHandle GDALGetRasterMinimum = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetRasterMinimum"),
+ FunctionDescriptor.of(ValueLayout.JAVA_DOUBLE,
+ ValueLayout.ADDRESS, ValueLayout.ADDRESS));
+
+ @Override
+ public double GDALGetRasterMinimum(MemorySegment hRBand, MemorySegment pbSuccess) throws Throwable {
+ return (double) GDALGetRasterMinimum.invokeExact(hRBand, pbSuccess);
+ }
+
+ private static MethodHandle GDALGetRasterMaximum = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetRasterMaximum"),
+ FunctionDescriptor.of(ValueLayout.JAVA_DOUBLE,
+ ValueLayout.ADDRESS, ValueLayout.ADDRESS));
+
+ @Override
+ public double GDALGetRasterMaximum(MemorySegment hRBand, MemorySegment pbSuccess) throws Throwable {
+ return (double) GDALGetRasterMaximum.invokeExact(hRBand, pbSuccess);
+ }
+
+ @Override
+ public CPLErr GDALGetRasterStatistics(MemorySegment hRBand, int bApproxOK, int bForce, MemorySegment pdfMin, MemorySegment pdfMax, MemorySegment pdfMean, MemorySegment pdfStdDev) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALComputeRasterStatistics(MemorySegment hRBand, int bApproxOK, MemorySegment pdfMin, MemorySegment pdfMax, MemorySegment pdfMean, MemorySegment pdfStdDev, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALSetRasterStatistics(MemorySegment hBand, double dfMin, double dfMax, double dfMean, double dfStdDev) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public MemorySegment GDALRasterBandAsMDArray(MemorySegment hRBand) throws Throwable {
+ return null;
+ }
+
+ private static MethodHandle GDALGetRasterUnitType = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetRasterUnitType"),
+ FunctionDescriptor.of(ValueLayout.ADDRESS,
+ ValueLayout.ADDRESS));
+
+ @Override
+ public String GDALGetRasterUnitType(MemorySegment hRBand) throws Throwable {
+ MemorySegment stringOuput = (MemorySegment) GDALGetRasterUnitType.invokeExact(hRBand);
+
+ stringOuput = stringOuput.reinterpret(Long.MAX_VALUE); //TODO : Remove if we can find another way to read Strings
+
+ if (stringOuput != MemorySegment.NULL && stringOuput.byteSize() > 0) {
+ return stringOuput.getString(0);
+ }
+
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALSetRasterUnitType(MemorySegment hBand, String pszNewValue) throws Throwable {
+ return null;
+ }
+
+ private static MethodHandle GDALGetRasterOffset = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetRasterOffset"),
+ FunctionDescriptor.of(ValueLayout.JAVA_DOUBLE,
+ ValueLayout.ADDRESS, ValueLayout.ADDRESS));
+
+ @Override
+ public double GDALGetRasterOffset(MemorySegment hRBand, MemorySegment pbSuccess) throws Throwable {
+ return (double) GDALGetRasterOffset.invokeExact(hRBand, pbSuccess);
+ }
+
+ @Override
+ public CPLErr GDALSetRasterOffset(MemorySegment hBand, double dfNewOffset) throws Throwable {
+ return null;
+ }
+
+ private static MethodHandle GDALGetRasterScale = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetRasterScale"),
+ FunctionDescriptor.of(ValueLayout.JAVA_DOUBLE,
+ ValueLayout.ADDRESS, ValueLayout.ADDRESS));
+
+ @Override
+ public double GDALGetRasterScale(MemorySegment hRBand, MemorySegment pbSuccess) throws Throwable {
+ return (double) GDALGetRasterScale.invokeExact(hRBand, pbSuccess);
+ }
+
+ @Override
+ public CPLErr GDALSetRasterScale(MemorySegment hBand, double dfNewOffset) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALComputeRasterMinMax(MemorySegment hBand, int bApproxOK, MemorySegment adfMinMax) throws Throwable {
+ return null;
+ }
+
+ private static MethodHandle GDALFlushRasterCache = Linker.nativeLinker().downcallHandle(
+ lookup("GDALFlushRasterCache"),
+ FunctionDescriptor.of(ValueLayout.JAVA_INT,
+ ValueLayout.ADDRESS));
+
+ @Override
+ public CPLErr GDALFlushRasterCache(MemorySegment hBand) throws Throwable {
+ int errCode = (int) GDALFlushRasterCache.invokeExact(hBand);
+
+ return CPLErr.getByValue(errCode);
+ }
+
+ @Override
+ public CPLErr GDALGetRasterHistogram(MemorySegment hBand, double dfMin, double dfMax, int nBuckets, MemorySegment panHistogram, int bIncludeOutOfRange, int bApproxOK, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALGetRasterHistogramEx(MemorySegment hBand, double dfMin, double dfMax, int nBuckets, MemorySegment panHistogram, int bIncludeOutOfRange, int bApproxOK, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALGetDefaultHistogramEx(MemorySegment hBand, MemorySegment pdfMin, MemorySegment pdfMax, MemorySegment pnBuckets, MemorySegment ppanHistogram, int bForce, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public CPLErr GDALSetDefaultHistogramEx(MemorySegment hBand, double dfMin, double dfMax, int nBuckets, MemorySegment panHistogram) throws Throwable {
+ return null;
+ }
+
+ @Override
+ public long GDALGetRasterSampleOverview(long hBand, int iOverview) {
+ return 0;
+ }
+
+ @Override
+ public long GDALGetRasterSampleOverviewEx(long hBand, long nOverviewIndex) {
+ return 0;
+ }
+
+ @Override
+ public int GDALFillRaster(long hBand, double dfRealValue, double dfImaginaryValue) {
+ return 0;
+ }
+
+ @Override
+ public int GDALComputeBandStats(long hBand, int nSampleStep, double[] pdfMean, double[] pdfStdDev, MemorySegment pfnProgress, long pProgressData) {
+ return 0;
+ }
+
+ @Override
+ public int GDALOverviewMagnitudeCorrection(long hBaseBand, int nOverviewCount, long[] pahOverviews, MemorySegment pfnProgress, long pProgressData) {
+ return 0;
+ }
+
+ @Override
+ public long GDALGetDefaultRAT(long hBand) {
+ return 0;
+ }
+
+ @Override
+ public int GDALSetDefaultRAT(long hBand, long hRAT) {
+ return 0;
+ }
+
+ @Override
+ public int GDALAddDerivedBandPixelFunc(String pszName, MemorySegment pfnPixelFunc) {
+ return 0;
+ }
+
+ @Override
+ public int GDALAddDerivedBandPixelFuncWithArgs(String pszName, MemorySegment pfnPixelFunc, String pszMetadata) {
+ return 0;
+ }
+
+ @Override
+ public long GDALGetMaskBand(long hBand) {
+ return 0;
+ }
+
+ @Override
+ public int GDALGetMaskFlags(long hBand) {
+ return 0;
+ }
+
+ @Override
+ public int GDALCreateMaskBand(long hBand, int nFlags) {
+ return 0;
+ }
+
+ @Override
+ public boolean GDALIsMaskBand(long hBand) {
+ return false;
+ }
+
+ @Override
+ public int GDALGetDataCoverageStatus(long hBand, int nXOff, int nYOff, int nXSize, int nYSize, int nMaskFlagStop, double[] pdfDataPct) {
+ return 0;
+ }
+
+ @Override
+ public int GDALARGetNextUpdatedRegion(long hARIO, double dfTimeout, int[] pnXBufOff, int[] pnYBufOff, int[] pnXBufSize, int[] pnYBufSize) {
+ return 0;
+ }
+
+ @Override
+ public int GDALARLockBuffer(long hARIO, double dfTimeout) {
+ return 0;
+ }
+
+ @Override
+ public void GDALARUnlockBuffer(long hARIO) {
+
+ }
+
+ @Override
+ public int GDALGeneralCmdLineProcessor(int nArgc, String[][] ppapszArgv, int nOptions) {
+ return 0;
+ }
+
+ @Override
+ public void GDALSwapWords(MemorySegment pData, int nWordSize, int nWordCount, int nWordSkip) {
+
+ }
+
+ @Override
+ public void GDALSwapWordsEx(MemorySegment pData, int nWordSize, long nWordCount, int nWordSkip) {
+
+ }
+
+ @Override
+ public void GDALCopyWords(MemorySegment pSrcData, int eSrcType, int nSrcPixelOffset, MemorySegment pDstData, int eDstType, int nDstPixelOffset, int nWordCount) {
+
+ }
+
+ @Override
+ public void GDALCopyWords64(MemorySegment pSrcData, int eSrcType, int nSrcPixelOffset, MemorySegment pDstData, int eDstType, int nDstPixelOffset, long nWordCount) {
+
+ }
+
+ @Override
+ public void GDALCopyBits(MemorySegment pabySrcData, int nSrcOffset, int nSrcStep, MemorySegment pabyDstData, int nDstOffset, int nDstStep, int nBitCount, int nStepCount) {
+
+ }
+
+ @Override
+ public void GDALDeinterleave(MemorySegment pSourceBuffer, int eSourceDT, int nComponents, MemorySegment[] ppDestBuffer, int eDestDT, long nIters) {
+
+ }
+
+ @Override
+ public int GDALLoadWorldFile(MemorySegment filename, double[] geotransform) {
+ return 0;
+ }
+
+ @Override
+ public int GDALReadWorldFile(MemorySegment filename, MemorySegment extension, double[] geotransform) {
+ return 0;
+ }
+
+ @Override
+ public int GDALWriteWorldFile(MemorySegment filename, MemorySegment extension, double[] geotransform) {
+ return 0;
+ }
+
+ @Override
+ public int GDALLoadTabFile(MemorySegment filename, double[] geotransform, MemorySegment[] ppszProjection, int[] pnGCPCount, MemorySegment[] ppasGCPList) {
+ return 0;
+ }
+
+ @Override
+ public int GDALReadTabFile(MemorySegment filename, double[] geotransform, MemorySegment[] ppszProjection, int[] pnGCPCount, MemorySegment[] ppasGCPList) {
+ return 0;
+ }
+
+ @Override
+ public int GDALLoadOziMapFile(MemorySegment filename, double[] geotransform, MemorySegment[] ppszProjection, int[] pnGCPCount, MemorySegment[] ppasGCPList) {
+ return 0;
+ }
+
+ @Override
+ public int GDALReadOziMapFile(MemorySegment filename, double[] geotransform, MemorySegment[] ppszProjection, int[] pnGCPCount, MemorySegment[] ppasGCPList) {
+ return 0;
+ }
+
+ @Override
+ public MemorySegment GDALDecToDMS(double dfAngle, MemorySegment pszAxis, int nPrecision) {
+ return null;
+ }
+
+ @Override
+ public double GDALPackedDMSToDec(double dfAngle) {
+ return 0;
+ }
+
+ @Override
+ public double GDALDecToPackedDMS(double dfAngle) {
+ return 0;
+ }
+
+ @Override
+ public long GDALCreateColorTable(int eInterp) {
+ return 0;
+ }
+
+ @Override
+ public void GDALDestroyColorTable(long hTable) {
+
+ }
+
+ @Override
+ public long GDALCloneColorTable(long hSrcTable) {
+ return 0;
+ }
+
+ @Override
+ public int GDALGetPaletteInterpretation(long hTable) {
+ return 0;
+ }
+
+ private static MethodHandle GDALGetColorEntryCount = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetColorEntryCount"),
+ FunctionDescriptor.of(ValueLayout.JAVA_INT,
+ ValueLayout.ADDRESS));
+
+ @Override
+ public int GDALGetColorEntryCount(MemorySegment hTable) throws Throwable {
+ return (int) GDALGetColorEntryCount.invokeExact(hTable);
+ }
+
+ @Override
+ public GDALColorEntry GDALGetColorEntry(long hTable, int i) throws Throwable {
+ return null;
+ }
+
+ private static MethodHandle GDALGetColorEntryAsRGB = Linker.nativeLinker().downcallHandle(
+ lookup("GDALGetColorEntryAsRGB"),
+ FunctionDescriptor.of(ValueLayout.JAVA_INT,
+ ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.ADDRESS));
+
+ @Override
+ public int GDALGetColorEntryAsRGB(MemorySegment hTable, int i, GDALColorEntry sColorEntry) throws Throwable {
+ return (int) GDALGetColorEntryAsRGB.invokeExact(hTable, i, sColorEntry.getSegment());
+ }
+
+ @Override
+ public void GDALSetColorEntry(long hTable, int i, GDALColorEntry sColorEntry) {
+
+ }
+
+ @Override
+ public void GDALCreateColorRamp(long hTable, int nStartIndex, GDALColorEntry psStartColor, int nEndIndex, GDALColorEntry psEndColor) {
+
+ }
+
+ private static MethodHandle GDALFlushCacheBlock = Linker.nativeLinker().downcallHandle(
+ lookup("GDALFlushCacheBlock"),
+ FunctionDescriptor.of(ValueLayout.JAVA_INT));
+
+ @Override
+ public int GDALFlushCacheBlock() throws Throwable {
+ return (int) GDALFlushCacheBlock.invokeExact();
+ }
+
+ @Override
+ public void CPLSetConfigOption(String pszKey, String pszValue) {
+
+ }
+
+ @Override
+ public void CPLSetThreadLocalConfigOption(String pszKey, String pszValue) {
+
+ }
+
+ @Override
+ public int OSRExportToWkt(MemorySegment hSRS, MemorySegment ppszReturn) {
+ return 0;
+ }
+
+ private static MethodHandle GDALVersionInfo = Linker.nativeLinker().downcallHandle(
+ lookup("GDALVersionInfo"),
+ FunctionDescriptor.of(ValueLayout.ADDRESS,
+ ValueLayout.ADDRESS));
+
+ @Override
+ public String GDALVersionInfo(String pszRequest) throws Throwable {
+ try (var arena = Arena.ofConfined()){
+ MemorySegment stringSegment = (MemorySegment) GDALVersionInfo.invokeExact(
+ arena.allocateFrom(pszRequest)
+ );
+
+ stringSegment = stringSegment.reinterpret(Long.MAX_VALUE); //TODO : Remove if we can find another way to read Strings
+
+ if (stringSegment != MemorySegment.NULL && stringSegment.byteSize() > 0) {
+ return stringSegment.getString(0);
+ }
+ return null;
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/GdalOgr.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/GdalOgr.java
new file mode 100644
index 0000000000..0a6bc42a76
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/GdalOgr.java
@@ -0,0 +1,149 @@
+package org.apache.sis.storage.gdal.internal;
+
+import org.apache.sis.io.stream.IOUtilities;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.gdal.internal.panama.Gdal;
+
+import java.io.IOException;
+import java.lang.foreign.MemorySegment;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Class that manages the gdal library and all access to it
+ *
+ * @author Quentin BIALOTA (Geomatys)
+ */
+public class GdalOgr {
+
+ /**
+ * GDAL interface instance (used to call gdal methods using panama)
+ */
+ private static Gdal GDAL;
+
+ /**
+ * Logger
+ */
+ public static final Logger LOGGER = Logger.getLogger("org.apache.sis.storage.gdal");
+
+ /**
+ * List known gdal extensions in lowercase.
+ */
+ public static final Set<String> GDAL_EXTENSIONS = new HashSet<>();
+
+ /**
+ * Map of mimetype associated with gdal extensions.
+ */
+ public static final Map<String,String> EXTENSION_TO_MIMETYPE = new HashMap<>();
+
+ /**
+ * Failed message if {@see GdalOgr#init()} failed.
+ */
+ private static String failed;
+
+ /**
+ * Get GDAL instance
+ * @return Gdal Instance
+ * @throws DataStoreException If loading of GDAL library goes wrong
+ */
+ public static Gdal GDAL() throws DataStoreException {
+ init();
+ return GDAL;
+ }
+
+ /**
+ * Load GDAL library and check drivers supported
+ * If gdal is already loaded this method has no effect.
+ *
+ * @throws DataStoreException if loading GDAL library failed
+ */
+ public static synchronized void init() throws DataStoreException {
+ if (GDAL != null) return;
+ if (failed != null) throw new DataStoreException(failed,null);
+
+ try {
+ //Set instance of GDAL library
+ GDAL = new GdalImpl();
+
+ GDAL.GDALAllRegister();
+
+ //StringBuilder to store information of GDAL (version, supported drivers, ...)
+ final StringBuilder sb = new StringBuilder();
+ sb.append("GDAL version : ").append(GDAL.GDALVersionInfo("RELEASE_NAME"))
+ .append(" ,Date : ").append(GDAL.GDALVersionInfo("RELEASE_DATE")).append('\n');
+ sb.append("GDAL formats : ");
+
+ final int nbDriver = GDAL.GDALGetDriverCount();
+
+ for (int i=0;i<nbDriver;i++) {
+ final MemorySegment driverPt = GDAL.GDALGetDriver(i);
+ String name = GDAL.GDALGetDriverLongName(driverPt);
+ String shortName = GDAL.GDALGetDriverShortName(driverPt);
+ if(i>0) sb.append(", ");
+ sb.append(name);
+ sb.append('(').append(shortName).append(')');
+
+ //build extension -> mime-type list
+ String mimeType = GDAL.GDALGetMetadataItem(driverPt, Gdal.GDAL_DMD_MIMETYPE, null);
+
+ final String extensions = GDAL.GDALGetMetadataItem(driverPt, Gdal.GDAL_DMD_EXTENSIONS, null);
+ final boolean rasterType = "YES".equalsIgnoreCase(GDAL.GDALGetMetadataItem(driverPt, Gdal.GDAL_DCAP_RASTER, null));
+ if (extensions!=null) {
+ for (String ext : extensions.split(" ")) {
+ ext = ext.trim().toLowerCase();
+ if (!ext.isEmpty()) {
+ if (rasterType) {
+ GDAL_EXTENSIONS.add(ext);
+ }
+ }
+ EXTENSION_TO_MIMETYPE.put(ext, mimeType);
+ }
+ }
+ }
+
+ //TODO: Remove that when GDALGetMetadataItem works (for the moment the result of the function is null, so there is no GDAL_EXTENTIONS supported)
+ GDAL_EXTENSIONS.add("tiff");
+ EXTENSION_TO_MIMETYPE.put("tiff", "tiff");
+
+ sb.append("\nGDAL extensions : ");
+ for(String str : GDAL_EXTENSIONS) sb.append(str).append(' ');
+ sb.append("\nUndeclared extensions(but supported) : aux, adf");
+ GDAL_EXTENSIONS.add("aux");
+ GDAL_EXTENSIONS.add("adf");
+
+ LOGGER.log(Level.FINE, sb.toString());
+ } catch(Throwable ex) {
+ failed = ex.getMessage();
+ LOGGER.log(Level.WARNING, "Failed to load GDAL/OGR (See debug log for more information)");
+ LOGGER.log(Level.FINE, "Failed to load GDAL/OGR", ex);
+ throw new DataStoreException("Failed to load GDAL/OGR library", ex);
+ }
+ }
+
+ /**
+ * List all existing paths with the same file name but possibly with different
+ * prefixes.
+ *
+ * @return array of sibling paths, including base path
+ */
+ public static Path[] listSiblings(Path base) throws IOException {
+
+ final String name = IOUtilities.filenameWithoutExtension(String.valueOf(base));
+ final Path parent = base.getParent();
+ final List<Path> siblings = new ArrayList<>();
+
+ try (DirectoryStream<Path> stream = Files.newDirectoryStream(parent, (Path entry) -> entry.getFileName().toString().startsWith(name))) {
+ stream.forEach(siblings::add);
+ }
+ return siblings.toArray(new Path[siblings.size()]);
+ }
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/CPL.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/CPL.java
new file mode 100644
index 0000000000..163d56f2d0
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/CPL.java
@@ -0,0 +1,13 @@
+package org.apache.sis.storage.gdal.internal.panama;
+
+import java.lang.foreign.MemorySegment;
+
+public interface CPL {
+
+ void CPLSetConfigOption(String pszKey, String pszValue);
+
+ void CPLSetThreadLocalConfigOption(String pszKey, String pszValue);
+
+ int OSRExportToWkt(MemorySegment hSRS, MemorySegment ppszReturn);
+
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/CPLErr.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/CPLErr.java
new file mode 100644
index 0000000000..3ef612fccb
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/CPLErr.java
@@ -0,0 +1,30 @@
+package org.apache.sis.storage.gdal.internal.panama;
+
+public enum CPLErr {
+ CE_None(0),
+ CE_Debug(1),
+ CE_Warning(2),
+ CE_Failure(3),
+ CE_Fatal(4);
+
+ private final int value;
+
+ CPLErr(int value){
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public static CPLErr getByValue(int value) throws IllegalArgumentException {
+ return switch (value) {
+ case 0 -> CPLErr.CE_None;
+ case 1 -> CPLErr.CE_Debug;
+ case 2 -> CPLErr.CE_Warning;
+ case 3 -> CPLErr.CE_Failure;
+ case 4 -> CPLErr.CE_Fatal;
+ default -> throw new IllegalArgumentException("Invalid CPLErr value: " + value);
+ };
+ }
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALAccess.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALAccess.java
new file mode 100644
index 0000000000..5145b5626f
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALAccess.java
@@ -0,0 +1,18 @@
+package org.apache.sis.storage.gdal.internal.panama;
+
+public enum GDALAccess {
+ // Read only (no update) access
+ GA_ReadOnly(0x00),
+ // Read/write access.
+ GA_Update(0x01);
+
+ private final int code;
+
+ private GDALAccess(int code){
+ this.code = code;
+ }
+
+ public int getCode(){
+ return code;
+ }
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALAsyncStatusType.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALAsyncStatusType.java
new file mode 100644
index 0000000000..b5d51a45c9
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALAsyncStatusType.java
@@ -0,0 +1,9 @@
+package org.apache.sis.storage.gdal.internal.panama;
+
+public enum GDALAsyncStatusType {
+ GARIO_PENDING,
+ GARIO_UPDATE,
+ GARIO_ERROR,
+ GARIO_COMPLETE,
+ GARIO_TypeCount
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALColorEntry.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALColorEntry.java
new file mode 100644
index 0000000000..8eddccd6f9
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALColorEntry.java
@@ -0,0 +1,46 @@
+package org.apache.sis.storage.gdal.internal.panama;
+
+import java.lang.foreign.MemoryLayout;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.ValueLayout;
+import java.lang.invoke.VarHandle;
+
+public class GDALColorEntry {
+ public static final MemoryLayout LAYOUT = MemoryLayout.structLayout(
+ ValueLayout.JAVA_SHORT.withName("c1"),
+ ValueLayout.JAVA_SHORT.withName("c2"),
+ ValueLayout.JAVA_SHORT.withName("c3"),
+ ValueLayout.JAVA_SHORT.withName("c4")
+ );
+
+ private final VarHandle handleC1 = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("c1"));
+ private final VarHandle handleC2 = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("c2"));
+ private final VarHandle handleC3 = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("c3"));
+ private final VarHandle handleC4 = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("c4"));
+
+ private final MemorySegment segment;
+
+ public GDALColorEntry(MemorySegment segment) {
+ this.segment = segment;
+ }
+
+ public MemorySegment getSegment() {
+ return segment;
+ }
+
+ public short getC1() {
+ return (short) handleC1.get(segment, 0);
+ }
+
+ public short getC2() {
+ return (short) handleC2.get(segment, 0);
+ }
+
+ public short getC3() {
+ return (short) handleC3.get(segment, 0);
+ }
+
+ public short getC4() {
+ return (short) handleC4.get(segment, 0);
+ }
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALColorInterp.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALColorInterp.java
new file mode 100644
index 0000000000..08cb8fdbf6
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALColorInterp.java
@@ -0,0 +1,38 @@
+package org.apache.sis.storage.gdal.internal.panama;
+
+public enum GDALColorInterp {
+ // Greyscale
+ GCI_GrayIndex,
+ // Paletted (see associated color table)
+ GCI_PaletteIndex,
+ // Red band of RGBA image
+ GCI_RedBand,
+ // Green band of RGBA image
+ GCI_GreenBand,
+ // Blue band of RGBA image
+ GCI_BlueBand,
+ // Alpha (0=transparent, 255=opaque)
+ GCI_AlphaBand,
+ // Hue band of HLS image
+ GCI_HueBand,
+ // Saturation band of HLS image
+ GCI_SaturationBand,
+ // Lightness band of HLS image
+ GCI_LightnessBand,
+ // Cyan band of CMYK image
+ GCI_CyanBand,
+ // Magenta band of CMYK image
+ GCI_MagentaBand,
+ // Yellow band of CMYK image
+ GCI_YellowBand,
+ // Black band of CMLY image
+ GCI_BlackBand,
+ // Y Luminance
+ GCI_YCbCr_YBand,
+ // Cb Chroma
+ GCI_YCbCr_CbBand,
+ // Cr Chroma
+ GCI_YCbCr_CrBand,
+ // Max current value
+ GCI_Max
+}
\ No newline at end of file
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALDataType.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALDataType.java
new file mode 100644
index 0000000000..faf3850a91
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALDataType.java
@@ -0,0 +1,133 @@
+package org.apache.sis.storage.gdal.internal.panama;
+
+import java.awt.image.DataBuffer;
+
+public enum GDALDataType {
+ /**
+ * Unknown or unspecified type
+ */
+ GDT_Unknown(Float.class, DataBuffer.TYPE_FLOAT, 0),
+ /**
+ * Eight bit unsigned integer
+ */
+ GDT_Byte(Byte.class, DataBuffer.TYPE_BYTE, 1),
+ /**
+ * Eight bit signed integer
+ */
+ GDT_Int8(Byte.class, DataBuffer.TYPE_BYTE, 14),
+ /**
+ * Sixteen bit unsigned integer
+ */
+ GDT_UInt16(Short.class, DataBuffer.TYPE_USHORT, 2),
+ /**
+ * Sixteen bit signed integer
+ */
+ GDT_Int16(Short.class, DataBuffer.TYPE_SHORT, 3),
+ /**
+ * Thirty two bit unsigned integer
+ */
+ GDT_UInt32(Integer.class, DataBuffer.TYPE_INT, 4),
+ /**
+ * Thirty two bit signed integer
+ */
+ GDT_Int32(Integer.class, DataBuffer.TYPE_INT, 5),
+ /**
+ * Sixty four bit unsigned integer
+ */
+ GDT_UInt64(Long.class, DataBuffer.TYPE_INT, 12),
+ /**
+ * Sixty four bit signed integer
+ */
+ GDT_Int64(Long.class, DataBuffer.TYPE_INT, 13),
+ /**
+ * Thirty two bit floating point
+ */
+ GDT_Float32(Float.class, DataBuffer.TYPE_FLOAT, 6),
+ /**
+ * Sixty four bit floating point
+ */
+ GDT_Float64(Double.class, DataBuffer.TYPE_DOUBLE, 7),
+ /**
+ * Complex Int16
+ */
+ GDT_CInt16(Short.class, DataBuffer.TYPE_SHORT, 8),
+ /**
+ * Complex Int32
+ */
+ GDT_CInt32(Integer.class, DataBuffer.TYPE_INT, 9),
+ /**
+ * Complex Float32
+ */
+ GDT_CFloat32(Float.class, DataBuffer.TYPE_FLOAT, 10),
+ /**
+ * Complex Float64
+ */
+ GDT_CFloat64(Double.class, DataBuffer.TYPE_DOUBLE, 11);
+
+ private final Class javaClass;
+ private final int imgDataType;
+ private final int value;
+
+ GDALDataType(Class javaClass, int imgDataType, int value){
+ this.javaClass = javaClass;
+ this.imgDataType = imgDataType;
+ this.value = value;
+ }
+
+ public Class getJavaClass(){
+ return javaClass;
+ }
+
+ /**
+ * Get most appropriate java sample model data type.
+ *
+ * @return DataBuffer.TYPE_X
+ */
+ public int getImageDataType() {
+ return imgDataType;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ /**
+ * Get corresponding gdal data type for a given databuffer.
+ *
+ * @param imgDataType java DataBuffer type
+ * @return GDALDataType GDAL equivalent
+ */
+ public static GDALDataType fromGdalTypeToDataBuffer(int imgDataType) {
+ switch(imgDataType) {
+ case DataBuffer.TYPE_BYTE : return GDT_Byte;
+ case DataBuffer.TYPE_SHORT : return GDT_Int16;
+ case DataBuffer.TYPE_USHORT : return GDT_UInt16;
+ case DataBuffer.TYPE_INT : return GDT_Int32;
+ case DataBuffer.TYPE_FLOAT : return GDT_Float32;
+ case DataBuffer.TYPE_DOUBLE : return GDT_Float64;
+ case DataBuffer.TYPE_UNDEFINED :return GDT_Float32;
+ default : throw new IllegalArgumentException("Unknown image data type"+imgDataType);
+ }
+ }
+
+ public static GDALDataType fromValue(final int value) {
+ return switch (value) {
+ case 0 -> GDALDataType.GDT_Unknown;
+ case 1 -> GDALDataType.GDT_Byte;
+ case 14 -> GDALDataType.GDT_Int8;
+ case 2 -> GDALDataType.GDT_UInt16;
+ case 3 -> GDALDataType.GDT_Int16;
+ case 4 -> GDALDataType.GDT_UInt32;
+ case 5 -> GDALDataType.GDT_Int32;
+ case 12 -> GDALDataType.GDT_UInt64;
+ case 13 -> GDALDataType.GDT_Int64;
+ case 6 -> GDALDataType.GDT_Float32;
+ case 7 -> GDALDataType.GDT_Float64;
+ case 8 -> GDALDataType.GDT_CInt16;
+ case 9 -> GDALDataType.GDT_CInt32;
+ case 10 -> GDALDataType.GDT_CFloat32;
+ case 11 -> GDALDataType.GDT_CFloat64;
+ default -> throw new IllegalArgumentException("Invalid data type: " + value);
+ };
+ }
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALPaletteInterp.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALPaletteInterp.java
new file mode 100644
index 0000000000..18635f19f3
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALPaletteInterp.java
@@ -0,0 +1,12 @@
+package org.apache.sis.storage.gdal.internal.panama;
+
+public enum GDALPaletteInterp {
+ // Grayscale (in GDALColorEntry.c1)
+ GPI_Gray,
+ // Red, Green, Blue and Alpha in (in c1, c2, c3 and c4)
+ GPI_RGB,
+ // Cyan, Magenta, Yellow and Black (in c1, c2, c3 and c4)
+ GPI_CMYK,
+ // Hue, Lightness and Saturation (in c1, c2, and c3)
+ GPI_HLS
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALRWFlag.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALRWFlag.java
new file mode 100644
index 0000000000..425e94b9af
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/GDALRWFlag.java
@@ -0,0 +1,24 @@
+package org.apache.sis.storage.gdal.internal.panama;
+
+public enum GDALRWFlag {
+ // Read data
+ GF_Read(0),
+ // Write data
+ GF_Write(1);
+
+ private final int value;
+
+ GDALRWFlag(final int value) {
+ this.value = value;
+ }
+
+ public int value() {return value;}
+
+ public static GDALRWFlag fromValue(final int value) {
+ return switch (value) {
+ case 0 -> GF_Read;
+ case 1 -> GF_Write;
+ default -> throw new IllegalStateException("Unexpected value: " + value);
+ };
+ }
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/Gdal.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/Gdal.java
new file mode 100644
index 0000000000..57879f3b1d
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/Gdal.java
@@ -0,0 +1,977 @@
+package org.apache.sis.storage.gdal.internal.panama;
+
+import java.lang.foreign.MemorySegment;
+
+/**
+ * Gdal interface
+ * Last updated for GDAL 3.7.3, released 2023/10/30
+ * @author Quentin BIALOTA (Geomatys)
+ */
+public interface Gdal extends CPL {
+
+ /*
+ ///// CONSTANTS
+ */
+
+ /** Metadata item for dataset that indicates the spatial interpreation of a pixel.*/
+ static final String GDALMD_AREA_OR_POINT = "AREA_OR_POINT";
+
+ /** Value for GDALMD_AREA_OR_POINT that indicates that a pixel represents an area.*/
+ static final String GDALMD_AOP_AREA = "Area";
+
+ /** Value for GDALMD_AREA_OR_POINT that indicates that a pixel represents a point.*/
+ static final String GDALMD_AOP_POINT = "Point";
+
+ /** Long name of the driver.*/
+ static final String GDAL_DMD_LONGNAME = "DMD_LONGNAME";
+
+ /* URL (relative to http://gdal.org/) to the help page of the driver.*/
+ static final String GDAL_DMD_HELPTOPIC = "DMD_HELPTOPIC";
+
+ // MIME type handled by the driver. More...
+ static final String GDAL_DMD_MIMETYPE = "DMD_MIMETYPE";
+
+ // Extension handled by the driver. More...
+ static final String GDAL_DMD_EXTENSION = "DMD_EXTENSION";
+
+ // Connection prefix to provide as the file name of the open function. More...
+ static final String GDAL_DMD_CONNECTION_PREFIX = "DMD_CONNECTION_PREFIX";
+
+ // List of (space separated) extensions handled by the driver. More...
+ static final String GDAL_DMD_EXTENSIONS = "DMD_EXTENSIONS";
+
+ // XML snippet with creation options. More...
+ static final String GDAL_DMD_CREATIONOPTIONLIST = "DMD_CREATIONOPTIONLIST";
+
+ // XML snippet with multidimensional dataset creation options.
+ static final String GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST = "DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST";
+
+ // XML snippet with multidimensional group creation options.
+ static final String GDAL_DMD_MULTIDIM_GROUP_CREATIONOPTIONLIST = "DMD_MULTIDIM_GROUP_CREATIONOPTIONLIST";
+
+ // XML snippet with multidimensional dimension creation options.
+ static final String GDAL_DMD_MULTIDIM_DIMENSION_CREATIONOPTIONLIST = "DMD_MULTIDIM_DIMENSION_CREATIONOPTIONLIST";
+
+ // XML snippet with multidimensional array creation options.
+ static final String GDAL_DMD_MULTIDIM_ARRAY_CREATIONOPTIONLIST = "DMD_MULTIDIM_ARRAY_CREATIONOPTIONLIST";
+
+ // XML snippet with multidimensional array open options.
+ static final String GDAL_DMD_MULTIDIM_ARRAY_OPENOPTIONLIST = "DMD_MULTIDIM_ARRAY_OPENOPTIONLIST";
+
+ // XML snippet with multidimensional attribute creation options.
+ static final String GDAL_DMD_MULTIDIM_ATTRIBUTE_CREATIONOPTIONLIST = "DMD_MULTIDIM_ATTRIBUTE_CREATIONOPTIONLIST";
+
+ // XML snippet with open options. More...
+ static final String GDAL_DMD_OPENOPTIONLIST = "DMD_OPENOPTIONLIST";
+
+ // List of (space separated) raster data types support by the Create()/CreateCopy() API. More...
+ static final String GDAL_DMD_CREATIONDATATYPES = "DMD_CREATIONDATATYPES";
+
+ // List of (space separated) vector field types support by the CreateField() API. More...
+ static final String GDAL_DMD_CREATIONFIELDDATATYPES = "DMD_CREATIONFIELDDATATYPES";
+
+ // List of (space separated) vector field sub-types supported by the CreateField() API.
+ static final String GDAL_DMD_CREATIONFIELDDATASUBTYPES = "DMD_CREATIONFIELDDATASUBTYPES";
+
+ // List of (space separated) capability flags supported by the CreateField() API.
+ static final String GDAL_DMD_CREATION_FIELD_DEFN_FLAGS = "DMD_CREATION_FIELD_DEFN_FLAGS";
+
+ // Capability set by a driver that exposes Subdatasets. More...
+ static final String GDAL_DMD_SUBDATASETS = "DMD_SUBDATASETS";
+
+ // Capability set by a vector driver that supports field width and precision.
+ static final String GDAL_DMD_NUMERIC_FIELD_WIDTH_INCLUDES_DECIMAL_SEPARATOR = "DMD_NUMERIC_FIELD_WIDTH_INCLUDES_DECIMAL_SEPARATOR";
+
+ // Capability set by a vector driver that supports field width and precision.
+ static final String GDAL_DMD_NUMERIC_FIELD_WIDTH_INCLUDES_SIGN = "DMD_NUMERIC_FIELD_WIDTH_INCLUDES_SIGN";
+
+ // Capability set by a driver that implements the Open() API. More...
+ static final String GDAL_DCAP_OPEN = "DCAP_OPEN";
+
+ // Capability set by a driver that implements the Create() API. More...
+ static final String GDAL_DCAP_CREATE = "DCAP_CREATE";
+
+ // Capability set by a driver that implements the CreateMultidimensional() API.
+ static final String GDAL_DCAP_CREATE_MULTIDIMENSIONAL = "DCAP_CREATE_MULTIDIMENSIONAL";
+
+ // Capability set by a driver that implements the CreateCopy() API. More...
+ static final String GDAL_DCAP_CREATECOPY = "DCAP_CREATECOPY";
+
+ // Capability set by a driver that implements the CreateCopy() API, but with multidimensional raster as input and output.
+ static final String GDAL_DCAP_CREATECOPY_MULTIDIMENSIONAL = "DCAP_CREATECOPY_MULTIDIMENSIONAL";
+
+ // Capability set by a driver that supports multidimensional data.
+ static final String GDAL_DCAP_MULTIDIM_RASTER = "DCAP_MULTIDIM_RASTER";
+
+ // Capability set by a driver that can copy over subdatasets.
+ static final String GDAL_DCAP_SUBCREATECOPY = "DCAP_SUBCREATECOPY";
+
+ // Capability set by a driver that can read/create datasets through the VSI*L API. More...
+ static final String GDAL_DCAP_VIRTUALIO = "DCAP_VIRTUALIO";
+
+ // Capability set by a driver having raster capability. More...
+ static final String GDAL_DCAP_RASTER = "DCAP_RASTER";
+
+ // Capability set by a driver having vector capability. More...
+ static final String GDAL_DCAP_VECTOR = "DCAP_VECTOR";
+
+ // Capability set by a driver having vector capability. More...
+ static final String GDAL_DCAP_GNM = "DCAP_GNM";
+
+ // Capability set by a driver that can create layers.
+ static final String GDAL_DCAP_CREATE_LAYER = "DCAP_CREATE_LAYER";
+
+ // Capability set by a driver that can delete layers.
+ static final String GDAL_DCAP_DELETE_LAYER = "DCAP_DELETE_LAYER";
+
+ // Capability set by a driver that can create fields.
+ static final String GDAL_DCAP_CREATE_FIELD = "DCAP_CREATE_FIELD";
+
+ // Capability set by a driver that can delete fields.
+ static final String GDAL_DCAP_DELETE_FIELD = "DCAP_DELETE_FIELD";
+
+ // Capability set by a driver that can reorder fields.
+ static final String GDAL_DCAP_REORDER_FIELDS = "DCAP_REORDER_FIELDS";
+
+ // List of (space separated) flags supported by the OGRLayer::AlterFieldDefn() API.
+ static final String GDAL_DMD_ALTER_FIELD_DEFN_FLAGS = "GDAL_DMD_ALTER_FIELD_DEFN_FLAGS";
+
+ // List of (space separated) field names which are considered illegal by the driver and should not be used when creating/altering fields.
+ static final String GDAL_DMD_ILLEGAL_FIELD_NAMES = "GDAL_DMD_ILLEGAL_FIELD_NAMES";
+
+ // Capability set by a driver that can create fields with NOT NULL constraint. More...
+ static final String GDAL_DCAP_NOTNULL_FIELDS = "DCAP_NOTNULL_FIELDS";
+
+ // Capability set by a driver that can create fields with UNIQUE constraint.
+ static final String GDAL_DCAP_UNIQUE_FIELDS = "DCAP_UNIQUE_FIELDS";
+
+ // Capability set by a driver that can create fields with DEFAULT values. More...
+ static final String GDAL_DCAP_DEFAULT_FIELDS = "DCAP_DEFAULT_FIELDS";
+
+ // Capability set by a driver that can create geometry fields with NOT NULL constraint. More...
+ static final String GDAL_DCAP_NOTNULL_GEOMFIELDS = "DCAP_NOTNULL_GEOMFIELDS";
+
+ // Capability set by a non-spatial driver having no support for geometries.
+ static final String GDAL_DCAP_NONSPATIAL = "DCAP_NONSPATIAL";
+
+ // Capability set by a driver that can support curved geometries.
+ static final String GDAL_DCAP_CURVE_GEOMETRIES = "DCAP_CURVE_GEOMETRIES";
+
+ // Capability set by a driver that can support measured geometries.
+ static final String GDAL_DCAP_MEASURED_GEOMETRIES = "DCAP_MEASURED_GEOMETRIES";
+
+ // Capability set by a driver that can support the Z dimension for geometries.
+ static final String GDAL_DCAP_Z_GEOMETRIES = "DCAP_Z_GEOMETRIES";
+
+ // List of (space separated) flags which reflect the geometry handling behavior of a driver.
+ static final String GDAL_DMD_GEOMETRY_FLAGS = "GDAL_DMD_GEOMETRY_FLAGS";
+
+ // Capability set by drivers which support either reading or writing feature styles.
+ static final String GDAL_DCAP_FEATURE_STYLES = "DCAP_FEATURE_STYLES";
+
+ // Capability set by drivers which support reading feature styles.
+ static final String GDAL_DCAP_FEATURE_STYLES_READ = "DCAP_FEATURE_STYLES_READ";
+
+ // Capability set by drivers which support writing feature styles.
+ static final String GDAL_DCAP_FEATURE_STYLES_WRITE = "DCAP_FEATURE_STYLES_WRITE";
+
+ // Capability set by drivers which support storing/retrieving coordinate epoch for dynamic CRS
+ static final String GDAL_DCAP_COORDINATE_EPOCH = "DCAP_COORDINATE_EPOCH";
+
+ // Capability set by drivers for formats which support multiple vector layers.
+ static final String GDAL_DCAP_MULTIPLE_VECTOR_LAYERS = "DCAP_MULTIPLE_VECTOR_LAYERS";
+
+ // Capability set by drivers for formats which support reading field domains.
+ static final String GDAL_DCAP_FIELD_DOMAINS = "DCAP_FIELD_DOMAINS";
+
+ // Capability set by drivers for formats which support reading table relationships.
+ static final String GDAL_DCAP_RELATIONSHIPS = "DCAP_RELATIONSHIPS";
+
+ // Capability set by drivers for formats which support creating table relationships.
+ static final String GDAL_DCAP_CREATE_RELATIONSHIP = "DCAP_CREATE_RELATIONSHIP";
+
+ // Capability set by drivers for formats which support deleting table relationships.
+ static final String GDAL_DCAP_DELETE_RELATIONSHIP = "DCAP_DELETE_RELATIONSHIP";
+
+ // Capability set by drivers for formats which support updating existing table relationships.
+ static final String GDAL_DCAP_UPDATE_RELATIONSHIP = "DCAP_UPDATE_RELATIONSHIP";
+
+ // List of (space separated) flags indicating the features of relationships are supported by the driver.
+ static final String GDAL_DMD_RELATIONSHIP_FLAGS = "GDAL_DMD_RELATIONSHIP_FLAGS";
+
+ // List of (space separated) standard related table types which are recognised by the driver.
+ static final String GDAL_DMD_RELATIONSHIP_RELATED_TABLE_TYPES = "GDAL_DMD_RELATIONSHIP_RELATED_TABLE_TYPES";
+
+ // Capability set by drivers for formats which support renaming vector layers.
+ static final String GDAL_DCAP_RENAME_LAYERS = "DCAP_RENAME_LAYERS";
+
+ // List of (space separated) field domain types supported by the AddFieldDomain() API.
+ static final String GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES = "DMD_CREATION_FIELD_DOMAIN_TYPES";
+
+ // List of (space separated) flags supported by the OGRLayer::AlterGeomFieldDefn() API.
+ static final String GDAL_DMD_ALTER_GEOM_FIELD_DEFN_FLAGS = "DMD_ALTER_GEOM_FIELD_DEFN_FLAGS";
+
+ // List of (space separated) SQL dialects supported by the driver.
+ static final String GDAL_DMD_SUPPORTED_SQL_DIALECTS = "DMD_SUPPORTED_SQL_DIALECTS";
+
+ // Value for GDALDimension::GetType() specifying the X axis of a horizontal CRS.
+ static final String GDAL_DIM_TYPE_HORIZONTAL_X = "HORIZONTAL_X";
+
+ // Value for GDALDimension::GetType() specifying the Y axis of a horizontal CRS.
+ static final String GDAL_DIM_TYPE_HORIZONTAL_Y = "HORIZONTAL_Y";
+
+ // Value for GDALDimension::GetType() specifying a vertical axis.
+ static final String GDAL_DIM_TYPE_VERTICAL = "VERTICAL";
+
+ // Value for GDALDimension::GetType() specifying a temporal axis.
+ static final String GDAL_DIM_TYPE_TEMPORAL = "TEMPORAL";
+
+ // Value for GDALDimension::GetType() specifying a parametric axis.
+ static final String GDAL_DIM_TYPE_PARAMETRIC = "PARAMETRIC";
+
+ // Dataset capability for supporting AddRelationship() (at least partially)
+ static final String GDsCAddRelationship = "AddRelationship";
+
+ // Dataset capability for supporting DeleteRelationship()
+ static final String GDsCDeleteRelationship = "DeleteRelationship";
+
+ // Dataset capability for supporting UpdateRelationship()
+ static final String GDsCUpdateRelationship = "UpdateRelationship";
+
+ // Open in read-only mode. More...
+ static final int GDAL_OF_READONLY = 0x00;
+
+ // Open in update mode. More...
+ static final int GDAL_OF_UPDATE = 0x01;
+
+ // Allow raster and vector drivers to be used. More...
+ static final int GDAL_OF_ALL = 0x00;
+
+ // Allow raster drivers to be used. More...
+ static final int GDAL_OF_RASTER = 0x02;
+
+ // Allow vector drivers to be used. More...
+ static final int GDAL_OF_VECTOR = 0x04;
+
+ // Allow gnm drivers to be used. More...
+ static final int GDAL_OF_GNM = 0x08;
+
+ //Allow multidimensional raster drivers to be used.
+ static final int GDAL_OF_MULTIDIM_RASTER = 0x10;
+
+ static final int GDAL_OF_KIND_MASK = 0x1E;
+
+ // Open in shared mode. More...
+ static final int GDAL_OF_SHARED = 0x20;
+
+ // Emit error message in case of failed open. More...
+ static final int GDAL_OF_VERBOSE_ERROR = 0x40;
+
+ // Open as internal dataset. More...
+ static final int GDAL_OF_INTERNAL = 0x80;
+
+ // Let GDAL decide if a array-based or hashset-based storage strategy for cached blocks must be used. More...
+ static final int GDAL_OF_DEFAULT_BLOCK_ACCESS = 0;
+
+ // Use a array-based storage strategy for cached blocks. More...
+ static final int GDAL_OF_ARRAY_BLOCK_ACCESS = 0x100;
+
+ // Use a hashset-based storage strategy for cached blocks. More...
+ static final int GDAL_OF_HASHSET_BLOCK_ACCESS = 0x200;
+
+ // Name of driver metadata item for layer creation option list.
+ static final String GDAL_DS_LAYER_CREATIONOPTIONLIST = "DS_LAYER_CREATIONOPTIONLIST";
+
+ // Flag returned by GDALGetMaskFlags() to indicate that all pixels are valid.
+ static final int GMF_ALL_VALID = 0x01;
+
+ // Flag returned by GDALGetMaskFlags() to indicate that the mask band is valid for all bands.
+ static final int GMF_PER_DATASET = 0x02;
+
+ // Flag returned by GDALGetMaskFlags() to indicate that the mask band is an alpha band.
+ static final int GMF_ALPHA = 0x04;
+
+ // Flag returned by GDALGetMaskFlags() to indicate that the mask band is computed from nodata values.
+ static final int GMF_NODATA = 0x08;
+
+ // Flag returned by GDALGetDataCoverageStatus() when the driver does not implement GetDataCoverageStatus(). More...
+ static final int GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED = 0x01;
+
+ // Flag returned by GDALGetDataCoverageStatus() when there is (potentially) data in the queried window. More...
+ static final int GDAL_DATA_COVERAGE_STATUS_DATA = 0x02;
+
+ // Flag returned by GDALGetDataCoverageStatus() when there is nodata in the queried window. More...
+ static final int GDAL_DATA_COVERAGE_STATUS_EMPTY = 0x04;
+
+ /*
+ ///// FUNCTIONS
+ */
+
+ // Get data type size in bits. More...
+ int GDALGetDataTypeSize(GDALDataType eDataType) throws Throwable;
+
+ // Get data type size in bits. More...
+ int GDALGetDataTypeSizeBits(GDALDataType eDataType) throws Throwable;
+
+ // Get data type size in bytes. More...
+ int GDALGetDataTypeSizeBytes(GDALDataType eDataType) throws Throwable;
+
+ // Is data type complex? More...
+ int GDALDataTypeIsComplex(GDALDataType eDataType) throws Throwable;
+
+ // Is data type an integer? More...
+ int GDALDataTypeIsInteger(GDALDataType eDataType) throws Throwable;
+
+ // Is data type a float? More...
+ int GDALDataTypeIsFloating(GDALDataType eDataType) throws Throwable;
+
+ // Is data type signed? More...
+ int GDALDataTypeIsSigned(GDALDataType eDataType) throws Throwable;
+
+ // Get name of data type. More...
+ String GDALGetDataTypeName(GDALDataType eDataType) throws Throwable;
+
+ GDALDataType GDALGetDataTypeByName(String pszName) throws Throwable;
+
+ // Return the smallest data type that can fully express both input data types. More...
+ GDALDataType GDALDataTypeUnion(GDALDataType eType1, GDALDataType eType2) throws Throwable;
+
+ // Return the smallest data type that can fully express both the input data type and the input value.
+ GDALDataType GDALDataTypeUnionWithValue(GDALDataType eDT, double dValue, int bComplex) throws Throwable;
+
+ // Find the data type that matches the given number of bits, signedness, floating-pointness, and complexity.
+ GDALDataType GDALFindDataType(int nBits, int bSigned, int bFloating, int bComplex) throws Throwable;
+
+ // Find the data type that can fully express the input value and complexity.
+ GDALDataType GDALFindDataTypeForValue(double dValue, int bComplex) throws Throwable;
+
+ // Adjust a value to the output data type.
+ double GDALAdjustValueToDataType(GDALDataType eDT, double dfValue, MemorySegment pbClamped, MemorySegment pbRounded) throws Throwable;
+
+ // Return the base data type for the specified input.
+ GDALDataType GDALGetNonComplexDataType(GDALDataType eDataType) throws Throwable;
+
+ // Check if converting a value from one data type to another will result in loss of precision or information.
+ int GDALDataTypeIsConversionLossy(GDALDataType eTypeFrom, GDALDataType eTypeTo) throws Throwable;
+
+ // Get name of AsyncStatus data type.
+ String GDALGetAsyncStatusTypeName(GDALAsyncStatusType eAsyncStatusType) throws Throwable;
+
+ // Get AsyncStatusType by symbolic name.
+ GDALAsyncStatusType GDALGetAsyncStatusTypeByName(String pszName) throws Throwable;
+
+ // Get name of color interpretation.
+ String GDALGetColorInterpretationName(GDALColorInterp eInterp) throws Throwable;
+
+ // Get color interpretation by symbolic name.
+ GDALColorInterp GDALGetColorInterpretationByName(String pszName) throws Throwable;
+
+ // Get name of palette interpretation.
+ String GDALGetPaletteInterpretationName(GDALPaletteInterp eInterp) throws Throwable;
+
+ // Register all known configured GDAL drivers.
+ void GDALAllRegister() throws Throwable;
+
+ // Create a new dataset with this driver.
+ MemorySegment GDALCreate(MemorySegment hDriver, String pszFilename, int nXSize, int nYSize, int nBands, GDALDataType eBandType, MemorySegment papszOptions) throws Throwable;
+
+ // Create a copy of a dataset.
+ MemorySegment GDALCreateCopy(MemorySegment hDriver, String pszFilename, MemorySegment hSrcDS, int bStrict, MemorySegment papszOptions, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable;
+
+ // Identify the driver that can open a raster file.
+ MemorySegment GDALIdentifyDriver(String pszFilename, MemorySegment papszFileList) throws Throwable;
+
+ // Identify the driver that can open a raster file.
+ MemorySegment GDALIdentifyDriverEx(String pszFilename, int nIdentifyFlags, MemorySegment papszAllowedDrivers, MemorySegment papszFileList) throws Throwable;
+
+ // Open a raster file as a GDALDataset.
+ MemorySegment GDALOpen(String pszFilename, GDALAccess eAccess) throws Throwable;
+
+ // Open a raster file as a GDALDataset.
+ MemorySegment GDALOpenShared(String pszFilename, GDALAccess eAccess) throws Throwable;
+
+ // Open a raster or vector file as a GDALDataset.
+ MemorySegment GDALOpenEx(String pszFilename, int nOpenFlags, String[] papszAllowedDrivers, String[] papszOpenOptions, String[] papszSiblingFiles) throws Throwable;
+
+ // List open datasets.
+ int GDALDumpOpenDatasets(MemorySegment fp) throws Throwable;
+
+ // Fetch a driver based on the short name.
+ MemorySegment GDALGetDriverByName(String name) throws Throwable;
+
+ // Fetch the number of registered drivers.
+ int GDALGetDriverCount() throws Throwable;
+
+ // Fetch driver by index.
+ MemorySegment GDALGetDriver(int iDriver) throws Throwable;
+
+ // Create a new driver.
+ MemorySegment GDALCreateDriver() throws Throwable;
+
+ // Destroy a GDALDriver.
+ void GDALDestroyDriver(MemorySegment hDriver) throws Throwable;
+
+ // Register a driver for use.
+ int GDALRegisterDriver(MemorySegment hDriver) throws Throwable;
+
+ // Deregister the passed driver.
+ void GDALDeregisterDriver(MemorySegment hDriver) throws Throwable;
+
+ // Destroy the driver manager.
+ void GDALDestroyDriverManager() throws Throwable;
+
+ // Delete named dataset.
+ CPLErr GDALDeleteDataset(MemorySegment hDriver, String pszFilename) throws Throwable;
+
+ // Rename a dataset.
+ CPLErr GDALRenameDataset(MemorySegment hDriver, String pszNewName, String pszOldName) throws Throwable;
+
+ // Copy the files of a dataset.
+ CPLErr GDALCopyDatasetFiles(MemorySegment hDriver, String pszNewName, String pszOldName) throws Throwable;
+
+ // Validate the list of creation options that are handled by a driver.
+ int GDALValidateCreationOptions(MemorySegment hDriver, MemorySegment papszCreationOptions) throws Throwable;
+
+ // Return the short name of a driver.
+ String GDALGetDriverShortName(MemorySegment hDriver) throws Throwable;
+
+ // Return the long name of a driver.
+ String GDALGetDriverLongName(MemorySegment hDriver) throws Throwable;
+
+ // Return the URL to the help that describes the driver.
+ String GDALGetDriverHelpTopic(MemorySegment hDriver) throws Throwable;
+
+ // Return the list of creation options of the driver.
+ String GDALGetDriverCreationOptionList(MemorySegment hDriver) throws Throwable;
+
+ // Initialize an array of GCPs.
+ void GDALInitGCPs(int nCount, MemorySegment psGCP) throws Throwable;
+
+ // De-initialize an array of GCPs(initialized with GDALInitGCPs() throws Throwable;) throws Throwable;
+ void GDALDeinitGCPs(int nCount, MemorySegment psGCP) throws Throwable;
+
+ // Duplicate an array of GCPs.
+ MemorySegment GDALDuplicateGCPs(int nCount, MemorySegment pasGCPList) throws Throwable;
+
+ // Generate Geotransform from GCPs.
+ int GDALGCPsToGeoTransform(int nGCPCount, MemorySegment pasGCPs, MemorySegment padfGeoTransform, int bApproxOK) throws Throwable;
+
+ // Invert Geotransform.
+ int GDALInvGeoTransform(MemorySegment padfGeoTransformIn, MemorySegment padfInvGeoTransformOut) throws Throwable;
+
+ // Apply GeoTransform to x/y coordinate.
+ void GDALApplyGeoTransform(MemorySegment padfGeoTransform, double dfPixel, double dfLine, MemorySegment pdfGeoX, MemorySegment pdfGeoY) throws Throwable;
+
+ // Compose two geotransforms.
+ void GDALComposeGeoTransforms(MemorySegment padfGeoTransform1, MemorySegment padfGeoTransform2, MemorySegment padfGeoTransformOut) throws Throwable;
+
+ // Fetch list of metadata domains.
+ MemorySegment GDALGetMetadataDomainList(MemorySegment hObject) throws Throwable;
+
+ // Fetch metadata.
+ MemorySegment GDALGetMetadata(MemorySegment hObject, String pszDomain) throws Throwable;
+
+ // Set metadata.
+ CPLErr GDALSetMetadata(MemorySegment hObject, MemorySegment papszMD, String pszDomain) throws Throwable;
+
+ // Fetch single metadata item.
+ String GDALGetMetadataItem(MemorySegment hObject, String pszName, String pszDomain) throws Throwable;
+
+ // Set single metadata item.
+ CPLErr GDALSetMetadataItem(MemorySegment hObject, String pszName, String pszValue, String pszDomain) throws Throwable;
+
+ // Fetch object description.
+ String GDALGetDescription(MemorySegment hObject) throws Throwable;
+
+ // Set object description.
+ void GDALSetDescription(MemorySegment hObject, String pszNewDesc) throws Throwable;
+
+ // Fetch the driver to which this dataset relates.
+ MemorySegment GDALGetDatasetDriver(MemorySegment hDataset) throws Throwable;
+
+ // Fetch files forming dataset.
+ MemorySegment GDALGetFileList(MemorySegment hDS) throws Throwable;
+
+ // Close GDAL dataset.
+ CPLErr GDALClose(MemorySegment hDS) throws Throwable;
+
+ // Fetch raster width in pixels.
+ int GDALGetRasterXSize(MemorySegment hDataset) throws Throwable;
+
+ // Fetch raster height in pixels.
+ int GDALGetRasterYSize(MemorySegment hDataset) throws Throwable;
+
+ // Fetch number of raster bands on dataset.
+ int GDALGetRasterCount(MemorySegment hDataset) throws Throwable;
+
+ // Fetch a raster band by index.
+ MemorySegment GDALGetRasterBand(MemorySegment hDataset, int nBand) throws Throwable;
+
+ // Add a new band to a dataset.
+ CPLErr GDALAddBand(MemorySegment hDS, GDALDataType eType, MemorySegment papszOptions) throws Throwable;
+
+ // Begin asynchronous reading of a raster band.
+ MemorySegment GDALBeginAsyncReader(MemorySegment hDS, int nXOff, int nYOff, int nXSize, int nYSize, MemorySegment pBuf, int nBufXSize, int nBufYSize, GDALDataType eBufType, int nBandCount, MemorySegment panBandMap, int nPixelSpace, int nLineSpace, int nBandSpace, MemorySegment papszOptions) throws Throwable;
+
+ // End asynchronous reading of a raster band.
+ void GDALEndAsyncReader(MemorySegment hDS, MemorySegment hAsynchReaderH) throws Throwable;
+
+ // Read/write raster data.
+ CPLErr GDALDatasetRasterIO(MemorySegment hDS, GDALRWFlag eRWFlag, int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, MemorySegment pBuffer, int nBXSize, int nBYSize, GDALDataType eBDataType, int nBandCount, MemorySegment panBandCount, int nPixelSpace, int nLineSpace, int nBandSpace) throws Throwable;
+
+ // Read/write raster data.
+ CPLErr GDALDatasetRasterIOEx(MemorySegment hDS, GDALRWFlag eRWFlag, int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, MemorySegment pBuffer, int nBXSize, int nBYSize, GDALDataType eBDataType, int nBandCount, MemorySegment panBandCount, long nPixelSpace, long nLineSpace, long nBandSpace, MemorySegment psExtraArg) throws Throwable;
+
+ // Advise the driver of the next region of data that will be needed.
+ CPLErr GDALDatasetAdviseRead(MemorySegment hDS, int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, int nBXSize, int nBYSize, GDALDataType eBDataType, int nBandCount, MemorySegment panBandCount, MemorySegment papszOptions) throws Throwable;
+
+ // Fetch list of compression formats supported by the dataset.
+ MemorySegment GDALDatasetGetCompressionFormats(MemorySegment hDS, int nXOff, int nYOff, int nXSize, int nYSize, int nBandCount, MemorySegment panBandList) throws Throwable;
+
+ // Read compressed data from the dataset.
+ CPLErr GDALDatasetReadCompressedData(MemorySegment hDS, String pszFormat, int nXOff, int nYOff, int nXSize, int nYSize, int nBandCount, MemorySegment panBandList, MemorySegment ppBuffer, MemorySegment pnBufferSize, MemorySegment ppszDetailedFormat) throws Throwable;
+
+ // Fetch projection definition string for dataset.
+ String GDALGetProjectionRef(MemorySegment hDS) throws Throwable;
+
+ // Fetch spatial reference for dataset.
+ MemorySegment GDALGetSpatialRef(MemorySegment hDS) throws Throwable;
+
+ // Set projection definition string for dataset.
+ CPLErr GDALSetProjection(MemorySegment hDS, String pszProjection) throws Throwable;
+
+ // Set spatial reference for dataset.
+ CPLErr GDALSetSpatialRef(MemorySegment hDS, MemorySegment hSRS) throws Throwable;
+
+ // Fetch geotransform for raster band.
+ CPLErr GDALGetGeoTransform(MemorySegment hDS, MemorySegment padfGeoTransform) throws Throwable;
+
+ // Set geotransform for raster band.
+ CPLErr GDALSetGeoTransform(MemorySegment hDS, MemorySegment padfGeoTransform) throws Throwable;
+
+ // Fetch number of GCPs for dataset.
+ int GDALGetGCPCount(MemorySegment hDS) throws Throwable;
+
+ // Fetch projection definition string for GCPs.
+ String GDALGetGCPProjection(MemorySegment hDS) throws Throwable;
+
+ // Fetch spatial reference for GCPs.
+ MemorySegment GDALGetGCPSpatialRef(MemorySegment hDS) throws Throwable;
+
+ // Fetch list of GCPs for dataset.
+ MemorySegment GDALGetGCPs(MemorySegment hDS) throws Throwable;
+
+ // Set list of GCPs for dataset.
+ CPLErr GDALSetGCPs(MemorySegment hDS, int nGCPCount, MemorySegment pasGCPs, String pszGCPProjection) throws Throwable;
+
+ // Set list of GCPs for dataset.
+ CPLErr GDALSetGCPs2(MemorySegment hDS, int nGCPCount, MemorySegment pasGCPs, MemorySegment hSRS) throws Throwable;
+
+ // Fetch driver specific "handle" on dataset.
+ MemorySegment GDALGetInternalHandle(MemorySegment hDS, String pszHandleName) throws Throwable;
+
+ // Increase reference count on dataset.
+ int GDALReferenceDataset(MemorySegment hDS) throws Throwable;
+
+ // Decrease reference count on dataset.
+ int GDALDereferenceDataset(MemorySegment hDS) throws Throwable;
+
+ // Release dataset handle.
+ int GDALReleaseDataset(MemorySegment hDS) throws Throwable;
+
+ // Build overview images for dataset.
+ CPLErr GDALBuildOverviews(MemorySegment hDS, String pszResampling, int nOverviews, MemorySegment panOverviewList, int nListBands, MemorySegment panBandList, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable;
+
+ // Build overview images for dataset.
+ CPLErr GDALBuildOverviewsEx(MemorySegment hDS, String pszResampling, int nOverviews, MemorySegment panOverviewList, int nListBands, MemorySegment panBandList, MemorySegment pfnProgress, MemorySegment pProgressData, MemorySegment papszOptions) throws Throwable;
+
+ // Fetch list of open datasets.
+ void GDALGetOpenDatasets(MemorySegment phDS, MemorySegment pnCount) throws Throwable;
+
+ // Fetch access mode of dataset.
+ int GDALGetAccess(MemorySegment hDS) throws Throwable;
+
+ // Flush cached data to disk.
+ CPLErr GDALFlushCache(MemorySegment hDS) throws Throwable;
+
+ // Create a mask band for a dataset.
+ CPLErr GDALCreateDatasetMaskBand(MemorySegment hDS, int nFlags) throws Throwable;
+
+ // Copy whole raster data from one dataset to another.
+ CPLErr GDALDatasetCopyWholeRaster(MemorySegment hSrcDS, MemorySegment hDstDS, MemorySegment papszOptions, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable;
+
+ // Copy whole raster data from one band to another.
+ CPLErr GDALRasterBandCopyWholeRaster(MemorySegment hSrcBand, MemorySegment hDstBand, MemorySegment papszOptions, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable;
+
+ // Regenerate overview images for a raster band.
+ CPLErr GDALRegenerateOverviews(MemorySegment hSrcBand, int nOverviewCount, MemorySegment pahOverviewBands, String pszResampling, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable;
+
+ // Regenerate overview images for a raster band.
+ CPLErr GDALRegenerateOverviewsEx(MemorySegment hSrcBand, int nOverviewCount, MemorySegment pahOverviewBands, String pszResampling, MemorySegment pfnProgress, MemorySegment pProgressData, MemorySegment papszOptions) throws Throwable;
+
+ // Fetch number of layers on dataset.
+ int GDALDatasetGetLayerCount(MemorySegment hDS) throws Throwable;
+
+ // Fetch a layer by index.
+ MemorySegment GDALDatasetGetLayer(MemorySegment hDS, int iLayer) throws Throwable;
+
+ // Fetch a layer by name.
+ MemorySegment GDALDatasetGetLayerByName(MemorySegment hDS, String pszName) throws Throwable;
+
+ // Check if a layer is private.
+ int GDALDatasetIsLayerPrivate(MemorySegment hDS, int iLayer) throws Throwable;
+
+ // Delete a layer from the dataset.
+ OGRErr GDALDatasetDeleteLayer(MemorySegment hDS, int iLayer) throws Throwable;
+
+ // Create a new layer on the dataset.
+ MemorySegment GDALDatasetCreateLayer(MemorySegment hDS, String pszName, MemorySegment hSRS, MemorySegment eType, MemorySegment papszOptions) throws Throwable;
+
+ // Copy a layer from one dataset to another.
+ MemorySegment GDALDatasetCopyLayer(MemorySegment hDS, MemorySegment hSrcLayer, String pszNewName, MemorySegment papszOptions) throws Throwable;
+
+ // Reset reading of dataset.
+ void GDALDatasetResetReading(MemorySegment hDS) throws Throwable;
+
+ // Fetch next feature from dataset.
+ MemorySegment GDALDatasetGetNextFeature(MemorySegment hDS, MemorySegment phBelongingLayer, MemorySegment pdfProgressPct, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable;
+
+ // Test if dataset has a capability.
+ int GDALDatasetTestCapability(MemorySegment hDS, String pszCapability) throws Throwable;
+
+ // Execute an SQL statement on the dataset.
+ MemorySegment GDALDatasetExecuteSQL(MemorySegment hDS, String pszSQL, MemorySegment hSpatialFilter, String pszDialect) throws Throwable;
+
+ // Abort an SQL statement on the dataset.
+ OGRErr GDALDatasetAbortSQL(MemorySegment hDS) throws Throwable;
+
+ // Release a result set from an SQL statement on the dataset.
+ void GDALDatasetReleaseResultSet(MemorySegment hDS, MemorySegment hLayer) throws Throwable;
+
+ // Fetch style table for dataset.
+ MemorySegment GDALDatasetGetStyleTable(MemorySegment hDS) throws Throwable;
+
+ // Set style table for dataset.
+ void GDALDatasetSetStyleTableDirectly(MemorySegment hDS, MemorySegment hStyleTable) throws Throwable;
+
+ // Set style table for dataset.
+ void GDALDatasetSetStyleTable(MemorySegment hDS, MemorySegment hStyleTable) throws Throwable;
+
+ // Start a transaction on the dataset.
+ OGRErr GDALDatasetStartTransaction(MemorySegment hDS, int bForce) throws Throwable;
+
+ // Commit a transaction on the dataset.
+ OGRErr GDALDatasetCommitTransaction(MemorySegment hDS) throws Throwable;
+
+ // Rollback a transaction on the dataset.
+ OGRErr GDALDatasetRollbackTransaction(MemorySegment hDS) throws Throwable;
+
+ // Clear statistics cache on the dataset.
+ void GDALDatasetClearStatistics(MemorySegment hDS) throws Throwable;
+
+ // Fetch list of field domain names for the dataset.
+ MemorySegment GDALDatasetGetFieldDomainNames(MemorySegment hDS, MemorySegment papszOptions) throws Throwable;
+
+ // Fetch a field domain by name.
+ MemorySegment GDALDatasetGetFieldDomain(MemorySegment hDS, String pszName) throws Throwable;
+
+ // Add a field domain to the dataset.
+ boolean GDALDatasetAddFieldDomain(MemorySegment hDS, MemorySegment hFieldDomain, MemorySegment ppszFailureReason) throws Throwable;
+
+ // Delete a field domain from the dataset.
+ boolean GDALDatasetDeleteFieldDomain(MemorySegment hDS, String pszName, MemorySegment ppszFailureReason) throws Throwable;
+
+ // Update a field domain on the dataset.
+ boolean GDALDatasetUpdateFieldDomain(MemorySegment hDS, MemorySegment hFieldDomain, MemorySegment ppszFailureReason) throws Throwable;
+
+ // Fetch list of relationship names for the dataset.
+ MemorySegment GDALDatasetGetRelationshipNames(MemorySegment hDS, MemorySegment papszOptions) throws Throwable;
+
+ // Fetch a relationship by name.
+ MemorySegment GDALDatasetGetRelationship(MemorySegment hDS, String pszName) throws Throwable;
+
+ // Add a relationship to the dataset.
+ boolean GDALDatasetAddRelationship(MemorySegment hDS, MemorySegment hRelationship, MemorySegment ppszFailureReason) throws Throwable;
+
+ // Delete a relationship from the dataset.
+ boolean GDALDatasetDeleteRelationship(MemorySegment hDS, String pszName, MemorySegment ppszFailureReason) throws Throwable;
+
+ // Update a relationship on the dataset.
+ boolean GDALDatasetUpdateRelationship(MemorySegment hDS, MemorySegment hRelationship, MemorySegment ppszFailureReason) throws Throwable;
+
+ // Fetch data type of raster band.
+ GDALDataType GDALGetRasterDataType(MemorySegment hRBand) throws Throwable;
+
+ // Fetch block size of raster band.
+ void GDALGetBlockSize(MemorySegment hRBand, MemorySegment pnXSize, MemorySegment pnYSize) throws Throwable;
+
+ // Fetch actual block size of raster band.
+ CPLErr GDALGetActualBlockSize(MemorySegment hRBand, int nXBlockOff, int nYBlockOff, MemorySegment pnXValid, MemorySegment pnYValid) throws Throwable;
+
+ // Advise the driver of the next region of data that will be needed.
+ CPLErr GDALRasterAdviseRead(MemorySegment hRB, int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, int nBXSize, int nBYSize, GDALDataType eBDataType, MemorySegment papszOptions) throws Throwable;
+
+ // Read/write raster data.
+ CPLErr GDALRasterIO(MemorySegment hRBand, GDALRWFlag eRWFlag, int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, MemorySegment pBuffer, int nBXSize, int nBYSize, GDALDataType eBDataType, int nPixelSpace, int nLineSpace) throws Throwable;
+
+ // Read/write raster data.
+ CPLErr GDALRasterIOEx(MemorySegment hRBand, GDALRWFlag eRWFlag, int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, MemorySegment pBuffer, int nBXSize, int nBYSize, GDALDataType eBDataType, long nPixelSpace, long nLineSpace, MemorySegment psExtraArg) throws Throwable;
+
+ // Read a block of data from a raster band.
+ CPLErr GDALReadBlock(MemorySegment hRBand, int nBlockXOff, int nBlockYOff, MemorySegment pData) throws Throwable;
+
+ // Write a block of data to a raster band.
+ CPLErr GDALWriteBlock(MemorySegment hRBand, int nBlockXOff, int nBlockYOff, MemorySegment pData) throws Throwable;
+
+ // Fetch raster width in pixels.
+ int GDALGetRasterBandXSize(MemorySegment hRBand) throws Throwable;
+
+ // Fetch raster height in pixels.
+ int GDALGetRasterBandYSize(MemorySegment hRBand) throws Throwable;
+
+ // Fetch access mode of raster band.
+ GDALAccess GDALGetRasterAccess(MemorySegment hRBand) throws Throwable;
+
+ // Fetch band index for this band.
+ int GDALGetBandNumber(MemorySegment hRBand) throws Throwable;
+
+ // Fetch dataset for this band.
+ MemorySegment GDALGetBandDataset(MemorySegment hRBand) throws Throwable;
+
+ // Fetch color interpretation of raster band.
+ GDALColorInterp GDALGetRasterColorInterpretation(MemorySegment hRBand) throws Throwable;
+
+ // Set color interpretation of raster band.
+ CPLErr GDALSetRasterColorInterpretation(MemorySegment hRBand, GDALColorInterp eColorInterp) throws Throwable;
+
+ // Fetch color table of raster band.
+ MemorySegment GDALGetRasterColorTable(MemorySegment hRBand) throws Throwable;
+
+ // Set color table of raster band.
+ CPLErr GDALSetRasterColorTable(MemorySegment hRBand, MemorySegment hColorTable) throws Throwable;
+
+ // Check if raster band has arbitrary overviews.
+ int GDALHasArbitraryOverviews(MemorySegment hRBand) throws Throwable;
+
+ // Fetch number of overviews for this band.
+ int GDALGetOverviewCount(MemorySegment hRBand) throws Throwable;
+
+ // Fetch overview band.
+ MemorySegment GDALGetOverview(MemorySegment hRBand, int iOverview) throws Throwable;
+
+ // Fetch no data value for raster band.
+ double GDALGetRasterNoDataValue(MemorySegment hRBand, MemorySegment pbSuccess) throws Throwable;
+
+ // Fetch no data value for raster band as an integer.
+ long GDALGetRasterNoDataValueAsInt64(MemorySegment hRBand, MemorySegment pbSuccess) throws Throwable;
+
+ // Fetch no data value for raster band as an unsigned integer.
+ long GDALGetRasterNoDataValueAsUInt64(MemorySegment hRBand, MemorySegment pbSuccess) throws Throwable;
+
+ // Set no data value for raster band.
+ CPLErr GDALSetRasterNoDataValue(MemorySegment hRBand, double dfNoDataValue) throws Throwable;
+
+ // Set no data value for raster band as an integer.
+ CPLErr GDALSetRasterNoDataValueAsInt64(MemorySegment hRBand, long nNoDataValue) throws Throwable;
+
+ // Set no data value for raster band as an unsigned integer.
+ CPLErr GDALSetRasterNoDataValueAsUInt64(MemorySegment hRBand, long nNoDataValue) throws Throwable;
+
+ // Delete no data value for raster band.
+ CPLErr GDALDeleteRasterNoDataValue(MemorySegment hRBand) throws Throwable;
+
+ // Fetch category names for raster band.
+ MemorySegment GDALGetRasterCategoryNames(MemorySegment hRBand) throws Throwable;
+
+ // Set category names for raster band.
+ CPLErr GDALSetRasterCategoryNames(MemorySegment hRBand, MemorySegment papszCategoryNames) throws Throwable;
+
+ // Fetch minimum value for raster band.
+ double GDALGetRasterMinimum(MemorySegment hRBand, MemorySegment pbSuccess) throws Throwable;
+
+ // Fetch maximum value for raster band.
+ double GDALGetRasterMaximum(MemorySegment hRBand, MemorySegment pbSuccess) throws Throwable;
+
+ // Fetch statistics for raster band.
+ CPLErr GDALGetRasterStatistics(MemorySegment hRBand, int bApproxOK, int bForce, MemorySegment pdfMin, MemorySegment pdfMax, MemorySegment pdfMean, MemorySegment pdfStdDev) throws Throwable;
+
+ // Compute statistics for raster band.
+ CPLErr GDALComputeRasterStatistics(MemorySegment hRBand, int bApproxOK, MemorySegment pdfMin, MemorySegment pdfMax, MemorySegment pdfMean, MemorySegment pdfStdDev, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable;
+
+ // Set statistics for raster band.
+ CPLErr GDALSetRasterStatistics(MemorySegment hBand, double dfMin, double dfMax, double dfMean, double dfStdDev) throws Throwable;
+
+ // Fetch raster band as a multi-dimensional array.
+ MemorySegment GDALRasterBandAsMDArray(MemorySegment hRBand) throws Throwable;
+
+ // Fetch unit type for raster band.
+ String GDALGetRasterUnitType(MemorySegment hRBand) throws Throwable;
+
+ // Set unit type for raster band.
+ CPLErr GDALSetRasterUnitType(MemorySegment hBand, String pszNewValue) throws Throwable;
+
+ // Fetch offset for raster band.
+ double GDALGetRasterOffset(MemorySegment hRBand, MemorySegment pbSuccess) throws Throwable;
+
+ // Set offset for raster band.
+ CPLErr GDALSetRasterOffset(MemorySegment hBand, double dfNewOffset) throws Throwable;
+
+ // Fetch scale for raster band.
+ double GDALGetRasterScale(MemorySegment hRBand, MemorySegment pbSuccess) throws Throwable;
+
+ // Set scale for raster band.
+ CPLErr GDALSetRasterScale(MemorySegment hBand, double dfNewOffset) throws Throwable;
+
+ // Compute minimum and maximum values for raster band.
+ CPLErr GDALComputeRasterMinMax(MemorySegment hBand, int bApproxOK, MemorySegment adfMinMax) throws Throwable;
+
+ // Flush cached data to disk.
+ CPLErr GDALFlushRasterCache(MemorySegment hBand) throws Throwable;
+
+ // Fetch histogram for raster band.
+ CPLErr GDALGetRasterHistogram(MemorySegment hBand, double dfMin, double dfMax, int nBuckets, MemorySegment panHistogram, int bIncludeOutOfRange, int bApproxOK, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable;
+
+ // Fetch histogram for raster band.
+ CPLErr GDALGetRasterHistogramEx(MemorySegment hBand, double dfMin, double dfMax, int nBuckets, MemorySegment panHistogram, int bIncludeOutOfRange, int bApproxOK, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable;
+
+ // Fetch default histogram for raster band.
+ CPLErr GDALGetDefaultHistogramEx(MemorySegment hBand, MemorySegment pdfMin, MemorySegment pdfMax, MemorySegment pnBuckets, MemorySegment ppanHistogram, int bForce, MemorySegment pfnProgress, MemorySegment pProgressData) throws Throwable;
+
+ // Set default histogram for raster band.
+ CPLErr GDALSetDefaultHistogramEx(MemorySegment hBand, double dfMin, double dfMax, int nBuckets, MemorySegment panHistogram) throws Throwable;
+
+ // Fetch raster sample overview.
+ long GDALGetRasterSampleOverview(long hBand, int iOverview) throws Throwable;
+
+ // Fetch raster sample overview with extended index.
+ long GDALGetRasterSampleOverviewEx(long hBand, long nOverviewIndex) throws Throwable;
+
+ // Fill raster with a value.
+ int GDALFillRaster(long hBand, double dfRealValue, double dfImaginaryValue) throws Throwable;
+
+ // Compute band statistics.
+ int GDALComputeBandStats(long hBand, int nSampleStep, double[] pdfMean, double[] pdfStdDev, MemorySegment pfnProgress, long pProgressData) throws Throwable;
+
+ // Overview magnitude correction.
+ int GDALOverviewMagnitudeCorrection(long hBaseBand, int nOverviewCount, long[] pahOverviews, MemorySegment pfnProgress, long pProgressData) throws Throwable;
+
+ // Fetch default raster attribute table.
+ long GDALGetDefaultRAT(long hBand) throws Throwable;
+
+ // Set default raster attribute table.
+ int GDALSetDefaultRAT(long hBand, long hRAT) throws Throwable;
+
+ // Add a derived band pixel function.
+ int GDALAddDerivedBandPixelFunc(String pszName, MemorySegment pfnPixelFunc) throws Throwable;
+
+ // Add a derived band pixel function with arguments.
+ int GDALAddDerivedBandPixelFuncWithArgs(String pszName, MemorySegment pfnPixelFunc, String pszMetadata) throws Throwable;
+
+ // Fetch mask band.
+ long GDALGetMaskBand(long hBand) throws Throwable;
+
+ // Fetch mask flags.
+ int GDALGetMaskFlags(long hBand) throws Throwable;
+
+ // Create mask band.
+ int GDALCreateMaskBand(long hBand, int nFlags) throws Throwable;
+
+ // Check if band is a mask band.
+ boolean GDALIsMaskBand(long hBand) throws Throwable;
+
+ // Get data coverage status.
+ int GDALGetDataCoverageStatus(long hBand, int nXOff, int nYOff, int nXSize, int nYSize, int nMaskFlagStop, double[] pdfDataPct) throws Throwable;
+
+ // Get next updated region.
+ int GDALARGetNextUpdatedRegion(long hARIO, double dfTimeout, int[] pnXBufOff, int[] pnYBufOff, int[] pnXBufSize, int[] pnYBufSize) throws Throwable;
+
+ // Lock buffer.
+ int GDALARLockBuffer(long hARIO, double dfTimeout) throws Throwable;
+
+ // Unlock buffer.
+ void GDALARUnlockBuffer(long hARIO) throws Throwable;
+
+ // General command line processor.
+ int GDALGeneralCmdLineProcessor(int nArgc, String[][] ppapszArgv, int nOptions) throws Throwable;
+
+ // Swap words.
+ void GDALSwapWords(MemorySegment pData, int nWordSize, int nWordCount, int nWordSkip) throws Throwable;
+
+ // Swap words with extended word count.
+ void GDALSwapWordsEx(MemorySegment pData, int nWordSize, long nWordCount, int nWordSkip) throws Throwable;
+
+ // Copy words.
+ void GDALCopyWords(MemorySegment pSrcData, int eSrcType, int nSrcPixelOffset, MemorySegment pDstData, int eDstType, int nDstPixelOffset, int nWordCount) throws Throwable;
+
+ // Copy words with extended word count.
+ void GDALCopyWords64(MemorySegment pSrcData, int eSrcType, int nSrcPixelOffset, MemorySegment pDstData, int eDstType, int nDstPixelOffset, long nWordCount) throws Throwable;
+
+ // Copy bits.
+ void GDALCopyBits(MemorySegment pabySrcData, int nSrcOffset, int nSrcStep, MemorySegment pabyDstData, int nDstOffset, int nDstStep, int nBitCount, int nStepCount) throws Throwable;
+
+ // Deinterleave.
+ void GDALDeinterleave(MemorySegment pSourceBuffer, int eSourceDT, int nComponents, MemorySegment[] ppDestBuffer, int eDestDT, long nIters) throws Throwable;
+
+ // Load world file.
+ int GDALLoadWorldFile(MemorySegment filename, double[] geotransform) throws Throwable;
+
+ // Read world file.
+ int GDALReadWorldFile(MemorySegment filename, MemorySegment extension, double[] geotransform) throws Throwable;
+
+ // Write world file.
+ int GDALWriteWorldFile(MemorySegment filename, MemorySegment extension, double[] geotransform) throws Throwable;
+
+ // Load tab file.
+ int GDALLoadTabFile(MemorySegment filename, double[] geotransform, MemorySegment[] ppszProjection, int[] pnGCPCount, MemorySegment[] ppasGCPList) throws Throwable;
+
+ // Read tab file.
+ int GDALReadTabFile(MemorySegment filename, double[] geotransform, MemorySegment[] ppszProjection, int[] pnGCPCount, MemorySegment[] ppasGCPList) throws Throwable;
+
+ // Load Ozi map file.
+ int GDALLoadOziMapFile(MemorySegment filename, double[] geotransform, MemorySegment[] ppszProjection, int[] pnGCPCount, MemorySegment[] ppasGCPList) throws Throwable;
+
+ // Read Ozi map file.
+ int GDALReadOziMapFile(MemorySegment filename, double[] geotransform, MemorySegment[] ppszProjection, int[] pnGCPCount, MemorySegment[] ppasGCPList) throws Throwable;
+
+ // Decimal to DMS.
+ MemorySegment GDALDecToDMS(double dfAngle, MemorySegment pszAxis, int nPrecision) throws Throwable;
+
+ // Packed DMS to decimal.
+ double GDALPackedDMSToDec(double dfAngle) throws Throwable;
+
+ // Decimal to packed DMS.
+ double GDALDecToPackedDMS(double dfAngle) throws Throwable;
+
+ // Create color table.
+ long GDALCreateColorTable(int eInterp) throws Throwable;
+
+ // Destroy color table.
+ void GDALDestroyColorTable(long hTable) throws Throwable;
+
+ // Clone color table.
+ long GDALCloneColorTable(long hSrcTable) throws Throwable;
+
+ // Get palette interpretation.
+ int GDALGetPaletteInterpretation(long hTable) throws Throwable;
+
+ // Get color entry count.
+ int GDALGetColorEntryCount(MemorySegment hTable) throws Throwable;
+
+ // Get color entry.
+ GDALColorEntry GDALGetColorEntry(long hTable, int i) throws Throwable;
+
+ // Get color entry as RGB.
+ int GDALGetColorEntryAsRGB(MemorySegment hTable, int i, GDALColorEntry sColorEntry) throws Throwable;
+
+ // Set color entry.
+ void GDALSetColorEntry(long hTable, int i, GDALColorEntry sColorEntry) throws Throwable;
+
+ // Create color ramp.
+ void GDALCreateColorRamp(long hTable, int nStartIndex, GDALColorEntry psStartColor, int nEndIndex, GDALColorEntry psEndColor) throws Throwable;
+
+ // Try to flush one cached raster block. More...
+ int GDALFlushCacheBlock() throws Throwable;
+
+ // Gdal version info
+ String GDALVersionInfo(String pszRequest) throws Throwable;
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/OGRErr.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/OGRErr.java
new file mode 100644
index 0000000000..bf08422775
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/OGRErr.java
@@ -0,0 +1,33 @@
+package org.apache.sis.storage.gdal.internal.panama;
+
+public enum OGRErr {
+ OGRERR_NONE(0),
+ OGRERR_NOT_ENOUGH_DATA(1),
+ OGRERR_NOT_ENOUGH_MEMORY(2),
+ OGRERR_UNSUPPORTED_GEOMETRY_TYPE(3),
+ OGRERR_UNSUPPORTED_OPERATION(4),
+ OGRERR_CORRUPT_DATA(5),
+ OGRERR_FAILURE(6),
+ OGRERR_UNSUPPORTED_SRS(7),
+ OGRERR_INVALID_HANDLE(8),
+ OGRERR_NON_EXISTING_FEATURE(9);
+
+ private final int value;
+
+ OGRErr(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public static OGRErr fromValue(int value) {
+ for (OGRErr err : values()) {
+ if (err.getValue() == value) {
+ return err;
+ }
+ }
+ throw new IllegalArgumentException("Invalid OGRErr value: " + value);
+ }
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/PanamaUtils.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/PanamaUtils.java
new file mode 100644
index 0000000000..7bf9cbe7c9
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/internal/panama/PanamaUtils.java
@@ -0,0 +1,93 @@
+package org.apache.sis.storage.gdal.internal.panama;
+
+import java.lang.foreign.Arena;
+import java.lang.foreign.Linker;
+import java.lang.foreign.MemoryLayout;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.SymbolLookup;
+import java.lang.foreign.ValueLayout;
+
+/**
+ * Utility class for working with Java Panama and the GDAL library.
+ *
+ * @author Quentin BIALOTA (Geomatys)
+ */
+public class PanamaUtils {
+
+ private static final Arena arena = Arena.global();
+
+ private static final String GDAL_LINUX = "libgdal.so";
+ private static final String GDAL_WINDOWS = "gdal.dll";
+
+ /**
+ * Private constructor (as it's en utility class)
+ */
+ private PanamaUtils() {}
+
+ /**
+ * Searches for a symbol in the GDAL library and returns a MemorySegment that points to it.
+ *
+ * @param symbol the symbol to search for
+ * @return a MemorySegment that points to the symbol, or null if the symbol was not found
+ */
+ public static MemorySegment lookupInGdal(String symbol) {
+ try {
+ String gdalLibName = switch (System.getProperty("os.name")) {
+ case "Linux" -> GDAL_LINUX;
+ case "Windows" -> GDAL_WINDOWS;
+ default -> throw new AssertionError("OS not recognized: " + System.getProperty("os.name"));
+ };
+
+ return SymbolLookup.libraryLookup(gdalLibName, arena).find(symbol).orElseThrow();
+ } catch (Exception ex) {
+ return null;
+ }
+ }
+
+ /**
+ * Searches for a symbol in the default lookup, the loader lookup, and the GDAL library lookup,
+ * and returns a MemorySegment that points to it.
+ *
+ * @param symbol the symbol to search for
+ * @return a MemorySegment that points to the symbol
+ */
+ public static MemorySegment lookup(String symbol) {
+ return Linker.nativeLinker().defaultLookup().find(symbol)
+ .or(() -> SymbolLookup.loaderLookup().find(symbol))
+ .or(() -> java.util.Optional.ofNullable(lookupInGdal(symbol)))
+ .orElseThrow();
+ }
+
+ /**
+ * Allocates memory for an array of strings and copies the strings into the memory segment.
+ *
+ * @param strings the array of strings to allocate memory for
+ * @param arena the Arena to allocate memory from
+ * @return a MemorySegment that points to the array of strings, or MemorySegment.NULL if the array is empty
+ */
+ public static MemorySegment allocateStringArray(String[] strings, Arena arena) {
+ if (strings.length == 0) {
+ return MemorySegment.NULL;
+ }
+
+ int maxLength = 0;
+ for (String s : strings) {
+ int length = s.length();
+ if (length > maxLength) {
+ maxLength = length;
+ }
+ }
+
+ MemoryLayout stringArrayLayout = MemoryLayout.sequenceLayout(strings.length,
+ MemoryLayout.sequenceLayout(maxLength, ValueLayout.JAVA_CHAR));
+
+ MemorySegment stringArray = arena.allocate(stringArrayLayout);
+
+ for (int i = 0; i < strings.length; i++) {
+ MemorySegment stringSegment = stringArray.asSlice((long) i * maxLength, maxLength);
+ stringSegment.setString(0, strings[i]);
+ }
+
+ return stringArray;
+ }
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/package-info.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/package-info.java
new file mode 100644
index 0000000000..f30f4ff4dd
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/package-info.java
@@ -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.
+ * 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.
+ */
+
+/**
+ * Read and write files using the GDAL library.
+ */
+package org.apache.sis.storage.gdal;
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
new file mode 100644
index 0000000000..048e20c370
--- /dev/null
+++ b/incubator/src/org.apache.sis.storage.gdal/test/org/apache/sis/storage/gdal/GdalStoreTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.storage.gdal;
+
+import org.apache.sis.coverage.SampleDimension;
+import org.apache.sis.coverage.grid.GridCoverage;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.coverage.grid.PixelInCell;
+import org.apache.sis.measure.Units;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.privy.AffineTransform2D;
+import org.apache.sis.storage.*;
+import org.apache.sis.storage.gdal.coverage.GdalProvider;
+import org.apache.sis.storage.gdal.coverage.GdalResource;
+import org.apache.sis.storage.gdal.coverage.GdalStore;
+import org.apache.sis.storage.gdal.internal.GdalOgr;
+import org.apache.sis.util.Utilities;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.opengis.referencing.operation.MathTransform1D;
+import org.opengis.util.FactoryException;
+import org.opengis.util.GenericName;
+
+import java.awt.image.DataBuffer;
+import java.awt.image.RenderedImage;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Iterator;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+/**
+ * Test class for GdalStore
+ *
+ * @author Johann Sorel (Geomatys)
+ * @author Quentin BIALOTA (Geomatys)
+ */
+public final class GdalStoreTest {
+
+ private static final double DELTA = 1e-6;
+
+ /**
+ * Check Gdal is available.
+ */
+ @BeforeAll
+ public static void beforeMethod() {
+ try {
+ GdalOgr.init();
+ } catch (DataStoreException ex) {
+ ex.printStackTrace();
+ assumeTrue(false);
+
+ }
+ }
+
+ /**
+ * Test factory is correctly registered.
+ */
+ @Test
+ public void testFactory() {
+
+ final Iterator<DataStoreProvider> ite = DataStores.providers().iterator();
+
+ boolean found = false;
+ while (ite.hasNext()){
+ if(ite.next() instanceof GdalProvider){
+ found = true;
+ }
+ }
+
+ if(!found){
+ fail("Factory not found");
+ }
+ }
+
+ /**
+ * Read indexed image test
+ * Small image with 1 band, color palette
+ */
+ @Test
+ public void readTest1() throws URISyntaxException, IOException, DataStoreException, FactoryException {
+
+ //check store
+ final GdalStore store = new GdalStore(GdalStoreTest.class.getResource("test.tiff").toURI());
+ assertEquals(1, store.components().size());
+ final Resource r = store.components().iterator().next();
+ final GenericName name = ((GdalResource)r).getIdentifier().get();
+ assertEquals("test", name.tip().toString());
+ final GridCoverageResource ref = (GridCoverageResource) store.findResource(name.toString());
+
+ //check reader
+ final GridCoverage coverage = ref.read(null);
+ assertTrue(Utilities.equalsIgnoreMetadata(CRS.forCode("EPSG:32617"),
+ coverage.getCoordinateReferenceSystem()));
+
+ //check grid geometry
+ final GridGeometry gridGeometry = coverage.getGridGeometry();
+ assertEquals(2, gridGeometry.getExtent().getDimension());
+ assertEquals(120, gridGeometry.getExtent().getSize(0));
+ assertEquals(75, gridGeometry.getExtent().getSize(1));
+ assertTrue(gridGeometry.getGridToCRS(PixelInCell.CELL_CENTER) instanceof AffineTransform2D);
+ final AffineTransform2D gridToCrs = (AffineTransform2D) gridGeometry.getGridToCRS(PixelInCell.CELL_CENTER);
+ assertEquals(664769.191709, gridToCrs.getTranslateX(), DELTA);
+ assertEquals(4600950.488848333, gridToCrs.getTranslateY(), DELTA);
+ assertEquals(839.977999999, gridToCrs.getScaleX(), DELTA);
+ assertEquals(-846.395733333, gridToCrs.getScaleY(), DELTA);
+ assertEquals(0.0, gridToCrs.getShearX(), DELTA);
+ assertEquals(0.0, gridToCrs.getShearY(), DELTA);
+
+ //check sample dimension
+ assertEquals(1, coverage.getSampleDimensions().size());
+ final SampleDimension dimension = coverage.getSampleDimensions().get(0);
+ MathTransform1D trs = dimension.getTransferFunction().get();
+ //assertEquals(1.0, dimension.getScale(),DELTA);
+ //assertEquals(0.0, dimension.getOffset(),DELTA);
+ assertEquals(Units.UNITY, dimension.getUnits().get());
+
+ //check image
+ final RenderedImage img = coverage.render(null);
+ assertEquals(120, img.getWidth());
+ assertEquals(75, img.getHeight());
+ assertTrue(img.getSampleModel().getNumBands() == 1);
+ assertEquals(DataBuffer.TYPE_BYTE, img.getData().getDataBuffer().getDataType());
+ assertEquals(9000, img.getData().getDataBuffer().getSize());
+ assertEquals(5, img.getData().getDataBuffer().getElem(2000)); //Test with one value to check the buffer
+ }
+}
diff --git a/incubator/src/org.apache.sis.storage.gdal/test/org/apache/sis/storage/gdal/test.tiff b/incubator/src/org.apache.sis.storage.gdal/test/org/apache/sis/storage/gdal/test.tiff
new file mode 100644
index 0000000000..7c6d960734
Binary files /dev/null and b/incubator/src/org.apache.sis.storage.gdal/test/org/apache/sis/storage/gdal/test.tiff differ
diff --git a/netbeans-project/nbproject/project.properties b/netbeans-project/nbproject/project.properties
index b157430750..84fdb90e9b 100644
--- a/netbeans-project/nbproject/project.properties
+++ b/netbeans-project/nbproject/project.properties
@@ -94,6 +94,7 @@ read.options = --add-reads org.apache.sis.cloud.aws=org.junit.jupiter.api,junit
--add-reads org.apache.sis.storage=org.junit.jupiter.api,junit \
--add-reads org.apache.sis.storage.gimi=org.junit.jupiter.api,junit \
--add-reads org.apache.sis.storage.gsf=org.junit.jupiter.api,junit \
+ --add-reads org.apache.sis.storage.gdal=org.junit.jupiter.api,junit \
--add-reads org.apache.sis.storage.geopackage=org.junit.jupiter.api,junit \
--add-reads org.apache.sis.storage.coveragejson=org.junit.jupiter.api,junit \
--add-reads org.apache.sis.storage.earthobservation=org.junit.jupiter.api,junit \