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 2026/06/18 16:13:33 UTC
(sis) branch geoapi-4.0 updated: Fix the order of pyramid levels in HEIF file. Opportunistic fix in the formatting of scale factors.
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits 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 ccfa3c1130 Fix the order of pyramid levels in HEIF file. Opportunistic fix in the formatting of scale factors.
ccfa3c1130 is described below
commit ccfa3c11306edc60f8adcddc9b4cea8d3c6a69d3
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Thu Jun 18 18:02:54 2026 +0200
Fix the order of pyramid levels in HEIF file.
Opportunistic fix in the formatting of scale factors.
---
.../org/apache/sis/coverage/grid/GridExtent.java | 38 ++++++++
.../org/apache/sis/coverage/grid/GridGeometry.java | 6 +-
.../main/org/apache/sis/io/wkt/Formatter.java | 2 +-
.../apache/sis/storage/tiling/TileMatrixSet.java | 2 +-
.../sis/storage/tiling/TileMatrixSetFormat.java | 43 +++++----
.../apache/sis/util/internal/shared/Numerics.java | 100 ++-------------------
.../sis/storage/geoheif/CoverageBuilder.java | 3 +-
.../org/apache/sis/storage/geoheif/ImageModel.java | 6 +-
.../apache/sis/storage/geoheif/ImageResource.java | 14 +--
.../org/apache/sis/storage/geoheif/Pyramid.java | 40 ++++++++-
.../isobmff/mpeg/CompressionConfiguration.java | 4 +
11 files changed, 126 insertions(+), 132 deletions(-)
diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
index e8aff27060..45b5d7e4bf 100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
@@ -22,6 +22,7 @@ import java.util.SortedMap;
import java.util.Arrays;
import java.util.Optional;
import java.util.Objects;
+import java.util.Comparator;
import java.util.Spliterator;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@@ -919,6 +920,7 @@ public class GridExtent implements GridEnvelope, LenientComparable, Serializable
* @see #getLow(int)
* @see #getHigh(int)
* @see #resize(long...)
+ * @see #SIZES_COMPARATOR
*/
@Override
public long getSize(final int index) {
@@ -936,6 +938,8 @@ public class GridExtent implements GridEnvelope, LenientComparable, Serializable
* @param index the dimension for which to obtain the size.
* @param minusOne {@code true} for returning <var>size</var>−1 instead of <var>size</var>.
* @return the number of cells along the given dimension, optionally minus one.
+ *
+ * @see #SIZES_COMPARATOR
*/
public double getSize(final int index, final boolean minusOne) {
final int dimension = getDimension();
@@ -947,6 +951,40 @@ public class GridExtent implements GridEnvelope, LenientComparable, Serializable
return Numerics.toUnsignedDouble(size);
}
+ /**
+ * A comparator of grid extent sizes where each dimension is compared in increasing index order.
+ * For a given pair of {@code GridExtent} instances, the comparison begins with the
+ * {@linkplain #getSize(int) size} of each extent in the first dimension (index 0):
+ *
+ * <ul>
+ * <li>If the first extent has a smaller size, a negative number is returned.</li>
+ * <li>If the second extent has a smaller size, a positive number is returned.</li>
+ * <li>Otherwise, the comparison is repeated with the next dimensions (1, 2, …,
+ * {@linkplain #getDimension() dimension} - 1) until a dimension is found
+ * where the sizes are not equal.
+ * </ul>
+ *
+ * If all dimensions have the same {@linkplain #getSize(int) size},
+ * then the extent with the smallest number of dimensions is considered before the other extent.
+ * Null values are considered after all non-null values.
+ *
+ * @since 1.7
+ */
+ public static final Comparator<GridExtent> SIZES_COMPARATOR = (e1, e2) -> {
+ if (e1 == e2) return 0;
+ if (e1 == null) return +1;
+ if (e2 == null) return -1;
+ final int d1 = e1.getDimension();
+ final int d2 = e2.getDimension();
+ final int dim = Math.min(d1, d2);
+ for (int i=0; i<dim; i++) {
+ int c = Long.compareUnsigned(e1.coordinates[d1 + i] - e1.coordinates[i],
+ e2.coordinates[d2 + i] - e2.coordinates[i]);
+ if (c != 0) return c;
+ }
+ return d1 - d2;
+ };
+
/**
* Returns the low and high coordinates packaged in a single array.
* The length of this array is twice the number of dimensions.
diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
index 77e8cffc39..46f7035f68 100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
@@ -2460,8 +2460,8 @@ public class GridGeometry implements LenientComparable, Serializable {
final double lower = envelope.getLower(i);
final double upper = envelope.getUpper(i);
final double delta = (resolution != null) ? resolution[i] : Double.NaN;
- nf.setMinimumFractionDigits(Numerics.fractionDigitsForDelta(delta));
- nf.setMaximumFractionDigits(Numerics.suggestFractionDigits(lower, upper) - 1); // The -1 is for rounding errors.
+ nf.setMaximumFractionDigits(Numerics.fractionDigitsForRange(lower, upper) - 1); // The -1 is for rounding errors.
+ nf.setMinimumFractionDigits(Numerics.fractionDigitsForDelta(delta)); // May adjust above max value.
final CoordinateSystemAxis axis = (cs != null) ? cs.getAxis(i) : null;
final String name = (axis != null) ? axis.getName().getCode() : vocabulary.getString(Vocabulary.Keys.Dimension_1, i);
table.append(name).append(": ").nextColumn();
@@ -2633,7 +2633,7 @@ public class GridGeometry implements LenientComparable, Serializable {
} else {
final NumberFormat nf = numberFormat();
final int n = Double.isNaN(delta)
- ? Numerics.suggestFractionDigits(value) / 2
+ ? Numerics.fractionDigitsForDelta(value) / 2
: Numerics.fractionDigitsForDelta(delta);
nf.setMaximumFractionDigits(n + 1);
out.append(nf.format(value));
diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
index 45aa889fc3..9b45a63daa 100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
@@ -1018,7 +1018,7 @@ public class Formatter implements Localized {
final double max = range.getMaxDouble();
int minimumFractionDigits = Numerics.fractionDigitsForDelta(max - min);
int maximumFractionDigits = Math.min(Math.min(
- Numerics.suggestFractionDigits(min, max),
+ Numerics.fractionDigitsForRange(min, max),
minimumFractionDigits + 2), VERTICAL_ACCURACY); // Arbitrarily limit to 2 more digits.
openElement(true, WKTKeywords.VerticalExtent);
setColor(ElementKind.EXTENT);
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrixSet.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrixSet.java
index 11e37f43af..a25843f0e0 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrixSet.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrixSet.java
@@ -129,7 +129,7 @@ public interface TileMatrixSet {
/**
* Returns all {@code TileMatrix} instances in this set, together with their identifiers.
* For each value in the map, the associated key is {@link TileMatrix#getIdentifier()}.
- * Entries are sorted from coarser resolution (highest scale denominator) to most detailed
+ * Entries are sorted from coarsest resolution (highest scale denominator) to most detailed
* resolution (lowest scale denominator).
* This is not necessarily the natural ordering of the {@link GenericName} instances used as keys.
*
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrixSetFormat.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrixSetFormat.java
index 321700a504..b528691764 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrixSetFormat.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrixSetFormat.java
@@ -43,7 +43,6 @@ import org.apache.sis.util.CharSequences;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.resources.Vocabulary;
-import org.apache.sis.util.internal.shared.Numerics;
import org.apache.sis.util.collection.TableColumn;
import org.apache.sis.util.collection.TreeTableFormat;
import org.apache.sis.util.collection.BackingStoreException;
@@ -52,6 +51,7 @@ import org.apache.sis.referencing.IdentifiedObjects;
import org.apache.sis.coverage.grid.GridExtent;
import org.apache.sis.coverage.grid.IncompleteGridGeometryException;
import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
+import org.apache.sis.math.DecimalFunctions;
import org.apache.sis.io.CompoundFormat;
import org.apache.sis.io.TableAppender;
@@ -113,6 +113,8 @@ public class TileMatrixSetFormat extends CompoundFormat<TileMatrixSet> {
/**
* Creates one row in the table for the given matrix.
+ * This constructor prepares string representations of all values except resolutions.
+ * Resolutions must be formatted by a call to {@link #formatResolutions(List, NumberFormat)}.
*
* @param matrix the matrix for which to store information.
* @param integerFormat a number format configured for integer values.
@@ -176,27 +178,34 @@ public class TileMatrixSetFormat extends CompoundFormat<TileMatrixSet> {
* This value should be equal to the number of dimensions in the <abbr>CRS</abbr>.
*/
static int formatResolutions(final List<Row> rows, final NumberFormat format) {
- final var values = new double[rows.size()];
- for (int i=0; ; i++) {
- int count = 0;
+ final int numRow = rows.size();
+ for (int column = 0; ; column++) {
+ boolean found = false;
+ double maximum = 0;
+ int n = 0;
for (final Row row : rows) {
- if (i < row.resolution.length) {
- values[count++] = row.resolution[i];
+ if (column < row.resolution.length) {
+ found = true;
+ double value = Math.abs(row.resolution[column]);
+ if (value > maximum) maximum = value;
+ value -= Math.floor(value);
+ if (value > 0) {
+ n = Math.max(n, DecimalFunctions.fractionDigitsForDelta(value, true));
+ }
}
}
- if (count == 0) return i;
- final int n = Numerics.suggestFractionDigits(ArraysExt.resize(values, count));
+ if (!found) return column;
+ if (n != 0) n++; // TODO: should do a better work for finding the number of fraction digits.
+ n = Math.min(n, DecimalFunctions.fractionDigitsForValue(maximum));
format.setMinimumFractionDigits(n);
format.setMaximumFractionDigits(n);
- final int column = i; // Because lambda requires final values.
- final String[] formatted = Numerics.formatAndTrimTrailingZeros(format, values.length, (j) -> {
- final double[] resolution = rows.get(j).resolution;
- return (column < resolution.length) ? resolution[column] : Double.NaN;
- });
- for (int j=0; j<values.length; j++) {
- final String[] resolution = rows.get(j).formattedResolution;
- if (i < resolution.length) {
- resolution[i] = formatted[j];
+ for (int i=0; i<numRow; i++) {
+ final Row row = rows.get(i);
+ final String[] target = row.formattedResolution;
+ if (column < target.length) {
+ final double[] resolution = row.resolution;
+ final double value = (column < resolution.length) ? resolution[column] : Double.NaN;
+ target[column] = format.format(value);
}
}
}
diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/Numerics.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/Numerics.java
index 6440d7ac52..a9de7d6262 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/Numerics.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/Numerics.java
@@ -16,14 +16,10 @@
*/
package org.apache.sis.util.internal.shared;
-import java.util.Arrays;
import java.text.Format;
-import java.text.NumberFormat;
import java.text.DecimalFormat;
-import java.text.FieldPosition;
import java.math.BigInteger;
import java.util.function.BiFunction;
-import java.util.function.IntToDoubleFunction;
import static java.lang.Math.min;
import static java.lang.Math.max;
import static java.lang.Math.abs;
@@ -597,46 +593,20 @@ public final class Numerics {
}
/**
- * Suggests a number of fraction digits for the given values, ignoring NaN and infinities.
+ * Suggests a number of fraction digits for the given range of values.
* This method uses heuristic rules that may change in any future <abbr>SIS</abbr> version.
* Current implementation returns a value which avoid printing "garbage" digits with highest numbers,
* at the cost of loosing significant digits on smallest numbers. It may further reduce the number of
* digits if this is sufficient for distinguishing the given values.
- * An arbitrary limit is set to 16 digits, which is the number of digits for {@code Math.ulp(1.0)}}.
*
- * <p>This method modifies the given array in-place. Callers should not pass original data.</p>
- *
- * @param values the values for which to get suggested number of fraction digits.
+ * @param minimum the minimum value of the range.
+ * @param maximum the maximum value of the range.
* @return suggested number of fraction digits for the given values. Always positive.
*/
- public static int suggestFractionDigits(final double... values) {
- switch (values.length) {
- case 0: return 0;
- case 1: return DecimalFunctions.fractionDigitsForValue(values[0]);
- }
- for (int i=0; i<values.length; i++) {
- values[i] = abs(values[i]);
- }
- Arrays.sort(values);
- double ulp = 0;
- double delta = Double.POSITIVE_INFINITY;
- for (int i = values.length; --i >= 0;) {
- double value = values[i];
- if (Double.isFinite(value)) {
- ulp = ulp(value);
- while (--i >= 0) {
- final double lower = values[i];
- final double diff = value - lower;
- if (diff > 0 && diff < delta) {
- delta = diff;
- }
- value = lower;
- }
- break;
- }
- }
- // Arbitrarily add 6 digits the difference between values.
- return min(fractionDigitsForDelta(ulp), fractionDigitsForDelta(delta) + 6);
+ public static int fractionDigitsForRange(final double minimum, final double maximum) {
+ final double ulp = max(ulp(minimum), ulp(maximum));
+ // Arbitrarily add 6 digits to the value computed from the difference between values.
+ return min(fractionDigitsForDelta(ulp), fractionDigitsForDelta(maximum - minimum) + 6);
}
/**
@@ -670,62 +640,6 @@ public final class Numerics {
return fractionDigitsForDelta(delta);
}
- /**
- * Gets the character used for zero.
- * If the given format is not a {@link DecimalFormat}, arbitrarily returns the white space.
- *
- * @param format the format for which to get the zero digit.
- * @return the zero digit, or {@code ' '} if the format is not decimal.
- */
- private static char getZeroDigit(final Format format) {
- return (format instanceof DecimalFormat) ? ((DecimalFormat) format).getDecimalFormatSymbols().getZeroDigit() : ' ';
- }
-
- /**
- * Formats values then removes the same number of trailing zeros in the fraction digits of all values.
- * For this method to be useful, the given format should use a fixed number of fraction digits.
- * NaN and infinite values are formatted as usual but ignored in the removal of trailing zeros.
- *
- * @param format the format to use.
- * @param count number of values to format.
- * @param values provider of values to format.
- * @return an array of length {@code count} with the formatted values.
- */
- public static String[] formatAndTrimTrailingZeros(final NumberFormat format, final int count, final IntToDoubleFunction values) {
- final var formatted = new String[count];
- final var buffer = new StringBuffer();
- final var fractionEnd = new int[count];
- final var fractionField = new FieldPosition(NumberFormat.Field.FRACTION);
- final char zeroDigit = getZeroDigit(format);
- int numberOfTrailingZeros = Integer.MAX_VALUE;
- for (int i=0; i<count; i++) {
- final double value = values.applyAsDouble(i);
- String text = format.format(value, buffer, fractionField).toString();
- formatted[i] = text;
- if (numberOfTrailingZeros != 0 && Double.isFinite(value)) {
- final int end = fractionField.getEndIndex();
- fractionEnd[i] = end;
- int firstNonZero = end;
- final int start = Math.max(fractionField.getBeginIndex(), end - numberOfTrailingZeros);
- while (--firstNonZero > start) {
- if (text.charAt(firstNonZero) != zeroDigit) break;
- }
- numberOfTrailingZeros = Math.min(numberOfTrailingZeros, end - (firstNonZero + 1));
- }
- buffer.setLength(0);
- }
- if (numberOfTrailingZeros != 0) {
- for (int i=0; i<count; i++) {
- final int end = fractionEnd[i];
- if (end != 0) {
- formatted[i] = buffer.append(formatted[i]).delete(end - numberOfTrailingZeros, end).toString();
- buffer.setLength(0);
- }
- }
- }
- return formatted;
- }
-
/**
* Formats the given value with the given format, using scientific notation if needed.
* This is a workaround for {@link DecimalFormat} not switching automatically to scientific notation for large numbers.
diff --git a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/CoverageBuilder.java b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/CoverageBuilder.java
index 551fc2fec8..16690e47a9 100644
--- a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/CoverageBuilder.java
+++ b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/CoverageBuilder.java
@@ -596,8 +596,9 @@ final class CoverageBuilder implements Emptiable {
* This method should be invoked at most once.
* It may be invoked not at all when this object is used for building a tile instead of an image.
*
- * @todo Need to add information from the {@code ExtraDimensionProperty} box.
+ * @todo Need to add information from the {@code ExtraDimensionProperty} (edim) box.
* These information include name, minimum, maximum and resolution.
+ * See https://docs.ogc.org/per/24-038r1.html
*
* @return the grid geometry.
* @throws DataStoreException if the "grid to <abbr>CRS</abbr>" transform cannot be created.
diff --git a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageModel.java b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageModel.java
index 0da11c285f..a8d64494bd 100644
--- a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageModel.java
+++ b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageModel.java
@@ -85,8 +85,9 @@ final class ImageModel {
/**
* Computes date type, color model, sample model, and sample dimensions.
*
- * @todo Need to add information from the {@code CellPropertyTypeProperty} box.
+ * @todo Need to add information from the {@code CellPropertyTypeProperty} ("pcel") box.
* These information include sample dimension name and unit of measurement.
+ * See https://docs.ogc.org/per/24-038r1.html
*
* @param width the width (in pixels) of the reconstructed image.
* @param height the height (in pixels) of the reconstructed image.
@@ -156,7 +157,8 @@ final class ImageModel {
}
/*
* Create the sample dimension and derive metadata from it.
- * TODO: parse CellPropertyTypeProperty and CellPropertyCategoriesProperty boxes.
+ * TODO: parse CellPropertyTypeProperty (pcel) and CellPropertyCategoriesProperty (pcat) boxes.
+ * See https://docs.ogc.org/per/24-038r1.html
*/
if (colorType != null) {
sb.setName(colorType.toString());
diff --git a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageResource.java b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageResource.java
index 1aa77f32c1..c1a0f9ee4b 100644
--- a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageResource.java
+++ b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageResource.java
@@ -182,18 +182,10 @@ final class ImageResource extends TiledGridCoverageResource implements StoreReso
* Declares that this image is the pyramid level of the given base grid.
* This method does nothing if this image already has its own "grid to <abbr>CRS</abbr>" transform.
*
- * <p>If {@code base} is null, then the base is assumed to be the grid geometry of this level.
- * Configuring a level relative to itself is useful when the grid geometry is incomplete,
- * in which case the resolution of the base level is set to 1.
- * This is needed because resolutions are mandatory in a grid.</p>
- *
- * @param base grid geometry of the pyramid level at the finest resolution, or {@code null} if this level.
- * @return grid geometry of the pyramid level at the finest resolution ({@code base} if it was non-null).
+ * @param base grid geometry of the pyramid level at the finest resolution.
* @throws TransformException if an error occurred while deriving the "grid to <abbr>CRS</abbr>" transform.
*/
- final GridGeometry setPyramidLevelOf(GridGeometry base) throws TransformException {
- final boolean self = (base == null);
- if (self) base = gridGeometry;
+ final void setPyramidLevelOf(final GridGeometry base) throws TransformException {
if (!gridGeometry.isDefined(GridGeometry.GRID_TO_CRS)) {
final GridExtent levelExtent = gridGeometry.getExtent();
final GridExtent baseExtent = base.getExtent();
@@ -202,9 +194,7 @@ final class ImageResource extends TiledGridCoverageResource implements StoreReso
factors[i] = 1 / Numerics.divide(levelExtent.getSize(i), baseExtent.getSize(i));
}
gridGeometry = new GridGeometry(base, levelExtent, MathTransforms.scale(factors));
- if (self) base = gridGeometry;
}
- return base;
}
/**
diff --git a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/Pyramid.java b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/Pyramid.java
index 9cf9d01abd..234dcbb2e9 100644
--- a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/Pyramid.java
+++ b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/Pyramid.java
@@ -16,6 +16,8 @@
*/
package org.apache.sis.storage.geoheif;
+import java.util.Arrays;
+import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
@@ -23,6 +25,7 @@ import org.opengis.util.GenericName;
import org.opengis.util.NameFactory;
import org.opengis.referencing.operation.TransformException;
import org.apache.sis.coverage.SampleDimension;
+import org.apache.sis.coverage.grid.GridExtent;
import org.apache.sis.coverage.grid.GridGeometry;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.isobmff.image.ImagePyramid;
@@ -58,13 +61,22 @@ final class Pyramid extends TiledGridCoverageResource implements TiledGridCovera
*/
private final ImageResource[] levels;
+ /**
+ * A comparator of pyramid level based on the size of their grid extent.
+ * We use the grid extent because this is sometime the only information which is known.
+ * The "grid to <abbr>CRS</abbr>", and therefore the resolution, may not have been computed yet.
+ */
+ private static final Comparator<ImageResource> LEVEL_COMPARATOR =
+ Comparator.comparing((image) -> image.getGridGeometry().getExtent(),
+ GridExtent.SIZES_COMPARATOR.reversed());
+
/**
* Creates a new pyramid.
*
* @param store the parent of this pyramid.
* @param name the name of this pyramid, or {@code null} if none.
* @param pyramid information about the pyramid.
- * @param levels the child resources from finest resolution to coarsest resolution.
+ * @param levels the child resources in arbitrary order. This array will be sorted in-place.
* @throws TransformException if an error occurred while deriving a "grid to <abbr>CRS</abbr>" transform.
*/
Pyramid(final GeoHeifStore store, final GenericName name, final ImagePyramid pyramid, final ImageResource[] levels)
@@ -75,9 +87,33 @@ final class Pyramid extends TiledGridCoverageResource implements TiledGridCovera
tileSizeX = pyramid.tileSizeX;
tileSizeY = pyramid.tileSizeY;
this.levels = levels;
+ Arrays.sort(levels, LEVEL_COMPARATOR);
+ /*
+ * Select the finest level for which the "grid to CRS" transform, and therefore the resolution,
+ * is defined. If none, select the very fist level, which should have the finest resolution.
+ */
GridGeometry base = null;
+ boolean updateBase = true;
+ for (final ImageResource level : levels) {
+ final GridGeometry grid = level.getGridGeometry();
+ if (grid.isDefined(GridGeometry.GRID_TO_CRS)) {
+ updateBase = false; // Base is already complete.
+ base = grid;
+ break;
+ } else if (base == null) {
+ base = grid;
+ }
+ }
+ /*
+ * Now compute the "grid to CRS" transform for each pyramid level.
+ * It may include the base level itself if the transform was not specified anywhere.
+ */
for (ImageResource level : levels) {
- base = level.setPyramidLevelOf(base);
+ level.setPyramidLevelOf(base);
+ if (updateBase) {
+ updateBase = false;
+ base = level.getGridGeometry();
+ }
}
}
diff --git a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/CompressionConfiguration.java b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/CompressionConfiguration.java
index 1a050b4356..cb79b3b409 100644
--- a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/CompressionConfiguration.java
+++ b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/CompressionConfiguration.java
@@ -39,11 +39,15 @@ public final class CompressionConfiguration extends FullBox {
/**
* The {@code "zlib"} value for {@link #compressionType}.
+ *
+ * @see <a href="https://www.ietf.org/rfc/rfc1950.pdf">IETF RFC 1950</a>
*/
public static final int COMPRESSION_ZLIB = ((((('z' << 8) | 'l') << 8) | 'i') << 8) | 'b';
/**
* The {@code "defl"} value for {@link #compressionType}.
+ *
+ * @see <a href="https://www.ietf.org/rfc/rfc1951.pdf">IETF RFC 1951</a>
*/
public static final int COMPRESSION_DEFLATE = ((((('d' << 8) | 'e') << 8) | 'f') << 8) | 'l';