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 2018/11/23 02:06:38 UTC
[sis] branch geoapi-4.0 updated: First attempt to take in account
the two-dimensional localization grid found in HYCOM files.
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 c0f83ed First attempt to take in account the two-dimensional localization grid found in HYCOM files.
c0f83ed is described below
commit c0f83edc66774d9586892439320866c971e54866
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Fri Nov 23 03:05:47 2018 +0100
First attempt to take in account the two-dimensional localization grid found in HYCOM files.
---
.../java/org/apache/sis/math/RepeatedVector.java | 8 +-
.../src/main/java/org/apache/sis/math/Vector.java | 60 +++++++-----
.../java/org/apache/sis/internal/netcdf/Axis.java | 87 +++++++++++------
.../java/org/apache/sis/internal/netcdf/Grid.java | 104 ++++++++++++++++-----
.../sis/internal/netcdf/impl/ChannelDecoder.java | 33 +++++--
.../apache/sis/internal/netcdf/impl/GridInfo.java | 20 +++-
6 files changed, 219 insertions(+), 93 deletions(-)
diff --git a/core/sis-utility/src/main/java/org/apache/sis/math/RepeatedVector.java b/core/sis-utility/src/main/java/org/apache/sis/math/RepeatedVector.java
index 0a9b7a0..db76073 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/math/RepeatedVector.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/math/RepeatedVector.java
@@ -106,10 +106,10 @@ final class RepeatedVector extends Vector implements Serializable {
}
/**
- * Creates a vector of repeated data from the result of a call to {@link Vector#repetitions()}.
+ * Creates a vector of repeated data from the result of a call to {@link Vector#repetitions(int...)}.
*
* @param base the vector on which this vector is derived from.
- * @param repetitions results of {@link Vector#repetitions()}. Must be non-empty.
+ * @param repetitions results of {@link Vector#repetitions(int...)}. Must be non-empty.
* @param tolerance tolerance factor for compression of the base vector.
*/
RepeatedVector(final Vector base, final int[] repetitions, final double tolerance) {
@@ -177,10 +177,10 @@ final class RepeatedVector extends Vector implements Serializable {
/**
* Returns the parameters used by this {@code RepeatedVector} instance on the assumption
- * that they are the result of a previous invocation to {@link Vector#repetitions()}.
+ * that they are the result of a previous invocation to {@link Vector#repetitions(int...)}.
*/
@Override
- public int[] repetitions() {
+ public int[] repetitions(final int... candidates) {
if (cycleLength * occurrences >= size) {
return new int[] {occurrences};
} else {
diff --git a/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java b/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
index 959b42b..8a4af37 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
@@ -530,12 +530,18 @@ public abstract class Vector extends AbstractList<Number> implements RandomAcces
* Those grids sometime have constant longitude for the same column index, or constant latitude for the same row index.
* This method can detect such regularity, which allows more efficient handling of the <cite>grid to CRS</cite> transform.</p>
*
+ * @param candidates probable values, or {@code null} or an empty array if unknown. If non-empty, those values will be used
+ * for narrowing the search, which may improve performances. There is no guarantees that the values returned by this
+ * method will be among the given candidates.
* @return the number of times that entities (numbers, or group of numbers) appears consecutively with identical values.
* If no such repetition is found, an empty array.
*
* @since 1.0
*/
- public int[] repetitions() {
+ public int[] repetitions(int... candidates) {
+ if (candidates != null && candidates.length == 0) {
+ candidates = null;
+ }
final int size = size();
if (size >= 2) {
/*
@@ -545,7 +551,7 @@ public abstract class Vector extends AbstractList<Number> implements RandomAcces
* is faster.
*/
int r0 = 0;
- for (int i=0; i < size; i += r0) {
+ for (int i=0; i < size-1; i += r0) {
final int p = r0;
r0 = indexOf(i, i+1, false) - i;
if (r0 <= 1 || (p % r0) != 0) {
@@ -562,30 +568,38 @@ public abstract class Vector extends AbstractList<Number> implements RandomAcces
* very slow when r0 = 1 because equals(…) is invoked for all values. Computing an amount of values that
* we can skip in the special case where r0 = 1 increases the speed a lot.
*/
+ int candidateIndex = 0;
final int skip = (r0 == 1) ? indexOf(0, 1, false) : 0;
int r = 0;
-nextMatch: for (;;) {
- r += r0;
- if (skip != 0) {
- /*
- * Optimization for reducing the number of method calls when r0 = 1: the default algorithm is to
- * search for a position multiple of 'r0' where all values since the beginning of the vector are
- * repeated. But if 'r0' is 1, the default algorithms perform a costly check at every positions.
- * To avoid that, we use 'indexOf' for searching the index of the next position where a match may
- * exist (we don't care anymore about multiples of r0 since r0 is 1). If the first expected values
- * are constants, we use 'indexOf' again for checking efficiently those constants.
- */
- r = indexOf(0, r, true);
- if (skip != 1) {
- if (r + skip >= size) break;
- final int end = indexOf(r, r+1, false);
- if (end - r != skip) {
- r = end - 1;
- continue;
+search: for (;;) {
+ if (candidates != null) {
+ do {
+ if (candidateIndex >= candidates.length) break search;
+ r = r0 * candidates[candidateIndex++];
+ } while (r <= 0 || r >= size);
+ } else {
+ r += r0;
+ if (skip != 0) {
+ /*
+ * Optimization for reducing the number of method calls when r0 = 1: the default algorithm is to
+ * search for a position multiple of 'r0' where all values since the beginning of the vector are
+ * repeated. But if 'r0' is 1, the default algorithms perform a costly check at every positions.
+ * To avoid that, we use 'indexOf' for searching the index of the next position where a match may
+ * exist (we don't care anymore about multiples of r0 since r0 is 1). If the first expected values
+ * are constants, we use 'indexOf' again for checking efficiently those constants.
+ */
+ r = indexOf(0, r, true);
+ if (skip != 1) {
+ if (r + skip >= size) break;
+ final int end = indexOf(r, r+1, false);
+ if (end - r != skip) {
+ r = end - 1;
+ continue;
+ }
}
}
+ if (r >= size) break;
}
- if (r >= size) break;
if (equals(skip, Math.min(r0, size - r), this, r + skip)) {
/*
* Found a possible repetition of length r. Verify if this repetition pattern is observed until
@@ -593,7 +607,7 @@ nextMatch: for (;;) {
*/
for (int i=r; i<size; i += r) {
if (!equals(0, Math.min(r, size - i), this, i)) {
- continue nextMatch;
+ continue search;
}
}
break; // At this point we verified that the repetition is observed until the vector end.
@@ -1199,7 +1213,7 @@ nextMatch: for (;;) {
* end must be at least 1/4 of the vector size.
*/
if (length > 20*Integer.SIZE / getBitCount()) {
- final int[] repetitions = repetitions();
+ final int[] repetitions = repetitions(MathFunctions.divisors(length));
switch (repetitions.length) {
default: if (length - repetitions[1] < length/4) break; // Otherwise fallthrough.
case 1: return new RepeatedVector(this, repetitions, tolerance);
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java
index bae678d..999267f 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java
@@ -48,6 +48,9 @@ import ucar.nc2.constants.CF;
* Whether the array length is 1 or 2 depends on whether the wrapped netCDF axis is an instance of
* {@link ucar.nc2.dataset.CoordinateAxis1D} or {@link ucar.nc2.dataset.CoordinateAxis2D} respectively.
*
+ * <p>The natural ordering of axes is based on the order in which dimensions are declared for a variable.
+ * This is not necessarily the same order than the order in which variables are listed in the netCDF file.</p>
+ *
* @author Martin Desruisseaux (Geomatys)
* @version 1.0
*
@@ -56,7 +59,7 @@ import ucar.nc2.constants.CF;
* @since 0.3
* @module
*/
-public final class Axis extends NamedElement {
+public final class Axis extends NamedElement implements Comparable<Axis> {
/**
* The abbreviation, also used as a way to identify the axis type.
* This is a controlled vocabulary: if any abbreviation is changed,
@@ -241,6 +244,19 @@ public final class Axis extends NamedElement {
}
/**
+ * Compares this axis with the given axis for ordering based on the dimensions declared in a variable.
+ * This is used for sorting axes in the same order than the dimension order on the variable using axes.
+ * This order is not necessarily the same than the order in which variables are listed in the netCDF file.
+ *
+ * @param other the other axis to compare to this axis.
+ * @return -1 if this axis should be before {@code other}, +1 if it should be after.
+ */
+ @Override
+ public int compareTo(final Axis other) {
+ return sourceDimensions[0] - other.sourceDimensions[0];
+ }
+
+ /**
* Creates an ISO 19111 axis from the information stored in this netCDF axis.
*
* @param factory the factory to use for creating the coordinate system axis.
@@ -327,37 +343,41 @@ public final class Axis extends NamedElement {
final boolean trySetTransform(final Matrix gridToCRS, final int srcEnd, final int tgtDim,
final List<MathTransform> nonLinears) throws IOException, DataStoreException
{
- /*
- * Normal case where the axis has only one dimension.
- */
- if (sourceDimensions.length == 1) {
- final int srcDim = srcEnd - sourceDimensions[0];
- if (coordinates.trySetTransform(gridToCRS, srcDim, tgtDim, null)) {
- return true;
- } else {
- nonLinears.add(MathTransforms.interpolate(null, coordinates.read().doubleValues()));
- return false;
+ switch (sourceDimensions.length) {
+ /*
+ * Defined as a matter of principle, but should never happen.
+ */
+ case 0: return true;
+ /*
+ * Normal case where the axis has only one dimension.
+ */
+ case 1: {
+ final int srcDim = srcEnd - sourceDimensions[0];
+ if (coordinates.trySetTransform(gridToCRS, srcDim, tgtDim, null)) {
+ return true;
+ } else {
+ nonLinears.add(MathTransforms.interpolate(null, coordinates.read().doubleValues()));
+ return false;
+ }
}
- }
- /*
- * In netCDF files, axes are sometime associated to two-dimensional localization grids.
- * If this is the case, then the following block checks if we can reduce those grids to
- * one-dimensional vector. For example the following localisation grids:
- *
- * 10 10 10 10 10 12 15 20
- * 12 12 12 12 or 10 12 15 20
- * 15 15 15 15 10 12 15 20
- * 20 20 20 20 10 12 15 20
- *
- * can be reduced to a one-dimensional {10 12 15 20} vector (orientation matter however).
- *
- * Note: following block is currently restricted to the two-dimensional case, but it could
- * be generalized to n-dimensional case if we resolve the default case in the switch statement.
- */
- if (sourceDimensions.length == 2) {
- Vector data = coordinates.read();
- if (!coordinates.readTriesToCompress() || data.getClass().getSimpleName().equals("RepeatedVector")) {
- final int[] repetitions = data.repetitions(); // Detects repetitions as illustrated above.
+ /*
+ * In netCDF files, axes are sometime associated to two-dimensional localization grids.
+ * If this is the case, then the following block checks if we can reduce those grids to
+ * one-dimensional vector. For example the following localisation grids:
+ *
+ * 10 10 10 10 10 12 15 20
+ * 12 12 12 12 or 10 12 15 20
+ * 15 15 15 15 10 12 15 20
+ * 20 20 20 20 10 12 15 20
+ *
+ * can be reduced to a one-dimensional {10 12 15 20} vector (orientation matter however).
+ *
+ * Note: following block is currently restricted to the two-dimensional case, but it could
+ * be generalized to n-dimensional case if we resolve the default case in the switch statement.
+ */
+ case 2: {
+ Vector data = coordinates.read();
+ final int[] repetitions = data.repetitions(sourceSizes); // Detects repetitions as illustrated above.
if (repetitions.length != 0) {
for (int i=0; i<sourceDimensions.length; i++) {
final int srcDim = srcEnd - sourceDimensions[i]; // "Natural" order is reverse of netCDF order.
@@ -385,6 +405,11 @@ public final class Axis extends NamedElement {
}
}
}
+ /*
+ * Localization grid of 3 dimensions or more are theoretically possible, but uncommon.
+ * Such grids are not yet supported. Note that we can read three or more dimensional data;
+ * it is only the localization grid which is limited to one or two dimensions.
+ */
}
nonLinears.add(null);
return false;
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
index c8e1d2f..48ac268 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
@@ -17,6 +17,7 @@
package org.apache.sis.internal.netcdf;
import java.util.List;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.io.IOException;
@@ -32,10 +33,12 @@ import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.metadata.spatial.DimensionNameType;
import org.apache.sis.internal.metadata.AxisDirections;
import org.apache.sis.referencing.operation.matrix.Matrices;
+import org.apache.sis.referencing.operation.builder.LocalizationGridBuilder;
import org.apache.sis.coverage.grid.GridExtent;
import org.apache.sis.coverage.grid.GridGeometry;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.util.NullArgumentException;
+import org.apache.sis.math.Vector;
/**
@@ -254,7 +257,7 @@ public abstract class Grid extends NamedElement {
*/
int srcEnd = getSourceDimensions();
int tgtEnd = getTargetDimensions();
- final int[] deferred = new int[axes.length];
+ final int[] deferred = new int[axes.length]; // Indices of axes that have been deferred.
final List<MathTransform> nonLinears = new ArrayList<>();
final Matrix affine = Matrices.createZero(tgtEnd + 1, srcEnd + 1);
affine.setElement(tgtEnd--, srcEnd--, 1);
@@ -264,47 +267,106 @@ public abstract class Grid extends NamedElement {
}
}
/*
- * If we have not been able to set coefficients in the matrix (because the transform is non-linear),
- * set a single scale factor to 1 in the matrix row; we will concatenate later with a non-linear transform.
- * The coefficient that we set to 1 is the one for the source dimension which is not already taken by another row.
+ * If we have not been able to set some coefficients in the matrix (because some transforms are non-linear),
+ * set a single scale factor to 1 in the matrix row. The coefficient that we set to 1 is the one for the source
+ * dimension which is not already taken by another row.
*/
- final MathTransformFactory factory = decoder.getMathTransformFactory();
- for (int i=0; i<nonLinears.size(); i++) {
+ final int[] sourceDimensions = new int[nonLinears.size()];
+ Arrays.fill(sourceDimensions, -1);
+ for (int i=0; i<sourceDimensions.length; i++) {
final int d = deferred[i];
-findFree: for (int srcDim : axes[d].sourceDimensions) {
+ final Axis axis = axes[d];
+findFree: for (int srcDim : axis.sourceDimensions) {
srcDim = srcEnd - srcDim;
for (int j=affine.getNumRow(); --j>=0;) {
if (affine.getElement(j, srcDim) != 0) {
continue findFree;
}
}
+ sourceDimensions[i] = srcDim;
final int tgtDim = tgtEnd - d;
affine.setElement(tgtDim, srcDim, 1);
- /*
- * Prepare non-linear transform here for later concatenation.
- * TODO: what to do if the transform is null?
- */
- MathTransform tr = nonLinears.get(i);
- if (tr != null) {
- tr = factory.createPassThroughTransform(srcDim, tr, (srcEnd + 1) - (srcDim + tr.getSourceDimensions()));
- nonLinears.set(i, tr);
- }
break;
}
}
/*
+ * Search for non-linear transforms not yet constructed. It may be because the transform requires a
+ * two-dimensional localization grid. Those transforms require two variables, i.e. "two-dimensional"
+ * axes come in pairs.
+ */
+ final MathTransformFactory factory = decoder.getMathTransformFactory();
+ for (int i=0; i<nonLinears.size(); i++) { // Length of 'nonLinears' may change in this loop.
+ if (nonLinears.get(i) == null) {
+ final Axis axis = axes[deferred[i]];
+ if (axis.sourceDimensions.length == 2) {
+ final int d1 = axis.sourceDimensions[0];
+ final int d2 = axis.sourceDimensions[1];
+ for (int j=i; ++j < nonLinears.size();) {
+ if (nonLinears.get(j) == null) {
+ final Axis other = axes[deferred[j]];
+ if (other.sourceDimensions.length == 2) {
+ final int o1 = other.sourceDimensions[0];
+ final int o2 = other.sourceDimensions[1];
+ if ((o1 == d1 && o2 == d2) || (o1 == d2 && o2 == d1)) {
+ /*
+ * Found two axes for the same set of dimensions, which implies that they have
+ * the same shape (width and height). In current implementation, we also need
+ * those axes to be at consecutive source dimensions.
+ */
+ final int srcDim = sourceDimensions[i];
+ final int otherDim = sourceDimensions[j];
+ if (Math.abs(srcDim - otherDim) == 1) {
+ final int width = axis.sourceSizes[0];
+ final int height = axis.sourceSizes[1];
+ final LocalizationGridBuilder grid = new LocalizationGridBuilder(width, height);
+ final Vector v1 = axis.coordinates.read();
+ final Vector v2 = other.coordinates.read();
+ final double[] target = new double[2];
+ int index = 0;
+ for (int y=0; y<height; y++) {
+ for (int x=0; x<width; x++) {
+ target[0] = v1.doubleValue(index);
+ target[1] = v2.doubleValue(index);
+ grid.setControlPoint(x, y, target);
+ index++;
+ }
+ }
+ /*
+ * Replace the first transform by the two-dimensional localization grid and
+ * remove the other transform. Removals need to be done in arrays too.
+ */
+ nonLinears.set(i, grid.create(factory));
+ nonLinears.remove(j);
+ final int n = nonLinears.size() - j;
+ System.arraycopy(deferred, j+1, deferred, j, n);
+ System.arraycopy(sourceDimensions, j+1, sourceDimensions, j, n);
+ if (otherDim < srcDim) {
+ sourceDimensions[i] = otherDim;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ /*
* Final transform, as the concatenation of the non-linear transforms followed by the affine transform.
* We concatenate the affine transform last because it may change axis order.
*/
MathTransform gridToCRS = null;
+ final int nonLinearCount = nonLinears.size();
nonLinears.add(factory.createAffineTransform(affine));
- for (final MathTransform tr : nonLinears) {
+ for (int i=0; i <= nonLinearCount; i++) {
+ MathTransform tr = nonLinears.get(i);
if (tr != null) {
- if (gridToCRS == null) {
- gridToCRS = tr;
- } else {
- gridToCRS = factory.createConcatenatedTransform(gridToCRS, tr);
+ if (i < nonLinearCount) {
+ final int srcDim = sourceDimensions[i];
+ tr = factory.createPassThroughTransform(srcDim, tr,
+ (srcEnd + 1) - (srcDim + tr.getSourceDimensions()));
}
+ gridToCRS = (gridToCRS == null) ? tr : factory.createConcatenatedTransform(gridToCRS, tr);
}
}
geometry = new GridGeometry(getExtent(axes), PixelInCell.CELL_CENTER, gridToCRS, getCoordinateReferenceSystem(decoder));
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
index 033fcd0..f468756 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
@@ -18,9 +18,12 @@ package org.apache.sis.internal.netcdf.impl;
import java.util.Set;
import java.util.Map;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.AbstractMap;
+import java.util.AbstractList;
+import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedHashMap;
import java.util.IdentityHashMap;
@@ -35,7 +38,6 @@ import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.channels.ReadableByteChannel;
-import java.util.AbstractList;
import javax.measure.UnitConverter;
import javax.measure.IncommensurableException;
import javax.measure.format.ParserException;
@@ -868,7 +870,8 @@ public final class ChannelDecoder extends Decoder {
* For each variable, get its list of axes. More than one variable may have the same list of axes,
* so we remember the previously created instances in order to share the grid geometry instances.
*/
- final Set<VariableInfo> axes = new LinkedHashSet<>(4);
+ final Set<VariableInfo> axes = new LinkedHashSet<>(8);
+ final Set<Dimension> usedDimensions = new HashSet<>(8);
final Map<GridInfo,GridInfo> shared = new LinkedHashMap<>();
nextVar: for (final VariableInfo variable : variables) {
if (variable.isCoordinateSystemAxis() || variable.dimensions.length == 0) {
@@ -881,38 +884,50 @@ nextVar: for (final VariableInfo variable : variables) {
* of this method. If and only if we can find all axes, we create the GridGeometryInfo.
* This is a "all or nothing" operation.
*/
+ axes.clear();
+ usedDimensions.clear();
final CharSequence[] coordinates = variable.getCoordinateVariables();
if (coordinates.length != 0) {
for (int i=coordinates.length; --i >= 0;) {
final VariableInfo axis = findVariable(coordinates[i].toString());
if (axis == null) {
+ usedDimensions.clear();
axes.clear();
break;
}
axes.add(axis);
+ usedDimensions.addAll(Arrays.asList(axis.dimensions));
}
}
- if (axes.isEmpty()) {
- for (final Dimension dimension : variable.dimensions) {
+ /*
+ * In theory the "coordinates" attribute would enumerate all axis needed for covering all dimensions,
+ * and we would not need to check for variables having dimension names. However in practice there is
+ * incomplete attributes, so we check for other dimensions even if the above loop did some work.
+ */
+ int mixedFlag = axes.isEmpty() ? 0 : 1;
+ for (final Dimension dimension : variable.dimensions) {
+ if (usedDimensions.add(dimension)) {
final List<VariableInfo> axis = dimToAxes.get(dimension); // Should have only 1 element.
if (axis == null) {
- axes.clear();
continue nextVar;
}
axes.addAll(axis);
+ mixedFlag |= 2;
}
}
/*
- * Creates the grid geometry using the given domain and range,
- * reusing existing instance if one exists.
+ * Creates the grid geometry using the given domain and range, reusing existing instance if one exists.
+ * We usually try to preserve axis order as declared in the netCDF file. But if we mixed axes inferred
+ * from the "coordinates" attribute and axes inferred from variable names matching dimension names, we
+ * are better to sort them.
*/
- GridInfo gridGeometry = new GridInfo(variable.dimensions, axes.toArray(new VariableInfo[axes.size()]));
+ GridInfo gridGeometry = new GridInfo(variable.dimensions,
+ axes.toArray(new VariableInfo[axes.size()]), mixedFlag == 3);
GridInfo existing = shared.putIfAbsent(gridGeometry, gridGeometry);
if (existing != null) {
gridGeometry = existing;
}
variable.gridGeometry = gridGeometry;
- axes.clear();
}
gridGeometries = shared.values().toArray(new Grid[shared.size()]);
}
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridInfo.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridInfo.java
index 033c74a..cddf008 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridInfo.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridInfo.java
@@ -88,15 +88,22 @@ final class GridInfo extends Grid {
private final VariableInfo[] range;
/**
+ * Whether axes should be sorted instead than relying on the order found in netCDF file.
+ */
+ private final boolean sortAxes;
+
+ /**
* Constructs a new grid geometry information.
* The {@code domain} and {@code range} arrays often have the same length, but not necessarily.
*
- * @param domain describes the input values of the "grid to CRS" conversion.
- * @param range the output values of the "grid to CRS" conversion.
+ * @param domain describes the input values of the "grid to CRS" conversion.
+ * @param range the output values of the "grid to CRS" conversion.
+ * @param sortAxes whether axes should be sorted instead than relying on the order found in netCDF file.
*/
- GridInfo(final Dimension[] domain, final VariableInfo[] range) {
- this.domain = domain;
- this.range = range;
+ GridInfo(final Dimension[] domain, final VariableInfo[] range, final boolean sortAxes) {
+ this.domain = domain;
+ this.range = range;
+ this.sortAxes = sortAxes;
}
/**
@@ -237,6 +244,9 @@ final class GridInfo extends Grid {
axes[targetDim] = new Axis(this, axis, abbreviation, axis.getAttributeString(CF.POSITIVE),
ArraysExt.resize(indices, i), ArraysExt.resize(sizes, i));
}
+ if (sortAxes) {
+ Arrays.sort(axes);
+ }
return axes;
}