Class DatumShiftGrid<C extends Quantity<C>,T extends Quantity<T>>
- Type Parameters:
C
- dimension of the coordinate unit (usuallyAngle
).T
- dimension of the translation unit (usuallyAngle
orLength
).
- All Implemented Interfaces:
Serializable
Geodetic datum changes can be implemented by translations in geographic
or geocentric coordinates. Translations given by DatumShiftGrid
instances are often, but not always,
applied directly on geographic coordinates (λ,φ). But some algorithms rather apply the
translations in geocentric coordinates (X,Y,Z). This DatumShiftGrid
class can describe both cases, but will be used with different MathTransform
implementations.
Steps for calculation of a translation vector:
- Coordinates are given in some "real world" unit.
The expected unit is given by
getCoordinateUnit()
. - Above coordinates are converted to grid indices including fractional parts.
That conversion is given by
getCoordinateToGrid()
. - Translation vectors are interpolated at the position of above grid indices.
That interpolation is done by
interpolateInCell(…)
. - If the above translations were given as a ratio of the real translation divided by the size of grid cells, apply
the inverse of the conversion given at step 2. This information is given by
isCellValueRatio()
. - The resulting translation vectors are in the unit given by
getTranslationUnit()
.
interpolateAt(…)
method performs all those steps.
But that method is provided only for convenience; it is not used by Apache SIS.
For performance reasons SIS MathTransform
implementations perform all the above-cited steps themselves,
and apply the interpolated translations on coordinate values in their own step between above steps 3 and 4.
Use cases
- Datum shift by geographic translations
NADCON and NTv2 grids are defined with longitude (λ) and latitude (φ) inputs in angular degrees and give (Δλ, Δφ) translations in angular seconds. However, SIS stores the translation values in units of grid cell rather than angular seconds. The translations will be applied byInterpolatedTransform
directly on the given (λ,φ) coordinates. - Datum shift by geocentric translations
France interpolation grid is defined with longitude (λ) and latitude (φ) inputs in angular degrees and gives (ΔX, ΔY, ΔZ) geocentric translations in metres. Those translations will not be added directly to the given (λ,φ) coordinates since there is a geographic/geocentric conversion in the middle (seeInterpolatedGeocentricTransform
). - Localization grid of raster data
Some remote sensing raster data are provided with a localization grid giving pixel coordinates (e.g. latitude and longitude). This can be seen as a change from image datum to geodetic datum. The coordinate transformation process can sometimes be performed by a mathematical conversion (for example an affine transform) applied as a first approximation, followed by small corrections for the residual part.DatumShiftGrid
can describe the small corrections part.
Number of dimensions
Input coordinates and translation vectors can have any number of dimensions. However, in the current implementation, only the two first dimensions are used for interpolating the translation vectors. This restriction appears in the following field and method signatures:INTERPOLATED_DIMENSIONS
.getCellValue(int, int, int)
where the two lastint
values are (x,y) grid indices.interpolateInCell(double, double, double[])
where the two firstdouble
values are (x,y) grid indices.derivativeInCell(double, double)
where the values are (x,y) grid indices.
DatumShiftGrid
to interpolate translation vectors
in more than two dimensions. See the above datum shift by geocentric translations use case for
an example.
Longitude wraparound
Some grids are defined over an area beyond the [−180° … +180°] range of longitudes. For example, NADCON grid for Alaska is defined in a [−194° … −127.875°] range, in which case a longitude of 170° needs to be replaced by −190° before it can be processed by the grid. The defaultDatumShiftGrid
class does not apply longitude wraparound automatically
(it does not even know which axis, if any, is longitude),
but subclasses can add this support by overriding the replaceOutsideGridCoordinates(double[])
method.
Sub-grids
Some datum shift grid files provide a grid valid on a wide region, refined with denser sub-grids in smaller regions. For each point to transform, theMathTransform
should search and use the
densest sub-grid containing the point. This functionality is not supported directly by DatumShiftGrid
,
but can be achieved by organizing many transforms in a tree. The first step is to create an instance of
InterpolatedTransform
for each DatumShiftGrid
.
Then, those transforms with their domain of validity can be given to
MathTransforms.specialize(…)
.
Serialization
Serialized objects of this class are not guaranteed to be compatible with future Apache SIS releases. Serialization support is appropriate for short term storage or RMI between applications running the same version of Apache SIS. But for long term storage, an established datum shift grid format like NTv2 should be preferred.Multi-threading
Implementations of this class shall be immutable and thread-safe.- Since:
- 0.7
- See Also:
-
Field Summary
Modifier and TypeFieldDescriptionprotected static final int
Number of source dimensions in which interpolations are applied. -
Constructor Summary
ModifierConstructorDescriptionprotected
DatumShiftGrid
(Unit<C> coordinateUnit, LinearTransform coordinateToGrid, int[] gridSize, boolean isCellValueRatio, Unit<T> translationUnit) Creates a new datum shift grid for the given size and units.protected
DatumShiftGrid
(DatumShiftGrid<C, T> other) Creates a new datum shift grid with the same grid geometry (size and units) than the given grid. -
Method Summary
Modifier and TypeMethodDescriptionderivativeInCell
(double gridX, double gridY) Estimates the derivative at the given grid indices.boolean
Returnstrue
if the given object is a grid containing the same data than this grid.double
getCellMean
(int dim) Returns an average translation value for the given dimension.abstract double
Returns an estimation of cell value precision (not to be confused with accuracy).abstract double
getCellValue
(int dim, int gridX, int gridY) Returns the translation stored at the given two-dimensional grid indices for the given dimension.Returns the conversion from the source coordinates (in "real world" units) to grid indices.Returns the unit of measurement of input values, before conversion to grid indices.Returns the domain of validity of input coordinates that can be specified to theinterpolateAt(…)
method.getDomainOfValidity
(Unit<C> unit) Returns the domain of validity converted to the specified unit of measurement.int[]
Returns the number of cells along each axis in the grid.int
getGridSize
(int dimension) Returns the number of cells in the specified dimension.abstract ParameterDescriptorGroup
Returns a description of the values in this grid.abstract void
getParameterValues
(Parameters parameters) Gets the parameter values for the grids and stores them in the providedparameters
group.abstract int
Returns the number of dimensions of the translation vectors interpolated by this datum shift grid.Returns the unit of measurement of output values, as interpolated by theinterpolateAt(…)
method.int
Returns a hash code value for this datum shift grid.double[]
interpolateAt
(double... coordinates) Interpolates the translation to apply for the given coordinates.void
interpolateInCell
(double gridX, double gridY, double[] vector) Interpolates the translation to apply for the given two-dimensional grid indices.boolean
isCellInGrid
(double gridX, double gridY) Returnstrue
if the given grid coordinates is inside this grid.boolean
Returnstrue
if the translation values in the cells are divided by the cell size.protected void
replaceOutsideGridCoordinates
(double[] gridCoordinates) Invoked when agridX
orgridY
coordinate is outside the range of valid grid coordinates.Returns a string representation of thisDatumShiftGrid
for debugging purposes.
-
Field Details
-
INTERPOLATED_DIMENSIONS
protected static final int INTERPOLATED_DIMENSIONSNumber of source dimensions in which interpolations are applied. The grids may have more dimensions, but only this number of dimensions will be used in interpolations. The value of this field is set to 2. That value is hard-coded not only in this field, but also in signature of various methods expecting a two-dimensional (x, y) position:getCellValue(…, x, y)
,interpolateInCell(x, y, …)
,derivativeInCell(x, y)
.Future evolution
If this class is generalized to more source dimensions in a future Apache SIS version, then this field may be deprecated or its value changed. That change would be accompanied by new methods with different signature. This field can be used as a way to detect that such change occurred.- Since:
- 1.0
- See Also:
-
-
Constructor Details
-
DatumShiftGrid
protected DatumShiftGrid(Unit<C> coordinateUnit, LinearTransform coordinateToGrid, int[] gridSize, boolean isCellValueRatio, Unit<T> translationUnit) Creates a new datum shift grid for the given size and units. The actual cell values need to be provided by subclasses.Meaning of argument values is documented more extensively in
getCoordinateUnit()
,getCoordinateToGrid()
,isCellValueRatio()
andgetTranslationUnit()
methods. The argument order is roughly the order in which they are used in the process of interpolating translation vectors.- Parameters:
coordinateUnit
- the unit of measurement of input values, before conversion to grid indices bycoordinateToGrid
.coordinateToGrid
- conversion from the "real world" coordinates to grid indices including fractional parts.gridSize
- number of cells along each axis in the grid. The length of this array shall be equal tocoordinateToGrid
target dimensions.isCellValueRatio
-true
if results ofinterpolateInCell(…)
are divided by grid cell size.translationUnit
- the unit of measurement of output values.
-
DatumShiftGrid
Creates a new datum shift grid with the same grid geometry (size and units) than the given grid.- Parameters:
other
- the other datum shift grid from which to copy the grid geometry.
-
-
Method Details
-
getGridSize
public int[] getGridSize()Returns the number of cells along each axis in the grid. The length of this array is the number of grid dimensions, which is typically 2<C extends Quantity<C>,T extends Quantity<T>>. The grid dimensions shall be equal to coordinateToGrid
target dimensions. That number of grid dimensions is not necessarily equal to the number of dimension of the translation vectors.- Returns:
- the number of cells along each axis in the grid.
-
getGridSize
public int getGridSize(int dimension) Returns the number of cells in the specified dimension. Invoking this method is equivalent togetGridSize()[dimension]
.- Parameters:
dimension
- the dimension for which to get the grid size.- Returns:
- the number of grid cells in the specified dimension.
- Throws:
IndexOutOfBoundsException
- if the given index is out of bounds.- Since:
- 1.1
-
getDomainOfValidity
Returns the domain of validity of input coordinates that can be specified to theinterpolateAt(…)
method. Coordinates outside that domain will still be accepted, but results may be extrapolations far from reality. This method does not take in account longitude wraparound (i.e. the returned envelope may cross the ±180° meridian).The envelope coordinates are computed at cell centers; the envelope does not contain the margin of 0.5 cell between cell center and cell border at the edges of the envelope. The unit of measurement for the coordinate values in the returned envelope is given by
getCoordinateUnit()
. The envelope CRS is not set, but its value is implicitly the CRS of grid input coordinates.- Returns:
- the domain covered by this grid.
- Throws:
TransformException
- if an error occurred while computing the envelope.
-
getDomainOfValidity
Returns the domain of validity converted to the specified unit of measurement. A common use case for this method is for converting the domain of a NADCON or NTv2 datum shift grid file, which are expressed inUnits.ARC_SECOND
, toUnits.DEGREE
.- Parameters:
unit
- the desired unit of measurement.- Returns:
- the domain covered by this grid, converted to the given unit of measurement.
- Throws:
TransformException
- if an error occurred while computing the envelope.- Since:
- 1.1
-
getCoordinateUnit
Returns the unit of measurement of input values, before conversion to grid indices. The coordinate unit is usuallyUnits.DEGREE
, but other units are allowed.- Returns:
- the unit of measurement of input values before conversion to grid indices.
- See Also:
-
getCoordinateToGrid
Returns the conversion from the source coordinates (in "real world" units) to grid indices. The input coordinates given to theLinearTransform
shall be in the unit of measurement given bygetCoordinateUnit()
. The output coordinates are grid indices as real numbers (i.e. can have a fractional part). Integer grid indices are located in the center of grid cells, i.e. the transform usesPixelInCell.CELL_CENTER
convention.This transform is usually two-dimensional, in which case conversions from (x,y) coordinates to (
gridX
,gridY
) indices can be done with the following formulas:- gridX = (x - x₀) / Δx
- gridY = (y - y₀) / Δy
- (x₀, y₀) is the coordinate of the center of the cell at grid index (0,0).
- Δx and Δy are the distances between two cells on the x and y
axes respectively, in the unit of measurement given by
getCoordinateUnit()
.
coordinateToGrid
transform for the above formulas can be represented by the following matrix:┌ ┐ │ 1/Δx 0 -x₀/Δx │ │ 0 1/Δy -y₀/Δy │ │ 0 0 1 │ └ ┘
- Returns:
- conversion from the "real world" coordinates to grid indices including fractional parts.
-
getTranslationDimensions
public abstract int getTranslationDimensions()Returns the number of dimensions of the translation vectors interpolated by this datum shift grid. This number of dimensions is not necessarily equals to the number of source or target dimensions of the "coordinate to grid" transform. The number of translation dimensions is usually 2 or 3, but other values are allowed.- Returns:
- number of dimensions of translation vectors.
-
getTranslationUnit
Returns the unit of measurement of output values, as interpolated by theinterpolateAt(…)
method. Apache SISMathTransform
implementations restrict the translation units to the following values:- For
InterpolatedTransform
, the translation unit shall be the same than the coordinate unit. - For
InterpolatedGeocentricTransform
, the translation unit shall be the same than the unit of source ellipsoid axis lengths.
- Returns:
- the unit of measurement of output values interpolated by
interpolateAt(…)
. - See Also:
- For
-
interpolateAt
Interpolates the translation to apply for the given coordinates. The input values are in the unit given bygetCoordinateUnit()
. The output values are in the unit given bygetTranslationUnit()
. The length of the returned array is given bygetTranslationDimensions()
.Default implementation
The default implementation performs the following steps:- Convert the given coordinate into grid indices using the transform given by
getCoordinateToGrid()
. - Interpolate the translation vector at the above grid indices with a call to
interpolateInCell(double, double, double[])
. - If
isCellValueRatio()
returnstrue
, delta transform the translation vector by the inverse of the conversion given at step 1.
interpolateInCell(double, double, double[])
.- Parameters:
coordinates
- the "real world" coordinate (often longitude and latitude, but not necessarily) of the point for which to get the translation.- Returns:
- the translation vector at the given position.
- Throws:
TransformException
- if an error occurred while computing the translation vector.
- Convert the given coordinate into grid indices using the transform given by
-
interpolateInCell
public void interpolateInCell(double gridX, double gridY, double[] vector) Interpolates the translation to apply for the given two-dimensional grid indices. The result is stored in the givenvector
array, which shall have a length of at leastgetTranslationDimensions()
. The output unit of measurement is the same than the one documented ingetCellValue(int, int, int)
.Extrapolations
If the given coordinates are outside this grid, then this method computes the translation vector at the closest position in the grid. Applying translations on points outside the grid is a kind of extrapolation, but some amount of extrapolations are necessary for operations like transforming an envelope before to compute its intersection with another envelope.Derivative (Jacobian matrix)
If the length of the given array is at least n + 4 where n =getTranslationDimensions()
, then this method appends the derivative (approximated) at the given grid indices. This is the same derivative than the one computed byderivativeInCell(double, double)
, opportunistically computed here for performance reasons. The matrix layout is as below, where t₀ and t₁ are the coordinates after translation.┌ ┐ ┌ ┐ │ ∂t₀/∂x ∂t₀/∂y │ = │ vector[n+0] vector[n+1] │ │ ∂t₁/∂x ∂t₁/∂y │ │ vector[n+2] vector[n+3] │ └ ┘ └ ┘
Default implementation
The default implementation performs the following steps for each dimension dim, where the number of dimension is determined bygetTranslationDimensions()
.- Clamp the
gridX
index into the [0 …gridSize[0]
- 1] range, inclusive. - Clamp the
gridY
index into the [0 …gridSize[1]
- 1] range, inclusive. - Using
getCellValue(int, int, int)
, get the cell values around the given indices. - Apply a bilinear interpolation and store the result in
vector[dim]
. - Appends Jacobian matrix coefficients if the
vector
length is sufficient.
- Parameters:
gridX
- first grid coordinate of the point for which to get the translation.gridY
- second grid coordinate of the point for which to get the translation.vector
- a pre-allocated array where to write the translation vector.- See Also:
- Clamp the
-
derivativeInCell
Estimates the derivative at the given grid indices. Derivatives must be consistent with values given byinterpolateInCell(double, double, double[])
at adjacent positions. For a two-dimensional grid,tₐ(x,y)
an abbreviation forinterpolateInCell(gridX, gridY, …)[a]
and for x and y integers, the derivative is:┌ ┐ ┌ ┐ │ ∂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 │ └ ┘ └ ┘
Extrapolations
Derivatives must be consistent withinterpolateInCell(double, double, double[])
even when the given coordinates are outside the grid. TheinterpolateInCell(…)
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:- If both
gridX
andgridY
are outside the grid, then the derivative is the identity matrix. - If only
gridX
is outside the grid, then only the first column is set to [1, 0, …]. The second column is set to the derivative of the closest cell atgridY
position. - If only
gridY
is outside the grid, then only the second column is set to [0, 1, …]. The first column is set to the derivative of the closest cell atgridX
position.
- Parameters:
gridX
- first grid coordinate of the point for which to get the translation.gridY
- second grid coordinate of the point for which to get the translation.- Returns:
- the derivative at the given location.
- See Also:
- If both
-
getCellValue
public abstract double getCellValue(int dim, int gridX, int gridY) Returns the translation stored at the given two-dimensional grid indices for the given dimension. The returned value is considered representative of the value in the center of the grid cell. The output unit of measurement depends on theisCellValueRatio()
boolean:- If
false
, the value returned by this method shall be in the unit of measurement given bygetTranslationUnit()
. - If
true
, the value returned by this method is the ratio of the translation divided by the distance between grid cells in the dim dimension (Δx or Δy in the constructor javadoc).
- Parameters:
dim
- the dimension of the translation vector component to get, from 0 inclusive togetTranslationDimensions()
exclusive.gridX
- the grid index on the x axis, from 0 inclusive togridSize[0]
exclusive.gridY
- the grid index on the y axis, from 0 inclusive togridSize[1]
exclusive.- Returns:
- the translation for the given dimension in the grid cell at the given index.
- Throws:
IndexOutOfBoundsException
- may be thrown (but is not guaranteed to be throw) if an argument is out of range.
- If
-
getCellMean
public double getCellMean(int dim) Returns an average translation value for the given dimension. Those average values shall provide a good "first guess" before to interpolate the actual translation value at the (x,y) coordinate. This "first guess" is needed for inverse transform.Default implementation
The default implementation computes the average of all values returned bygetCellValue(dim, …)
, but subclasses may override with more specific values.Example
In the "France geocentric interpolation" (ESPG:9655) operation method, those "average" values are fixed by definition to -168, -60 and +320 metres for dimensions 0, 1 and 2 respectively (geocentric X, Y and Z).- Parameters:
dim
- the dimension for which to get an average translation value, from 0 inclusive togetTranslationDimensions()
exclusive.- Returns:
- a translation value close to the average for the given dimension.
-
getCellPrecision
public abstract double getCellPrecision()Returns an estimation of cell value precision (not to be confused with accuracy). This information can be determined in different ways:- If the data are read from an ASCII file with a fixed number of digits, then a suggested value is half the precision of the last digit (i.e. 0.5 × 10⁻ⁿ where n is the number of digits after the comma).
- If there is no indication about precision, then this method should return a value smaller than the best accuracy found in the grid. Accuracy are often specified on a cell-by-cell basis in grid files.
getCellValue(int, int, int)
. In particular ifisCellValueRatio()
returnstrue
, then the accuracy is in units of grid cell size.This information is used for determining a tolerance threshold in iterative calculation.
- Returns:
- an estimation of cell value precision.
-
isCellValueRatio
public boolean isCellValueRatio()Returnstrue
if the translation values in the cells are divided by the cell size. Iftrue
, then the values returned bygetCellValue(…)
,getCellMean(…)
andinterpolateInCell(…)
methods are the ratio of the translation divided by the distance between grid cells in the requested dimension (Δx or Δy in the constructor javadoc).- Returns:
true
if the translation values in the cells are divided by the cell size.
-
isCellInGrid
public boolean isCellInGrid(double gridX, double gridY) Returnstrue
if the given grid coordinates is inside this grid.- Parameters:
gridX
- first grid coordinate of the point to test.gridY
- second grid coordinate of the point to test.- Returns:
- whether the given point is inside this grid.
- Since:
- 1.0
- See Also:
-
replaceOutsideGridCoordinates
protected void replaceOutsideGridCoordinates(double[] gridCoordinates) Invoked when agridX
orgridY
coordinate is outside the range of valid grid coordinates. This method can replace the invalid coordinate by a valid one. The main purpose is to handle datum shift grids crossing the anti-meridian. For example, NADCON grid for Alaska is defined in a [−194° … −127.875°] longitude range, so a longitude of 170° needs to be converted to a longitude of −190° before it can be processed by that grid.The default implementation does nothing. Subclasses need to override this method if they want to handle longitude wraparounds. Note that the coordinate values are grid indices, not longitude or latitude values. So the period to add or remove is the number of cells that the grid would have if it was spanning 360° of longitude.
Example
If longitude values are mapped togridX
coordinates (in dimension 0), and if a shift of 360° in longitude values is equivalent to a shift ofperiodX
cells in the grid, then this method can be implemented as below:private final double periodX = ...; // Number of grid cells in 360° of longitude. @Override protected void replaceOutsideGridCoordinates(double[] gridCoordinates) { gridCoordinates[0] = Math.IEEEremainder(gridCoordinates[0], periodX); }
gridCoordinates
argument and can modify any of them, possibly many at once. The reason is because a shift of 360° of longitude (for example) may correspond to an offset in bothgridX
andgridY
indices if the grid is inclined. ThegridCoordinates
array length is the number of grid dimensions, typically 2<C extends Quantity<C>,T extends Quantity<T>>. - Parameters:
gridCoordinates
- on input, the cell indices of the point which is outside the grid. On output, the cell indices of an equivalent point inside the grid if possible. Coordinate values are modified in-place.- Since:
- 1.1
- See Also:
-
getParameterDescriptors
Returns a description of the values in this grid. Grid values may be given directly as matrices or tensors, or indirectly as name of file from which data were loaded. If grid values are given directly, then:- The number of grid dimensions determines the parameter type:
one-dimensional grids are represented by
Vector
instances, two-dimensional grids are represented byMatrix
instances, and grids with more than 2<C extends Quantity<C>,T extends Quantity<T>> are represented by tensors. - The number of dimensions of translation vectors determines how many matrix or tensor parameters appear.
Example 1
if thisDatumShiftGrid
instance has been created for performing NADCON datum shifts, then this method returns a group named "NADCON" with two parameters:- A parameter of type
Path
named “Latitude difference file”. - A parameter of type
Path
named “Longitude difference file”.
Example 2
if thisDatumShiftGrid
instance has been created byLocalizationGridBuilder
, then this method returns a group named "Localization grid" with four parameters:- Returns:
- a description of the values in this grid.
- Since:
- 1.0
- The number of grid dimensions determines the parameter type:
one-dimensional grids are represented by
-
getParameterValues
Gets the parameter values for the grids and stores them in the providedparameters
group. The givenparameters
must have the descriptor returned bygetParameterDescriptors()
. The matrices, tensors or file names are stored in the givenparameters
instance.Implementation note
This method is invoked byInterpolatedTransform
and other transforms for initializing the values of their parameter group.- Parameters:
parameters
- the parameter group where to set the values.- Since:
- 1.0
-
toString
Returns a string representation of thisDatumShiftGrid
for debugging purposes. -
equals
Returnstrue
if the given object is a grid containing the same data than this grid. Default implementation compares only the properties known to this abstract class like grid size, coordinate unit, etc. Subclasses need to override for adding comparison of the actual values. -
hashCode
public int hashCode()Returns a hash code value for this datum shift grid. This method does not need to compute a hash code from all grid values. Comparing some metadata like the grid filename is considered sufficient
-