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 6fd90af Add a "Satellite ground track" transform (internal only) for
trying to make GCOM data a little bit more linear before to let
LocalizationGridBuilder to its job.
6fd90af is described below
commit 6fd90afa3e5d7053bbf9d79d844646b4f71f4d8c
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Mon Apr 15 22:09:14 2019 +0200
Add a "Satellite ground track" transform (internal only) for trying to make
GCOM data a little bit more linear before to let LocalizationGridBuilder to its
job.
---
.../sis/referencing/datum/DatumShiftGrid.java | 28 ++-
.../operation/builder/LinearTransformBuilder.java | 23 ++
.../operation/builder/LocalizationGridBuilder.java | 94 +++++---
.../operation/transform/ContextualParameters.java | 25 ++-
.../transform/CoordinateSystemTransform.java | 8 +-
.../operation/transform/DatumShiftTransform.java | 6 +-
.../transform/EllipsoidToCentricTransform.java | 2 +-
.../operation/transform/InterpolatedTransform.java | 4 +-
.../builder/LocalizationGridBuilderTest.java | 5 +
.../transform/ContextualParametersTest.java | 8 +-
.../src/main/java/org/apache/sis/math/Vector.java | 8 +-
.../apache/sis/internal/earth/netcdf/GCOM_C.java | 15 ++
.../apache/sis/internal/earth/netcdf/GCOM_W.java | 15 ++
storage/sis-netcdf/pom.xml | 14 ++
.../java/org/apache/sis/internal/netcdf/Axis.java | 33 ++-
.../java/org/apache/sis/internal/netcdf/Grid.java | 2 +-
.../org/apache/sis/internal/netcdf/Linearizer.java | 57 ++++-
.../sis/internal/netcdf/SatelliteGroundTrack.java | 246 +++++++++++++++++++++
.../internal/netcdf/SatelliteGroundTrackTest.java | 81 +++++++
.../org/apache/sis/test/suite/NetcdfTestSuite.java | 1 +
20 files changed, 590 insertions(+), 85 deletions(-)
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java
index e9efe3c..15d8d5d 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java
@@ -417,13 +417,13 @@ public abstract class DatumShiftGrid<C extends
Quantity<C>, T extends Quantity<T
* If the length of the given array is at least <var>n</var> + 4 where
<var>n</var> = {@link #getTranslationDimensions()},
* then this method appends the derivative (approximated) at the given
grid indices. This is the same derivative than the
* one computed by {@link #derivativeInCell(double, double)},
opportunistically computed here for performance reasons.
- * The matrix layout is as below, where <var>u</var> and <var>v</var> are
the coordinates after translation.
+ * The matrix layout is as below, where <var>t₀</var> and <var>t₁</var>
are the coordinates after translation.
*
* {@preformat math
- * ┌ ┐ ┌ ┐
- * │ ∂u/∂x ∂u/∂y │ = │ vector[n+0] vector[n+1] │
- * │ ∂v/∂x ∂v/∂y │ │ vector[n+2] vector[n+3] │
- * └ ┘ └ ┘
+ * ┌ ┐ ┌ ┐
+ * │ ∂t₀/∂x ∂t₀/∂y │ = │ vector[n+0] vector[n+1] │
+ * │ ∂t₁/∂x ∂t₁/∂y │ │ vector[n+2] vector[n+3] │
+ * └ ┘ └ ┘
* }
*
* <div class="section">Default implementation</div>
@@ -504,10 +504,24 @@ public abstract class DatumShiftGrid<C extends
Quantity<C>, T extends Quantity<T
}
/**
- * Estimates the derivative at the given grid indices.
+ * Estimates the derivative at the given grid indices. Derivatives must be
consistent with values given by
+ * {@link #interpolateInCell(double, double, double[])} at adjacent
positions. For a two-dimensional grid,
+ * {@code tₐ(x,y)} an abbreviation for {@code interpolateInCell(gridX,
gridY, …)[a]} and for <var>x</var>
+ * and <var>y</var> integers, the derivative is:
+ *
+ * {@preformat math
+ * ┌ ┐ ┌
┐
+ * │ ∂t₀/∂x ∂t₀/∂y │ = │ t₀(x+1,y) - t₀(x,y) + 1
t₀(x,y+1) - t₀(x,y) │
+ * │ ∂t₁/∂x ∂t₁/∂y │ │ t₁(x+1,y) - t₁(x,y)
t₁(x,y+1) - t₁(x,y) + 1 │
+ * └ ┘ └
┘
+ * }
*
* <div class="section">Extrapolations</div>
- * If the given coordinates is outside the grid, then the derivative will
have some columns set to identity.
+ * Derivatives must be consistent with {@link #interpolateInCell(double,
double, double[])} even when the
+ * given coordinates are outside the grid. The {@code
interpolateInCell(…)} contract in such cases is to
+ * compute the translation vector at the closest position in the grid. A
consequence of this contract is
+ * that translation vectors stay constant when moving along at least one
direction outside the grid.
+ * Consequences on the derivative matrix are as below:
*
* <ul>
* <li>If both {@code gridX} and {@code gridY} are outside the grid,
then the derivative is the identity matrix.</li>
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java
index 2bb4b0d..9883a30 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java
@@ -1052,6 +1052,29 @@ search: for (int j=domain(); --j >= 0;) {
}
/**
+ * Returns the coordinates of a single row or column in the given
dimension. This method can be invoked
+ * only when this {@code LinearTransformBuilder} is known to have been
built for grid source coordinates.
+ *
+ * <div class="note"><b>Note:</b>
+ * while this method is primarily for row and columns, it can be
generalized to more dimensions.</div>
+ *
+ * The returned vector is a view; changes in the returned vector will be
reflected in this builder.
+ *
+ * @param dimension the dimension of source point for which to get
coordinate values.
+ * @param start index of the first coordinate value to get.
+ * @param direction 0 for getting a row, 1 for getting a column.
+ * @return coordinate values for specified row or column in the given
dimension.
+ */
+ final Vector getTransect(final int dimension, final int[] start, final int
direction) {
+ final int first = flatIndex(start);
+ int step = 1;
+ for (int i=0; i<direction; i++) {
+ step *= gridSize[i];
+ }
+ return Vector.create(targets[dimension]).subSampling(first, step,
gridSize[direction] - start[direction]);
+ }
+
+ /**
* Tries to remove discontinuities in coordinates values caused by
anti-meridian crossing. This is the implementation of
* {@link LocalizationGridBuilder#resolveWraparoundAxis(int, int, double)}
public method. See that method for javadoc.
*
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilder.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilder.java
index adda0d8..aca8b7e 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilder.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilder.java
@@ -387,6 +387,46 @@ public class LocalizationGridBuilder extends
TransformBuilder {
}
/**
+ * Returns the envelope of source coordinates. The {@code fullArea}
argument control whether
+ * the returned envelope shall encompass full surface of every cells or
only their centers:
+ * <ul>
+ * <li>If {@code true}, then the returned envelope encompasses full cell
surfaces,
+ * from lower border to upper border. In other words, the returned
envelope encompasses all
+ * {@linkplain org.opengis.referencing.datum.PixelInCell#CELL_CORNER
cell corners}.</li>
+ * <li>If {@code false}, then the returned envelope encompasses only
+ * {@linkplain org.opengis.referencing.datum.PixelInCell#CELL_CENTER
cell centers}, inclusive.</li>
+ * </ul>
+ *
+ * This is the envelope of the grid domain (i.e. the ranges of valid
{@code gridX} and {@code gridY} argument
+ * values in calls to {@code get/setControlPoint(…)} methods) transformed
as below:
+ * <ol>
+ * <li>expanded by ½ cell on each side if {@code fullArea} is {@code
true}</li>
+ * <li>transformed by the inverse of {@linkplain #getSourceToGrid()
source to grid} transform.</li>
+ * </ol>
+ *
+ * @param fullArea whether the the envelope shall encompass the full
cell surfaces instead than only their centers.
+ * @return the envelope of grid points, from lower corner to upper corner.
+ * @throws IllegalStateException if the grid points are not yet known.
+ * @throws TransformException if the envelope can not be calculated.
+ *
+ * @see LinearTransformBuilder#getSourceEnvelope()
+ *
+ * @since 1.0
+ */
+ public Envelope getSourceEnvelope(final boolean fullArea) throws
TransformException {
+ Envelope envelope = linear.getSourceEnvelope();
+ if (fullArea) {
+ for (int i = envelope.getDimension(); --i >= 0;) {
+ final GeneralEnvelope ge =
GeneralEnvelope.castOrCopy(envelope);
+ ge.setRange(i, ge.getLower(i) - 0.5,
+ ge.getUpper(i) + 0.5);
+ envelope = ge;
+ }
+ }
+ return Envelopes.transform(sourceToGrid.inverse(), envelope);
+ }
+
+ /**
* Sets all control points. The length of given vectors must be equal to
the total number of cells in the grid.
* The first vector provides the <var>x</var> coordinates; the second
vector provides the <var>y</var> coordinates,
* <i>etc.</i>. Coordinates are stored in row-major order (column index
varies faster, followed by row index).
@@ -440,43 +480,35 @@ public class LocalizationGridBuilder extends
TransformBuilder {
}
/**
- * Returns the envelope of source coordinates. The {@code fullArea}
argument control whether
- * the returned envelope shall encompass full surface of every cells or
only their centers:
- * <ul>
- * <li>If {@code true}, then the returned envelope encompasses full cell
surfaces,
- * from lower border to upper border. In other words, the returned
envelope encompasses all
- * {@linkplain org.opengis.referencing.datum.PixelInCell#CELL_CORNER
cell corners}.</li>
- * <li>If {@code false}, then the returned envelope encompasses only
- * {@linkplain org.opengis.referencing.datum.PixelInCell#CELL_CENTER
cell centers}, inclusive.</li>
- * </ul>
+ * Returns a row of coordinate values in the given dimension.
+ * The returned vector is a view; changes in the returned vector will be
reflected in this builder.
*
- * This is the envelope of the grid domain (i.e. the ranges of valid
{@code gridX} and {@code gridY} argument
- * values in calls to {@code get/setControlPoint(…)} methods) transformed
as below:
- * <ol>
- * <li>expanded by ½ cell on each side if {@code fullArea} is {@code
true}</li>
- * <li>transformed by the inverse of {@linkplain #getSourceToGrid()
source to grid} transform.</li>
- * </ol>
+ * @param dimension the target dimension for which to get coordinate
values.
+ * @param row index of the row to get.
+ * @return coordinate values of the specified row in the specified
dimension.
*
- * @param fullArea whether the the envelope shall encompass the full
cell surfaces instead than only their centers.
- * @return the envelope of grid points, from lower corner to upper corner.
- * @throws IllegalStateException if the grid points are not yet known.
- * @throws TransformException if the envelope can not be calculated.
+ * @since 1.0
+ */
+ public Vector getRow(final int dimension, final int row) {
+ tmp[0] = 0;
+ tmp[1] = row;
+ return linear.getTransect(dimension, tmp, 0);
+ }
+
+ /**
+ * Returns a column of coordinate values in the given dimension.
+ * The returned vector is a view; changes in the returned vector will be
reflected in this builder.
*
- * @see LinearTransformBuilder#getSourceEnvelope()
+ * @param dimension the target dimension for which to get coordinate
values.
+ * @param column index of the column to get.
+ * @return coordinate values of the specified column in the specified
dimension.
*
* @since 1.0
*/
- public Envelope getSourceEnvelope(final boolean fullArea) throws
TransformException {
- Envelope envelope = linear.getSourceEnvelope();
- if (fullArea) {
- for (int i = envelope.getDimension(); --i >= 0;) {
- final GeneralEnvelope ge =
GeneralEnvelope.castOrCopy(envelope);
- ge.setRange(i, ge.getLower(i) - 0.5,
- ge.getUpper(i) + 0.5);
- envelope = ge;
- }
- }
- return Envelopes.transform(sourceToGrid.inverse(), envelope);
+ public Vector getColumn(final int dimension, final int column) {
+ tmp[0] = column;
+ tmp[1] = 0;
+ return linear.getTransect(dimension, tmp, 1);
}
/**
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
index f7a62cd..99e9d3c 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
@@ -53,8 +53,7 @@ import org.apache.sis.io.wkt.FormattableObject;
import org.apache.sis.io.wkt.Formatter;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.resources.Errors;
-
-import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
+import org.apache.sis.util.ArgumentChecks;
/**
@@ -126,7 +125,7 @@ import static
org.apache.sis.util.ArgumentChecks.ensureNonNull;
* Serialization should be used only for short term storage or RMI between
applications running the same SIS version.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
*
* @see org.apache.sis.referencing.operation.projection.NormalizedProjection
* @see AbstractMathTransform#getContextualParameters()
@@ -247,7 +246,7 @@ public class ContextualParameters extends Parameters
implements Serializable {
* @param method the non-linear operation method for which to define the
parameter values.
*/
public ContextualParameters(final OperationMethod method) {
- ensureNonNull("method", method);
+ ArgumentChecks.ensureNonNull("method", method);
descriptor = method.getParameters();
normalize = linear("sourceDimensions", method.getSourceDimensions());
denormalize = linear("targetDimensions", method.getTargetDimensions());
@@ -255,16 +254,22 @@ public class ContextualParameters extends Parameters
implements Serializable {
}
/**
- * Equivalent to the public constructor, but avoid the need for an {@link
OperationMethod} instance.
+ * Creates a new group of parameters with the given descriptor. This
constructor performs the same construction than
+ * {@link #ContextualParameters(OperationMethod)} but without the need to
specify an {@code OperationMethod} instance.
*
* @param descriptor the parameter descriptor.
- * @param srcSize size of the normalization matrix: source dimensions
+ 1.
- * @param tgtSize size of the denormalization matrix: target
dimensions + 1.
+ * @param srcDim number of source dimensions.
+ * @param tgtDim number of target dimensions.
+ *
+ * @since 1.0
*/
- ContextualParameters(final ParameterDescriptorGroup descriptor, final int
srcSize, final int tgtSize) {
+ public ContextualParameters(final ParameterDescriptorGroup descriptor, int
srcDim, int tgtDim) {
+ ArgumentChecks.ensureNonNull("descriptor", descriptor);
+ ArgumentChecks.ensureStrictlyPositive("srcDim", srcDim);
+ ArgumentChecks.ensureStrictlyPositive("tgtDim", tgtDim);
this.descriptor = descriptor;
- this.normalize = Matrices.create(srcSize, srcSize,
ExtendedPrecisionMatrix.IDENTITY);
- this.denormalize = Matrices.create(tgtSize, tgtSize,
ExtendedPrecisionMatrix.IDENTITY);
+ this.normalize = Matrices.create(++srcDim, srcDim,
ExtendedPrecisionMatrix.IDENTITY);
+ this.denormalize = Matrices.create(++tgtDim, tgtDim,
ExtendedPrecisionMatrix.IDENTITY);
this.values = new
ParameterValue<?>[descriptor.descriptors().size()];
}
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CoordinateSystemTransform.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CoordinateSystemTransform.java
index a773c3f..f363e2e 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CoordinateSystemTransform.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/CoordinateSystemTransform.java
@@ -38,7 +38,6 @@ import org.apache.sis.metadata.iso.citation.Citations;
import org.apache.sis.parameter.DefaultParameterDescriptorGroup;
import org.apache.sis.referencing.cs.AxesConvention;
import org.apache.sis.referencing.cs.CoordinateSystems;
-import org.apache.sis.referencing.operation.DefaultOperationMethod;
/**
@@ -46,7 +45,7 @@ import
org.apache.sis.referencing.operation.DefaultOperationMethod;
* Each subclasses should have a singleton instance.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
* @since 0.7
* @module
*/
@@ -83,10 +82,9 @@ abstract class CoordinateSystemTransform extends
AbstractMathTransform {
*/
CoordinateSystemTransform(final String method, final int dimension) {
this.dimension = dimension;
- final Map<String,?> properties =
Collections.singletonMap(DefaultOperationMethod.NAME_KEY,
+ final Map<String,?> properties =
Collections.singletonMap(DefaultParameterDescriptorGroup.NAME_KEY,
new ImmutableIdentifier(Citations.SIS, Constants.SIS, method));
- context = new ContextualParameters(new
DefaultOperationMethod(properties, dimension, dimension,
- new DefaultParameterDescriptorGroup(properties, 1, 1)));
+ context = new ContextualParameters(new
DefaultParameterDescriptorGroup(properties, 1, 1), dimension, dimension);
}
/**
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DatumShiftTransform.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DatumShiftTransform.java
index 74cfb4e..c027c9c 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DatumShiftTransform.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DatumShiftTransform.java
@@ -121,8 +121,8 @@ public abstract class DatumShiftTransform extends
AbstractMathTransform implemen
* @param grid interpolation grid.
*/
DatumShiftTransform(ParameterDescriptorGroup descriptor, final
DatumShiftGrid<?,?> grid) {
- final int size = grid.getTranslationDimensions() + 1;
- context = new ContextualParameters(descriptor, size, size);
+ final int dim = grid.getTranslationDimensions();
+ context = new ContextualParameters(descriptor, dim, dim);
this.grid = grid;
computeConversionFactors();
}
@@ -139,7 +139,7 @@ public abstract class DatumShiftTransform extends
AbstractMathTransform implemen
DatumShiftTransform(final ParameterDescriptorGroup descriptor,
final boolean isSource3D, final boolean isTarget3D, final
DatumShiftGrid<?,?> grid)
{
- context = new ContextualParameters(descriptor, isSource3D ? 4 : 3,
isTarget3D ? 4 : 3);
+ context = new ContextualParameters(descriptor, isSource3D ? 3 : 2,
isTarget3D ? 3 : 2);
this.grid = grid;
computeConversionFactors();
}
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java
index 845f02a..d3ac78b 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java
@@ -279,7 +279,7 @@ public class EllipsoidToCentricTransform extends
AbstractMathTransform implement
* Copy parameters to the ContextualParameter. Those parameters are
not used directly by
* EllipsoidToCentricTransform, but we need to store them in case the
user asks for them.
*/
- context = new ContextualParameters(GeographicToGeocentric.PARAMETERS,
withHeight ? 4 : 3, 4);
+ context = new ContextualParameters(GeographicToGeocentric.PARAMETERS,
withHeight ? 3 : 2, 3);
context.getOrCreate(SEMI_MAJOR).setValue(semiMajor, unit);
context.getOrCreate(SEMI_MINOR).setValue(semiMinor, unit);
/*
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedTransform.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedTransform.java
index 0f3d4cd..2044551 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedTransform.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedTransform.java
@@ -164,8 +164,8 @@ public class InterpolatedTransform extends
DatumShiftTransform {
Number offset = 0.0;
final Number[] coefficients =
Units.coefficients(normalized.getConverterTo(unit));
switch (coefficients != null ? coefficients.length : -1) {
- case 2: scale = coefficients[1]; // Fall through
- case 1: offset = coefficients[0]; // Fall through
+ case 2: scale = coefficients[1];
// Fall through
+ case 1: offset = coefficients[0];
// Fall through
case 0: break;
default: throw new
IllegalArgumentException(Resources.format(Resources.Keys.NonLinearUnitConversion_2,
normalized, unit));
}
diff --git
a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilderTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilderTest.java
index 11a3201..158d283 100644
---
a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilderTest.java
+++
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilderTest.java
@@ -138,5 +138,10 @@ public final strictfp class LocalizationGridBuilderTest
extends TransformTestCas
assertArrayEquals(new double[] { 0.4, -21.7},
builder.getControlPoint(1, 0), STRICT);
assertArrayEquals(new double[] { 1.3, -8.5},
builder.getControlPoint(0, 2), STRICT);
assertArrayEquals(new double[] { 87.7, -123.7},
builder.getControlPoint(1, 2), STRICT);
+ /*
+ * Verify getting a row and a column.
+ */
+ assertArrayEquals(new double[] {-8.5, -123.7}, builder.getRow(1,
2).doubleValues(), STRICT);
+ assertArrayEquals(new double[] {-21.7, -26.2, -123.7},
builder.getColumn(1, 1).doubleValues(), STRICT);
}
}
diff --git
a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ContextualParametersTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ContextualParametersTest.java
index 2336cf3..eac37f4 100644
---
a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ContextualParametersTest.java
+++
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ContextualParametersTest.java
@@ -16,14 +16,12 @@
*/
package org.apache.sis.referencing.operation.transform;
-import java.util.Collections;
import org.opengis.util.FactoryException;
import org.opengis.parameter.ParameterValue;
import org.opengis.parameter.ParameterNotFoundException;
import org.opengis.referencing.operation.Matrix;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransformFactory;
-import org.apache.sis.referencing.operation.DefaultOperationMethod;
import org.apache.sis.parameter.DefaultParameterDescriptorGroupTest;
import org.apache.sis.referencing.operation.matrix.Matrix3;
import org.apache.sis.test.DependsOnMethod;
@@ -40,7 +38,7 @@ import static org.opengis.test.Assert.*;
* Tests {@link ContextualParameters}.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.6
+ * @version 1.0
* @since 0.6
* @module
*/
@@ -50,9 +48,7 @@ public final strictfp class ContextualParametersTest extends
TestCase {
* Creates an instance to use for testing purpose.
*/
private static ContextualParameters create(final int srcDim, final int
dstDim) {
- return new ContextualParameters(new DefaultOperationMethod(
- Collections.singletonMap(DefaultOperationMethod.NAME_KEY,
"Test method"),
- srcDim, dstDim,
DefaultParameterDescriptorGroupTest.M1_M1_O1_O2));
+ return new
ContextualParameters(DefaultParameterDescriptorGroupTest.M1_M1_O1_O2, srcDim,
dstDim);
}
/**
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 3c035ea..8f9dad0 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
@@ -842,10 +842,10 @@ search: for (;;) {
* <p>This method does not copy the values. Consequently any modification
to the
* values of this vector will be reflected in the returned view and
vice-versa.</p>
*
- * @param first index of the first value to be included in the returned
view.
- * @param step the index increment in this vector between two
consecutive values
- * in the returned vector. Can be positive, zero or
negative.
- * @param length the length of the vector to be returned. Can not be
greater than
+ * @param first index of the first value in this vector to be included
in the returned view.
+ * @param step the index increment between values in this vector to be
included in the returned view.
+ * Can be positive, zero or negative.
+ * @param length the length of the view to be returned. Can not be
greater than
* the length of this vector, except if the {@code step}
is zero.
* @return a view of this vector containing values in the given index
range.
* @throws IndexOutOfBoundsException if {@code first} or {@code first +
step*(length-1)}
diff --git
a/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
b/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
index a10fda0..a17d736 100644
---
a/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
+++
b/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
@@ -16,14 +16,17 @@
*/
package org.apache.sis.internal.earth.netcdf;
+import java.util.Set;
import java.util.Map;
import java.util.HashMap;
+import java.util.Collections;
import java.util.regex.Pattern;
import org.apache.sis.storage.netcdf.AttributeNames;
import org.apache.sis.internal.netcdf.Convention;
import org.apache.sis.internal.netcdf.Decoder;
import org.apache.sis.internal.netcdf.Variable;
import org.apache.sis.internal.netcdf.VariableRole;
+import org.apache.sis.internal.netcdf.Linearizer;
import org.apache.sis.referencing.operation.transform.TransferFunction;
import org.apache.sis.measure.NumberRange;
@@ -306,4 +309,16 @@ public final class GCOM_C extends Convention {
}
return tr;
}
+
+ /**
+ * Returns an enumeration of two-dimensional non-linear transforms that
may be tried in attempts to make
+ * localization grid more linear.
+ *
+ * @param decoder the netCDF file for which to determine linearizers
that may possibly apply.
+ * @return enumeration of two-dimensional non-linear transforms to try.
+ */
+ @Override
+ public Set<Linearizer> linearizers(final Decoder decoder) {
+ return Collections.singleton(Linearizer.GROUND_TRACK);
+ }
}
diff --git
a/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_W.java
b/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_W.java
index 90177bb..9c53ad6 100644
---
a/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_W.java
+++
b/storage/sis-earth-observation/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_W.java
@@ -16,14 +16,17 @@
*/
package org.apache.sis.internal.earth.netcdf;
+import java.util.Set;
import java.util.Map;
import java.util.HashMap;
+import java.util.Collections;
import java.util.regex.Pattern;
import org.apache.sis.storage.netcdf.AttributeNames;
import org.apache.sis.internal.netcdf.Convention;
import org.apache.sis.internal.netcdf.Decoder;
import org.apache.sis.internal.netcdf.Variable;
import org.apache.sis.internal.netcdf.VariableRole;
+import org.apache.sis.internal.netcdf.Linearizer;
import org.apache.sis.referencing.operation.transform.TransferFunction;
@@ -204,4 +207,16 @@ public final class GCOM_W extends Convention {
}
return tr;
}
+
+ /**
+ * Returns an enumeration of two-dimensional non-linear transforms that
may be tried in attempts to make
+ * localization grid more linear.
+ *
+ * @param decoder the netCDF file for which to determine linearizers
that may possibly apply.
+ * @return enumeration of two-dimensional non-linear transforms to try.
+ */
+ @Override
+ public Set<Linearizer> linearizers(final Decoder decoder) {
+ return Collections.singleton(Linearizer.GROUND_TRACK);
+ }
}
diff --git a/storage/sis-netcdf/pom.xml b/storage/sis-netcdf/pom.xml
index 583d3bc..34dd5b5 100644
--- a/storage/sis-netcdf/pom.xml
+++ b/storage/sis-netcdf/pom.xml
@@ -122,6 +122,20 @@
<!-- Leverage GeoAPI tests. -->
<dependency>
+ <groupId>org.apache.sis.core</groupId>
+ <artifactId>sis-referencing</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sis.core</groupId>
+ <artifactId>sis-metadata</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<scope>test</scope>
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 2b8438a..2678593 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
@@ -109,6 +109,8 @@ public final class Axis extends NamedElement {
* <p>A given {@link Grid} should not have two {@code Axis} instances with
equal {@code sourceDimensions} array.
* When {@code sourceDimensions.length} ≧ 2 we may have two {@code Axis}
instances with the same indices in their
* {@code sourceDimensions} arrays, but those indices should be in
different order.</p>
+ *
+ * @see #getMainDirection()
*/
final int[] sourceDimensions;
@@ -245,7 +247,7 @@ public final class Axis extends NamedElement {
* @throws DataStoreException if a logical error occurred.
* @throws ArithmeticException if the size of an axis exceeds {@link
Integer#MAX_VALUE}, or other overflow occurs.
*
- * @see #getDimension()
+ * @see #getMainDirection()
*/
final void mainDimensionFirst(final Axis[] axes, final int count) throws
IOException, DataStoreException {
final int d0 = sourceDimensions[0];
@@ -313,6 +315,26 @@ public final class Axis extends NamedElement {
}
/**
+ * Returns the fastest varying dimension of this "two-dimensional" axis.
+ *
+ * <ul>
+ * <li>If this method returns 0, then axis coordinates vary mostly in
columns.
+ * Or to be more accurate, in first grid dimension (in netCDF order)
associated to this axis.</li>
+ * <li>If this method returns 1, then axis coordinates vary mostly in
rows.
+ * Or to be more accurate, in second grid dimension (in netCDF
order) associated to this axis.</li>
+ * </ul>
+ *
+ * If a grid has <var>n</var> dimensions but we copy in an array of length
2 the dimensions used by this
+ * {@code Axis} instance, while preserving the dimension order as declared
in the netCDF file, then the
+ * value returned by this method is the index of the "main" dimension in
this array of length 2.
+ *
+ * @return 0 or 1, depending on whether coordinates vary mostly on columns
or on rows respectively.
+ */
+ final int getMainDirection() {
+ return (sourceDimensions.length < 2 || sourceDimensions[0] <=
sourceDimensions[1]) ? 0 : 1;
+ }
+
+ /**
* Returns the number of dimension of the localization grid used by this
axis.
* This method returns 2 if this axis if backed by a localization grid
having 2 or more dimensions.
* In the netCDF UCAR library, such axes are handled by a {@link
ucar.nc2.dataset.CoordinateAxis2D}.
@@ -344,6 +366,7 @@ public final class Axis extends NamedElement {
* Returns the {@link #sourceSizes} value at the given index, making sure
it is representable as a
* signed integer value. This method is invoked by operations not designed
for unsigned integers.
*
+ * @param i index of the desired dimension, in the same order than
{@link #sourceDimensions}.
* @throws ArithmeticException if the size can not be represented as a
signed 32 bits integer.
*/
private int getSize(final int i) {
@@ -564,7 +587,7 @@ public final class Axis extends NamedElement {
final boolean trySetTransform(final Matrix gridToCRS, final int
lastSrcDim, final int tgtDim,
final List<MathTransform> nonLinears) throws IOException,
DataStoreException
{
-main: switch (getDimension()) {
+ switch (getDimension()) {
/*
* Defined as a matter of principle, but should never happen.
*/
@@ -609,7 +632,7 @@ main: switch (getDimension()) {
for (int r : repetitions) {
repetitionLength = Math.multiplyExact(repetitionLength, r);
}
- final int ri = (sourceDimensions[0] <= sourceDimensions[1]) ?
0 : 1;
+ final int ri = getMainDirection();
for (int i=0; i<=1; i++) {
final int width = getSize(ri ^ i );
final int height = getSize(ri ^ i ^ 1);
@@ -672,8 +695,8 @@ main: switch (getDimension()) {
*/
final int ri = (xd <= yd) ? 0 : 1; // Take in account
that mainDimensionFirst(…) may have reordered values.
final int ro = (xo <= yo) ? 0 : 1;
- final int width = getSize(ri ^ 1); // Fastest varying is
right-most dimension.
- final int height = getSize(ri ); // Slowest varying if
left-most dimension.
+ final int width = getSize(ri ^ 1); // Fastest varying is
right-most dimension (when in netCDF order).
+ final int height = getSize(ri ); // Slowest varying is
left-most dimension (when in netCDF order).
if (other.sourceSizes[ro ^ 1] == width &&
other.sourceSizes[ro ] == height)
{
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 461cdf8..ac5301c 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
@@ -436,7 +436,7 @@ findFree: for (int srcDim : axis.sourceDimensions) {
if (grid != null) {
final Set<Linearizer> linearizers =
decoder.convention().linearizers(decoder);
if (!linearizers.isEmpty()) {
- Linearizer.applyTo(linearizers, grid,
gridAxes);
+ Linearizer.applyTo(linearizers, factory,
grid, gridAxes);
}
/*
* Replace the first transform by the
two-dimensional localization grid and
diff --git
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Linearizer.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Linearizer.java
index d59bf99..3917825 100644
---
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Linearizer.java
+++
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Linearizer.java
@@ -23,6 +23,7 @@ import org.opengis.util.FactoryException;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.referencing.operation.TransformException;
import org.apache.sis.referencing.operation.builder.LocalizationGridBuilder;
import org.apache.sis.internal.metadata.ReferencingServices;
import org.apache.sis.internal.system.DefaultFactories;
@@ -54,7 +55,12 @@ public enum Linearizer {
* Mercator (Spherical) projection. Inputs are latitude and longitude in
any order (axis order will
* be detected by inspection of {@link Axis} elements). Outputs are
projected coordinates.
*/
- MERCATOR("Mercator (Spherical)");
+ MERCATOR("Mercator (Spherical)"),
+
+ /**
+ * Satellite ground track.
+ */
+ GROUND_TRACK(null);
/**
* The map projection method to use for constructing {@link #transform},
or {@code null} if the operation
@@ -79,6 +85,8 @@ public enum Linearizer {
/**
* Returns the hard-coded transform represented by this enumeration that
may help to make a localization grid
* more linear.
+ *
+ * @return the transform, or {@code null} if it can not be built.
*/
private synchronized MathTransform transform() {
final String p = projection;
@@ -96,12 +104,7 @@ public enum Linearizer {
pg.parameter(Constants.SEMI_MINOR).setValue(ReferencingServices.AUTHALIC_RADIUS);
transform = factory.createParameterizedTransform(pg);
} catch (FactoryException e) {
- /*
- * Should never happen. But if it happens anyway, do not cause
the whole netCDF reader
- * to fail for all files because of this error. Declare this
error as originating from
- * Variable.getGridGeometry() because it is the caller
(indirectly) for this class.
- */
- Logging.unexpectedException(Logging.getLogger(Modules.NETCDF),
Variable.class, "getGridGeometry", e);
+ warning(e);
}
}
return transform;
@@ -110,11 +113,14 @@ public enum Linearizer {
/**
* Applies non-linear transform candidates to the given localization grid.
*
+ * @param factory the factory to use for creating transforms.
* @param linearizers the linearizers to apply.
* @param grid the grid on which to add non-linear transform
candidates.
* @param axes coordinate system axes in CRS order.
*/
- static void applyTo(final Set<Linearizer> linearizers, final
LocalizationGridBuilder grid, final Axis[] axes) {
+ static void applyTo(final Set<Linearizer> linearizers, final
MathTransformFactory factory,
+ final LocalizationGridBuilder grid, final Axis[] axes)
+ {
int xdim = -1, ydim = -1;
for (int i=axes.length; --i >= 0;) {
switch (axes[i].abbreviation) {
@@ -125,11 +131,42 @@ public enum Linearizer {
if (xdim >= 0 && ydim >= 0) {
final Map<String,MathTransform> projections = new HashMap<>();
for (final Linearizer linearizer : linearizers) {
- if (linearizer.transform != null) {
- projections.put(linearizer.name(), linearizer.transform());
+ MathTransform transform;
+ switch (linearizer) {
+ default: {
+ transform = linearizer.transform();
+ break;
+ }
+ /*
+ * Some special cases require information about the
particular grid we have at hand.
+ * Only one case for now, but more cases may be added here
in the future.
+ */
+ case GROUND_TRACK: {
+ int direction = axes[ydim].getMainDirection(); //
Fastest varying dimension (in netCDF order) of latitude.
+ direction ^= 1; //
Convert netCDF order to CRS order.
+ try {
+ transform = SatelliteGroundTrack.create(factory,
grid, xdim, direction);
+ } catch (FactoryException | TransformException e) {
+ transform = null;
+ warning(e);
+ }
+ break;
+ }
+ }
+ if (transform != null) {
+ projections.put(linearizer.name(), transform);
}
}
grid.addLinearizers(projections, xdim, ydim);
}
}
+
+ /**
+ * Reports a warning as originating from {@link
Variable#getGridGeometry()}, because it is the caller
+ * (indirectly) for this class. The warning reported to this method should
never happen. But if one
+ * happens anyway, do not cause the whole netCDF reader to fail for all
files because of this error.
+ */
+ private static void warning(final Exception e) {
+ Logging.unexpectedException(Logging.getLogger(Modules.NETCDF),
Variable.class, "getGridGeometry", e);
+ }
}
diff --git
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/SatelliteGroundTrack.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/SatelliteGroundTrack.java
new file mode 100644
index 0000000..d6eddf0
--- /dev/null
+++
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/SatelliteGroundTrack.java
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.netcdf;
+
+import org.opengis.util.FactoryException;
+import org.opengis.geometry.DirectPosition;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.MathTransform2D;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.referencing.operation.transform.AbstractMathTransform2D;
+import org.apache.sis.referencing.operation.transform.ContextualParameters;
+import org.apache.sis.referencing.operation.builder.LocalizationGridBuilder;
+import org.apache.sis.referencing.operation.matrix.MatrixSIS;
+import org.apache.sis.referencing.operation.matrix.Matrix2;
+import org.apache.sis.parameter.ParameterBuilder;
+import org.apache.sis.geometry.DirectPosition2D;
+import org.apache.sis.internal.util.DoubleDouble;
+import org.apache.sis.internal.util.Constants;
+import org.apache.sis.math.Vector;
+import org.apache.sis.math.Line;
+
+
+/**
+ * An estimation of the position of the satellite for given row and column
indices.
+ * The calculation done in this class is very rough; the intent is not to give
an exact answer,
+ * but to convert grid indices to something roughly proportional to latitudes
and longitudes
+ * in order to make {@link LocalizationGridBuilder} work easier.
+ *
+ * <p>Current implementation is similar to a <a
href="https://en.wikipedia.org/wiki/Sinusoidal_projection">sinusoidal
projection</a>
+ * in which the central meridian is oblique. That "oblique central meridian"
is fitted (by linear regression) to the presumed
+ * satellite trajectory. This model may change in any future SIS version.</p>
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+final class SatelliteGroundTrack extends AbstractMathTransform2D {
+ /**
+ * Parameter descriptor for this transform.
+ */
+ private static final ParameterDescriptorGroup PARAMETERS;
+ static {
+ final ParameterBuilder builder = new
ParameterBuilder().setRequired(true);
+ final ParameterDescriptor<?>[] grids = new ParameterDescriptor<?>[] {
+ builder.addName(Constants.CENTRAL_MERIDIAN +
"_start").create(DirectPosition.class, null),
+ builder.addName(Constants.CENTRAL_MERIDIAN + "_end")
.create(DirectPosition.class, null),
+ };
+ PARAMETERS = builder.addName("Satellite ground
track").createGroup(grids);
+ }
+
+ /**
+ * Parameters describing (at least partially) this transform.
+ * They are used for formatting <cite>Well Known Text</cite> (WKT).
+ *
+ * @see #getContextualParameters()
+ */
+ private final ContextualParameters context;
+
+ /**
+ * Terms of the λ = <var>slope</var>⋅φ + λ₀ equation estimating the
satellite longitude λ for a latitude φ.
+ */
+ private final double λ0, slope;
+
+ /**
+ * The inverse of this transform.
+ */
+ private final MathTransform2D inverse;
+
+ /**
+ * Creates a new instance of this transform.
+ *
+ * @param grid localization grid containing longitude and latitude
coordinates.
+ * @param lonDim dimension of the longitude coordinates in the given
grid.
+ * @param direction 0 if the ground track is on rows, of 1 if it is on
columns.
+ */
+ private SatelliteGroundTrack(final LocalizationGridBuilder grid, final int
lonDim, final int direction) throws TransformException {
+ /*
+ * We presume that the row or column in the middle of the localization
grid give the
+ * coordinates that are closest to coordinates of the actual satellite
ground track.
+ */
+ final int median = (int)
grid.getSourceEnvelope(false).getMedian(direction ^ 1);
+ final Vector longitudes, latitudes;
+ if (direction == 0) {
+ longitudes = grid.getRow(lonDim, median);
+ latitudes = grid.getRow(lonDim ^ 1, median);
+ } else {
+ longitudes = grid.getColumn(lonDim, median);
+ latitudes = grid.getColumn(lonDim ^ 1, median);
+ }
+ final Line line = new Line();
+ line.fit(latitudes, longitudes);
+ λ0 = line.y0(); // Longitude in
degrees.
+ slope = Math.toRadians(line.slope()); // Take the slope
as if all latitudes were given in radians.
+ inverse = new Inverse();
+ context = new ContextualParameters(PARAMETERS, 2, 2);
+ final MatrixSIS normalize =
context.getMatrix(ContextualParameters.MatrixRole.NORMALIZATION);
+ normalize.convertAfter(1, DoubleDouble.createDegreesToRadians(), null);
+ setPositionParameter(Constants.CENTRAL_MERIDIAN + "_start", line,
latitudes.doubleValue(0));
+ setPositionParameter(Constants.CENTRAL_MERIDIAN + "_end", line,
latitudes.doubleValue(latitudes.size() - 1));
+ }
+
+ /**
+ * Sets the {@link DirectPosition} value of a parameter.
+ */
+ private void setPositionParameter(final String name, final Line line,
final double φ) {
+ final DirectPosition2D pos = new DirectPosition2D(φ, line.y(φ));
+ context.parameter(name).setValue(pos);
+ }
+
+ /**
+ * Creates a new instance of this transform, or returns {@code null} if no
instance can be created.
+ * The grid is presumed to contains latitude and longitude coordinates in
decimal degrees.
+ *
+ * @param factory the factory to use for creating transforms.
+ * @param grid localization grid containing longitude and latitude
coordinates.
+ * @param lonDim dimension of the longitude coordinates in the given
grid.
+ * @param direction 0 if the ground track is on rows, of 1 if it is on
columns.
+ */
+ static MathTransform create(final MathTransformFactory factory, final
LocalizationGridBuilder grid,
+ final int lonDim, final int direction) throws TransformException,
FactoryException
+ {
+ final SatelliteGroundTrack tr = new SatelliteGroundTrack(grid, lonDim,
direction);
+ return tr.context.completeTransform(factory, tr);
+ }
+
+ /**
+ * Returns the parameter values for this math transform.
+ */
+ @Override
+ public ParameterValueGroup getParameterValues() {
+ return context;
+ }
+
+ /**
+ * Returns the parameters used for creating the complete transformation.
Those parameters describe a sequence
+ * of <cite>normalize</cite> → {@code this} → <cite>denormalize</cite>
transforms.
+ */
+ @Override
+ protected ContextualParameters getContextualParameters() {
+ return context;
+ }
+
+ /**
+ * Converts a single geographic coordinates into something hopefully more
proportional to grid indices.
+ * For each coordinate tuple in {@code srcPts}, the first coordinate value
is longitude in degrees and
+ * the second value is latitude in <strong>radians</strong>. The
conversion from degrees to radians is
+ * done by the concatenated transform.
+ */
+ @Override
+ public Matrix transform(final double[] srcPts, final int srcOff,
+ final double[] dstPts, final int dstOff,
+ final boolean derivate) throws TransformException
+ {
+ final double λ = srcPts[srcOff ];
+ final double φ = srcPts[srcOff+1];
+ final double cosφ = Math.cos(φ);
+ final double m = φ * slope + λ0; // Central
meridian at the given latitude.
+ final double Δλ = λ - m;
+ if (dstPts != null) {
+ dstPts[dstOff ] = Δλ * cosφ + m; // TODO: use
Math.fma with JDK9.
+ dstPts[dstOff+1] = φ;
+ }
+ if (!derivate) {
+ return null;
+ }
+ final Matrix2 d = new Matrix2();
+ d.m00 = cosφ;
+ d.m01 = slope * (1 - cosφ) - Δλ * Math.sin(φ);
+ return d;
+ }
+
+ /**
+ * Returns the inverse of this transform.
+ */
+ @Override
+ public MathTransform2D inverse() {
+ return inverse;
+ }
+
+ /**
+ * The inverse of {@link SatelliteGroundTrack} transform.
+ */
+ private final class Inverse extends AbstractMathTransform2D.Inverse {
+ /**
+ * Creates a new instance of the inverse transform.
+ */
+ Inverse() {
+ }
+
+ /**
+ * Returns the inverse of this transform, which is the enclosing
{@link SatelliteGroundTrack} transform.
+ */
+ @Override
+ public MathTransform2D inverse() {
+ return SatelliteGroundTrack.this;
+ }
+
+ /**
+ * Converts grid indices to geographic coordinates (not necessarily in
degrees units).
+ * See {@link SatelliteGroundTrack#transform(double[], int, double[],
int, boolean)}
+ * for the units of measurement.
+ */
+ @Override
+ public Matrix transform(final double[] srcPts, final int srcOff,
+ final double[] dstPts, final int dstOff,
+ final boolean derivate) throws
TransformException
+ {
+ final double x = srcPts[srcOff ];
+ final double φ = srcPts[srcOff+1];
+ final double cosφ = Math.cos(φ);
+ final double m = φ * slope + λ0; // Central
meridian at the given latitude.
+ final double Δx = x - m;
+ if (dstPts != null) {
+ dstPts[dstOff ] = Δx / cosφ + m;
+ dstPts[dstOff+1] = φ;
+ }
+ if (!derivate) {
+ return null;
+ }
+ final Matrix2 d = new Matrix2();
+ d.m00 = 1 / cosφ;
+ d.m01 = (Δx * Math.sin(φ) / cosφ - slope) / cosφ + slope;
+ return d;
+ }
+ }
+}
diff --git
a/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/SatelliteGroundTrackTest.java
b/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/SatelliteGroundTrackTest.java
new file mode 100644
index 0000000..c2fdad6
--- /dev/null
+++
b/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/SatelliteGroundTrackTest.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.netcdf;
+
+import java.awt.geom.AffineTransform;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.apache.sis.referencing.operation.builder.LocalizationGridBuilder;
+import org.apache.sis.referencing.operation.transform.MathTransformTestCase;
+import org.apache.sis.referencing.operation.transform.CoordinateDomain;
+import org.apache.sis.internal.system.DefaultFactories;
+import org.junit.Test;
+
+
+/**
+ * Tests {@link SatelliteGroundTrack}. There is no external data that we can
use as a reference.
+ * Consequently this test merely verifies that {@link SatelliteGroundTrack} is
self-consistent.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+public final strictfp class SatelliteGroundTrackTest extends
MathTransformTestCase {
+ /**
+ * Size of the grid created for testing purpose.
+ */
+ private static final int WIDTH = 20, HEIGHT = 20;
+
+ /**
+ * Creates a transform for a grid of fixed size in a geographic domain.
+ */
+ private void createTransform() throws TransformException, FactoryException
{
+ final LocalizationGridBuilder grid = new
LocalizationGridBuilder(WIDTH, HEIGHT);
+ final AffineTransform tr =
AffineTransform.getRotateInstance(StrictMath.random()/2 + 0.25); // Between
14 and 43°.
+ tr.translate(-WIDTH / 2, -HEIGHT / 2);
+ final double[] point = new double[2];
+ for (int y=0; y<HEIGHT; y++) {
+ for (int x=0; x<WIDTH; x++) {
+ point[0] = x;
+ point[1] = y;
+ tr.transform(point, 0, point, 0, 1);
+ grid.setControlPoint(x, y, point);
+ }
+ }
+ transform =
SatelliteGroundTrack.create(DefaultFactories.forBuildin(MathTransformFactory.class),
grid, 1, 1);
+ tolerance = 1E-12;
+ derivativeDeltas = new double[] {0.1, 0.1};
+ }
+
+ /**
+ * Tests self-consistency at random points.
+ *
+ * @throws FactoryException if an error occurred while creating the
transform.
+ * @throws TransformException if an error occurred while transforming a
point.
+ */
+ @Test
+ public void testConsistency() throws TransformException, FactoryException {
+ createTransform();
+ validate();
+ verifyInDomain(CoordinateDomain.RANGE_10, -979924465940961910L);
+ transform = transform.inverse();
+ validate();
+ verifyInDomain(CoordinateDomain.RANGE_10, -2122465178330330413L);
+ }
+}
diff --git
a/storage/sis-netcdf/src/test/java/org/apache/sis/test/suite/NetcdfTestSuite.java
b/storage/sis-netcdf/src/test/java/org/apache/sis/test/suite/NetcdfTestSuite.java
index 5074f24..90537e9 100644
---
a/storage/sis-netcdf/src/test/java/org/apache/sis/test/suite/NetcdfTestSuite.java
+++
b/storage/sis-netcdf/src/test/java/org/apache/sis/test/suite/NetcdfTestSuite.java
@@ -34,6 +34,7 @@ import org.junit.BeforeClass;
org.apache.sis.internal.netcdf.DecoderTest.class,
org.apache.sis.internal.netcdf.VariableTest.class,
org.apache.sis.internal.netcdf.GridTest.class,
+ org.apache.sis.internal.netcdf.SatelliteGroundTrackTest.class,
org.apache.sis.internal.netcdf.impl.ChannelDecoderTest.class,
org.apache.sis.internal.netcdf.impl.VariableInfoTest.class,
org.apache.sis.internal.netcdf.impl.GridInfoTest.class,