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

jsorel 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 3953075dd2 feat(NetCDF): improve moving features read performances, 
fix moving feature time property single value for features which are not 
trajectories
3953075dd2 is described below

commit 3953075dd25451aff4ba2b88543ae49fd03d7f34
Author: jsorel <[email protected]>
AuthorDate: Wed Feb 18 15:25:15 2026 +0100

    feat(NetCDF): improve moving features read performances, fix moving feature 
time property single value for features which are not trajectories
---
 .../feature/internal/shared/MovingFeatures.java    | 28 +++++++++++++++++--
 .../apache/sis/storage/netcdf/base/FeatureSet.java | 32 ++++++++++++++++------
 2 files changed, 50 insertions(+), 10 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/shared/MovingFeatures.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/shared/MovingFeatures.java
index 890e9518ba..a78a34c543 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/shared/MovingFeatures.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/shared/MovingFeatures.java
@@ -99,9 +99,9 @@ public class MovingFeatures {
     }
 
     /**
-     * Sets the "datetimes" characteristic on the given attribute.
+     * Sets the "datetimes" characteristic on the given attribute to a 
sequence of temporal values.
      * If the {@code converter} is non-null, it will be used for converting 
values to {@link Instant} instances.
-     * Otherwise values are stored as-is as time elapsed in arbitrary units 
since an arbitrary epoch.
+     * Otherwise, values are stored as-is as time elapsed in arbitrary units 
since an arbitrary epoch.
      *
      * <p>Values should be in chronological order, but this is not verified.
      * Current implementation does not cache the values, but this policy may 
be revisited in a future version.</p>
@@ -125,4 +125,28 @@ public class MovingFeatures {
         }
         dest.characteristics().values().add(ct);
     }
+
+    /**
+     * Sets the "datetimes" characteristic on the given attribute to a single 
temporal value.
+     * If the {@code converter} is non-null, it will be used for converting 
the value to an {@link Instant} instance.
+     * Otherwise, the value is stored as-is as time elapsed in arbitrary units 
since an arbitrary epoch.
+     *
+     * @param  dest       the attribute on which to set time characteristic.
+     * @param  value      time in arbitrary units since an arbitrary epoch.
+     * @param  converter  the CRS to use for converting values to {@link 
Instant} instances, or {@code null}.
+     */
+    public static void setTime(final Attribute<?> dest, final double value, 
final DefaultTemporalCRS converter) {
+        final Attribute<?> ct;
+        if (converter != null) {
+            final Instant instant = converter.toInstant(value);
+            final Attribute<Instant> c = TIME_AS_INSTANTS.newInstance();
+            c.setValue(instant);
+            ct = c;
+        } else {
+            final Attribute<Number> c = TIME_AS_NUMBERS.newInstance();
+            c.setValue(value);
+            ct = c;
+        }
+        dest.characteristics().values().add(ct);
+    }
 }
diff --git 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/FeatureSet.java
 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/FeatureSet.java
index 1195aca525..6d8ca82e91 100644
--- 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/FeatureSet.java
+++ 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/FeatureSet.java
@@ -80,13 +80,20 @@ final class FeatureSet extends DiscreteSampling {
      */
     static final String TRAJECTORY = "trajectory";
 
+    /**
+     * Amount of memory (in number of bytes) to use when reading features in 
advance.
+     * This is used for computing the value of {@link #pageSize}, taking in 
account
+     * the size of each variable.
+     */
+    @Configuration
+    private static final int MEMORY_USAGE = 2 * 1024 * 1024;
+
     /**
      * Number of features to get in one read operation. We do not read 
features one-by-one because it may be slow.
      * We do not read all features at once neither because it consumes a lot 
of memory if the netCDF file is large.
      * This value is a compromise between reducing I/O operations and reducing 
memory consumption.
      */
-    @Configuration
-    private static final int PAGE_SIZE = 512;
+    private final int pageSize;
 
     /**
      * The number of instances for each feature, or {@code null} if none. If 
non-null, then the number of features
@@ -205,6 +212,11 @@ final class FeatureSet extends DiscreteSampling {
         this.referencingDimension = selectedAxes.size();
         this.isTrajectory         = isTrajectory | (referencingDimension == 0);
         this.hasTime              = hasTime;
+        int bytesPerFeature = 0;
+        for (final Variable property : properties) {
+            bytesPerFeature += property.getDataType().size();
+        }
+        pageSize = Math.max(MEMORY_USAGE / Math.max(bytesPerFeature, 1), 1);
         /*
          * We will create a description of the features to be read with 
following properties:
          *
@@ -683,7 +695,7 @@ skip:           for (final Variable v : properties) {
 
         /**
          * Values of all simple properties (having a single value per feature 
instance).
-         * The list size should not exceed {@value FeatureSet#PAGE_SIZE} 
elements.
+         * The size of each list should not exceed {@value 
FeatureSet#pageSize} elements.
          *
          * @see FeatureSet#properties
          */
@@ -708,7 +720,7 @@ skip:           for (final Variable v : properties) {
 
         /**
          * Creates a new iterator. This constructor reads immediately data for 
the first
-         * {@value #PAGE_SIZE} feature instances as a way to detect problem 
early.
+         * {@value #pageSize} feature instances as a way to detect problem 
early.
          */
         Iter() throws IOException, DataStoreException {
             size = (int) Math.min(getFeatureCount().orElse(0), 
Integer.MAX_VALUE);
@@ -724,11 +736,11 @@ skip:           for (final Variable v : properties) {
         }
 
         /**
-         * Reads static property values for the next {@value #PAGE_SIZE} 
feature instances.
+         * Reads static property values for the next {@value #pageSize} 
feature instances.
          * Reading starts at the feature at index given by {@link 
#currentLowerIndex}.
          */
         private void readNextPage() throws IOException, DataStoreException {
-            final int length = Math.min(size - currentLowerIndex, PAGE_SIZE);
+            final int length = Math.min(size - currentLowerIndex, pageSize);
             read(properties, propertyIndexOffset, currentLowerIndex, length, 
propertyValues);
             currentUpperIndex = currentLowerIndex + length;
         }
@@ -880,8 +892,12 @@ makeGeom:   if (!isEmpty) {
              * The time vector is the first vector after the geometry 
dimensions.
              */
             if (hasTime) {
-                MovingFeatures.setTimes((Attribute<?>) 
feature.getProperty(TRAJECTORY),
-                                        coordinateValues[geometryDimension], 
timeCRS);
+                final var t = (Attribute<?>) feature.getProperty(TRAJECTORY);
+                if (isTrajectory) {
+                    MovingFeatures.setTimes(t, 
coordinateValues[geometryDimension], timeCRS);
+                } else {
+                    MovingFeatures.setTime(t, 
coordinateValues[geometryDimension].doubleValue(offset), timeCRS);
+                }
             }
             action.accept(feature);
             dynamicPropertyPosition += length;         // Check for 
ArithmeticException is already done by `extent(…)` call.

Reply via email to