You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by js...@apache.org on 2026/02/18 14:25:43 UTC
(sis) branch geoapi-4.0 updated: feat(NetCDF): improve moving features read performances, fix moving feature time property single value for features which are not trajectories
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 <jo...@geomatys.com>
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.