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.