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
commit e88fd77241f423f60c06b504a3b9a9c39e54acb9 Author: Martin Desruisseaux <[email protected]> AuthorDate: Thu Oct 8 12:57:22 2026 +0200 Rename GeoTIFF `Localization` class as `ModelTiePoints` and use the class added in previous commit for tabular formatting. --- .../apache/sis/storage/geotiff/NativeMetadata.java | 9 ++- .../geotiff/reader/GridGeometryBuilder.java | 6 +- .../{Localization.java => ModelTiePoints.java} | 77 +++++++++++++++++++--- ...calizationTest.java => ModelTiePointsTest.java} | 14 ++-- 4 files changed, 86 insertions(+), 20 deletions(-) diff --git a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/NativeMetadata.java b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/NativeMetadata.java index 39c7a23780..6f7abffbfc 100644 --- a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/NativeMetadata.java +++ b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/NativeMetadata.java @@ -16,7 +16,6 @@ */ package org.apache.sis.storage.geotiff; -import java.util.Set; import java.util.HashSet; import java.util.Map; import java.util.LinkedHashMap; @@ -41,6 +40,7 @@ import org.apache.sis.storage.geotiff.base.GeoKeys; import org.apache.sis.storage.geotiff.base.Tags; import org.apache.sis.storage.geotiff.reader.Type; import org.apache.sis.storage.geotiff.reader.GeoKeysLoader; +import org.apache.sis.storage.geotiff.reader.ModelTiePoints; import org.apache.sis.storage.geotiff.reader.XMLMetadata; @@ -116,7 +116,7 @@ final class NativeMetadata extends GeoKeysLoader { input.mark(); try { input.seek(isClassic ? 2*Short.BYTES : 4*Short.BYTES); - final Set<Long> doneIFD = new HashSet<>(); + final var doneIFD = new HashSet<Long>(); long nextIFD; /* * Following loop is a simplified copy of `Reader.getImageFileDirectory(int)` method, @@ -187,6 +187,10 @@ final class NativeMetadata extends GeoKeysLoader { } break; } + case (short) TAG_MODEL_TIE_POINT: { + value = new ModelTiePoints(type.readAsVector(input, count)); + break; + } default: { value = type.readAsObject(input, count); if (value instanceof Vector) { @@ -278,6 +282,7 @@ final class NativeMetadata extends GeoKeysLoader { } return String.join(", ", items); }); + break; } } } diff --git a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/GridGeometryBuilder.java b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/GridGeometryBuilder.java index cf6abb4b17..7e779168f7 100644 --- a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/GridGeometryBuilder.java +++ b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/GridGeometryBuilder.java @@ -228,7 +228,7 @@ public final class GridGeometryBuilder extends GeoKeysLoader { int nearest = 0; // Index of the record nearest to origin. double distance = Double.POSITIVE_INFINITY; // Distance squared of the nearest record. final int size = modelTiePoints.size(); - for (int i=0; i<size; i += Localization.RECORD_LENGTH) { + for (int i=0; i<size; i += ModelTiePoints.RECORD_LENGTH) { double t; final double d = (t = modelTiePoints.doubleValue(i )) * t + (t = modelTiePoints.doubleValue(i + 1)) * t @@ -252,7 +252,7 @@ public final class GridGeometryBuilder extends GeoKeysLoader { final int trCol = affine.getNumCol() - 1; for (int j=0; j<numDim; j++) { final double src = -modelTiePoints.doubleValue(nearest + j); - final double tgt = modelTiePoints.doubleValue(nearest + j + Localization.RECORD_LENGTH / BIDIMENSIONAL); + final double tgt = modelTiePoints.doubleValue(nearest + j + ModelTiePoints.RECORD_LENGTH / BIDIMENSIONAL); var t = DoubleDouble.of(src, decimal).multiply(affine.getNumber(j,j), decimal).add(tgt, decimal); affine.setNumber(j, trCol, t); } @@ -362,7 +362,7 @@ public final class GridGeometryBuilder extends GeoKeysLoader { gridToCRS = factory.createAffineTransform(m); } else if (modelTiePoints != null) { anchor = PixelInCell.CELL_CENTER; - gridToCRS = Localization.nonLinear(modelTiePoints); + gridToCRS = new ModelTiePoints(modelTiePoints).nonLinear(); gridToCRS = factory.createPassThroughTransform(0, gridToCRS, spatialDimension - BIDIMENSIONAL); if (temporalCRS != null) { gridToCRS = MathTransforms.compound(gridToCRS, MathTransforms.linear(Double.NaN, timeCoordinate)); diff --git a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/Localization.java b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/ModelTiePoints.java similarity index 83% rename from endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/Localization.java rename to endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/ModelTiePoints.java index 731c24b302..c65aaa000b 100644 --- a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/Localization.java +++ b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/reader/ModelTiePoints.java @@ -21,6 +21,9 @@ import java.util.Set; import java.util.Map; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.Objects; +import java.util.Locale; +import java.text.Format; import org.opengis.geometry.Envelope; import org.opengis.util.FactoryException; import org.opengis.referencing.operation.MathTransform; @@ -29,20 +32,19 @@ import org.apache.sis.referencing.operation.transform.MathTransforms; import org.apache.sis.referencing.operation.transform.LinearTransform; import org.apache.sis.referencing.operation.builder.LocalizationGridBuilder; import org.apache.sis.referencing.factory.InternalFactoryException; +import org.apache.sis.util.internal.shared.TableRowList; import org.apache.sis.math.Vector; /** * The conversion or transformation from pixel coordinates to model coordinates. + * Used for analyzing the {@code TAG_MODEL_TIE_POINT} data from a GeoTIFF file. * The target CRS may be the image CRS if the image is "georeferenceable" instead of georeferenced. * - * This code is provided in a separated class for making easier to move it to some shared location - * if another data store needs similar functionality in the future. - * * @author Martin Desruisseaux (Geomatys) * @author Jonatas Fischer */ -final class Localization { +public final class ModelTiePoints extends TableRowList<Vector, Double> { /** * Number of floating point values in each (I,J,K,X,Y,Z) record. */ @@ -57,9 +59,68 @@ final class Localization { private static final double PRECISION = 1E-6; /** - * Do not allow instantiation of this class. + * Coordinates of the model tie points read from GeoTIFF file. + */ + private final Vector coordinates; + + /** + * Creates a new localization grid. + * + * @param coordinates the model tie points read from GeoTIFF file. + */ + public ModelTiePoints(final Vector coordinates) { + this.coordinates = coordinates; + } + + /** + * Returns the number of rows in the model tie points. + * + * @return number of (I,J,K,X,Y,Z) records. + */ + @Override + public int size() { + return coordinates.size() / RECORD_LENGTH; + } + + /** + * Returns the column headers. + * The returned array length is {@link #RECORD_LENGTH}. + */ + @Override + public String[] columns() { + return new String[] {"i", "j", "k", "x", "y", "z"}; + } + + /** + * Returns the value in the specified row and column. + */ + @Override + public Double get(final int row, final int column) { + Objects.checkIndex(row, size()); + Objects.checkIndex(column, RECORD_LENGTH); + return coordinates.doubleValue(row * RECORD_LENGTH + column); + } + + /** + * Returns the source and (I,J,K,X,Y,Z) record in the given row. + */ + @Override + public Vector get(int row) { + Objects.checkIndex(row, size()); + row *= RECORD_LENGTH; + return coordinates.subList(row, row + RECORD_LENGTH); + } + + /** + * Returns the format to use for the given column. + * + * @param locale the locale of the format to create. + * @param column the column for which to get a format. */ - private Localization() { + @Override + public Format createFormat(final Locale locale, final int column) { + return coordinates.subSampling(Objects.checkIndex(column, RECORD_LENGTH), RECORD_LENGTH, size()) + .createNumberFormat(locale); } /** @@ -69,8 +130,8 @@ final class Localization { * @param modelTiePoints the tie points to use for computing {@code gridToCRS}. * @return the grid geometry created from above properties. Never null. */ - static MathTransform nonLinear(final Vector modelTiePoints) throws FactoryException, TransformException { - return localizationGrid(modelTiePoints, null); + final MathTransform nonLinear() throws FactoryException, TransformException { + return localizationGrid(coordinates, null); } /** diff --git a/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/reader/LocalizationTest.java b/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/reader/ModelTiePointsTest.java similarity index 95% rename from endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/reader/LocalizationTest.java rename to endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/reader/ModelTiePointsTest.java index 111ea6a9a9..f95781e93e 100644 --- a/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/reader/LocalizationTest.java +++ b/endorsed/src/org.apache.sis.storage.geotiff/test/org/apache/sis/storage/geotiff/reader/ModelTiePointsTest.java @@ -38,7 +38,7 @@ import org.apache.sis.test.TestCase; * * @author Jonatas Fischer */ -public final class LocalizationTest extends TestCase { +public final class ModelTiePointsTest extends TestCase { /** * Tolerance threshold, in degrees, when comparing tie point coordinates. * This is about one metre, while the tie points of the grids tested here @@ -55,7 +55,7 @@ public final class LocalizationTest extends TestCase { /** * Creates a new test case. */ - public LocalizationTest() { + public ModelTiePointsTest() { } /** @@ -95,7 +95,7 @@ public final class LocalizationTest extends TestCase { /** * Returns the pixel coordinates of {@value #GRID_SIZE} tie points spaced by the given step, * except the last point which is closer to its predecessor. This is the spacing of Sentinel 1 - * images, and the reason why {@link Localization} splits irregular grids in four parts. + * images, and the reason why {@link ModelTiePoints} splits irregular grids in four parts. * * @param step step between two consecutive tie points, except the last two. * @param last step between the two last tie points. @@ -121,7 +121,7 @@ public final class LocalizationTest extends TestCase { * @return the (I,J,K,X,Y,Z) records of the tie points. */ private static Vector tiePoints(final double[] columns, final double[] rows) { - final double[] records = new double[columns.length * rows.length * Localization.RECORD_LENGTH]; + final double[] records = new double[columns.length * rows.length * ModelTiePoints.RECORD_LENGTH]; int p = 0; for (final double y : rows) { for (final double x : columns) { @@ -147,11 +147,11 @@ public final class LocalizationTest extends TestCase { */ private static void verify(final double[] columns, final double[] rows) throws Exception { final Vector tiePoints = tiePoints(columns, rows); - final MathTransform gridToCRS = Localization.nonLinear(tiePoints); + final MathTransform gridToCRS = new ModelTiePoints(tiePoints).nonLinear(); assertNotNull(gridToCRS); final double[] source = new double[2]; final double[] target = new double[2]; - for (int i=0; i<tiePoints.size(); i += Localization.RECORD_LENGTH) { + for (int i=0; i<tiePoints.size(); i += ModelTiePoints.RECORD_LENGTH) { source[0] = tiePoints.doubleValue(i); source[1] = tiePoints.doubleValue(i+1); gridToCRS.transform(source, 0, target, 0, 1); @@ -194,7 +194,7 @@ public final class LocalizationTest extends TestCase { /** * Tests a grid where the steps differ by one pixel on a single axis. Only two of the four parts - * in which {@code Localization} would split such a grid receive points; the empty parts shall + * in which {@code ModelTiePoints} would split such a grid receive points; the empty parts shall * not cause an {@link IndexOutOfBoundsException}. * * <p>This is the case of ICEYE Single Look Complex images of 114644 × 16714 pixels:
