Class DatumShiftGrid<C extends Quantity<C>,​T extends Quantity<T>>

  • Type Parameters:
    C - dimension of the coordinate unit (usually Angle).
    T - dimension of the translation unit (usually Angle or Length).
    All Implemented Interfaces:
    Serializable

    public abstract class DatumShiftGrid<C extends Quantity<C>,​T extends Quantity<T>>
    extends Object
    implements Serializable
    Small but non-constant translations to apply on coordinates for datum shifts or other transformation process. The main purpose of this class is to encapsulate the data provided by datum shift grid files like NTv2, NADCON or RGF93. But this class could also be used for other kind of transformations, provided that the shifts are relatively small (otherwise algorithms may not converge).

    Geodetic datum changes can be implemented by translations in geographic or geocentric coordinates. Translations given by Datum­Shift­Grid 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 Datum­Shift­Grid class can describe both cases, but will be used with different Math­Transform implementations.

    Steps for calculation of a translation vector:

    1. Coordinates are given in some "real world" unit. The expected unit is given by get­Coordinate­Unit().
    2. Above coordinates are converted to grid indices including fractional parts. That conversion is given by get­Coordinate­To­Grid().
    3. Translation vectors are interpolated at the position of above grid indices. That interpolation is done by interpolate­In­Cell(…).
    4. 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 is­Cell­Value­Ratio().
    5. The resulting translation vectors are in the unit given by get­Translation­Unit().
    The interpolate­At(…) method performs all those steps. But that method is provided only for convenience; it is not used by Apache SIS. For performance reasons SIS Math­Transform 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 by Interpolated­Transform 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 (see Interpolated­Geocentric­Transform).
    • 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 been seen as a change from image datum to geodetic datum. The coordinate transformation process can sometime be performed by a mathematical conversion (for example an affine transform) applied as a first approximation, followed by small corrections for the residual part. Datum­Shift­Grid can describe the small corrections part.
    Implementations of this class shall be immutable and thread-safe.
    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: Note that the above restriction does not prevent Datum­Shift­Grid to interpolate translation vectors in more than two dimensions. See the above datum shift by geocentric translations use case for an example.
    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.
    Since:
    0.7
    See Also:
    Datum­Shift­Transform, Serialized Form

    Defined in the sis-referencing module

    • Field Summary

      Fields 
      Modifier and Type Field Description
      protected static int INTERPOLATED_DIMENSIONS
      Number of source dimensions in which interpolations are applied.
    • Constructor Summary

      Constructors 
      Modifier Constructor Description
      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.
      protected DatumShiftGrid​(DatumShiftGrid<C,​T> other)
      Creates a new datum shift grid with the same grid geometry (size and units) than the given grid.
    • Field Detail

      • INTERPOLATED_DIMENSIONS

        protected static final int INTERPOLATED_DIMENSIONS
        Number 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: get­Cell­Value(…, x, y), interpolate­In­Cell(x, y, …), derivative­In­Cell(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:
        Constant Field Values
    • Constructor Detail

      • 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 get­Coordinate­Unit(), get­Coordinate­To­Grid(), is­Cell­Value­Ratio() and get­Translation­Unit() methods. The argument order is roughly the order in which they are used in the process of interpolating translation vectors.

        Parameters:
        coordinate­Unit - the unit of measurement of input values, before conversion to grid indices by coordinate­To­Grid.
        coordinate­To­Grid - conversion from the "real world" coordinates to grid indices including fractional parts.
        grid­Size - number of cells along each axis in the grid. The length of this array shall be equal to coordinate­To­Grid target dimensions.
        is­Cell­Value­Ratio - true if results of interpolate­In­Cell(…) are divided by grid cell size.
        translation­Unit - the unit of measurement of output values.
      • DatumShiftGrid

        protected DatumShiftGrid​(DatumShiftGrid<C,​T> other)
        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 Detail

      • 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. The grid dimensions shall be equal to coordinate­To­Grid target dimensions.
        Note: the 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.
      • getDomainOfValidity

        public Envelope getDomainOfValidity()
                                     throws TransformException
        Returns the domain of validity of input coordinates that can be specified to the interpolate­At(…) method. Coordinates outside that domain of validity will still be accepted, but the extrapolated results may be very wrong.

        The unit of measurement for the coordinate values in the returned envelope is given by get­Coordinate­Unit(). The envelope CRS is undefined.

        Returns:
        the domain covered by this grid.
        Throws:
        Transform­Exception - if an error occurred while computing the envelope.
      • getCoordinateToGrid

        public LinearTransform getCoordinateToGrid()
        Returns the conversion from the source coordinates (in "real world" units) to grid indices. The input coordinates given to the Linear­Transform shall be in the unit of measurement given by get­Coordinate­Unit(). 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 uses Pixel­In­Cell​.CELL_CENTER convention.

        This transform is usually two-dimensional, in which case conversions from (x,y) coordinates to (grid­X, grid­Y) indices can be done with the following formulas:

        • gridX = (x - x₀) / Δx
        • gridY = (y - y₀) / Δy
        where:
        • (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 get­Coordinate­Unit().
        The coordinate­To­Grid 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.
      • 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 given vector array, which shall have a length of at least get­Translation­Dimensions(). The output unit of measurement is the same than the one documented in get­Cell­Value(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 = get­Translation­Dimensions(), then this method appends the derivative (approximated) at the given grid indices. This is the same derivative than the one computed by derivative­In­Cell(double, double), opportunistically computed here for performance reasons. The matrix layout is as below, where t₀ and t₁ are the coordinates after translation.
        ┌                   ┐         ┌                             ┐
        │  ∂t₀/∂xt₀/∂y  │    =    │  vector[n+0]   vector[n+1]  │
        │  ∂t₁/∂xt₁/∂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 by get­Translation­Dimensions().
        1. Clamp the grid­X index into the [0 … grid­Size[0] - 1] range, inclusive.
        2. Clamp the grid­Y index into the [0 … grid­Size[1] - 1] range, inclusive.
        3. Using get­Cell­Value(int, int, int), get the cell values around the given indices.
        4. Apply a bilinear interpolation and store the result in vector[dim].
        5. Appends Jacobian matrix coefficients if the vector length is sufficient.
        Parameters:
        grid­X - first grid coordinate of the point for which to get the translation.
        grid­Y - 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:
        is­Cell­In­Grid(double, double)
      • derivativeInCell

        public Matrix derivativeInCell​(double gridX,
                                       double gridY)
        Estimates the derivative at the given grid indices. Derivatives must be consistent with values given by interpolate­In­Cell(double, double, double[]) at adjacent positions. For a two-dimensional grid, tₐ(x,y) an abbreviation for interpolate­In­Cell(grid­X, grid­Y, …)[a] and for x and y integers, the derivative is:
        ┌                   ┐         ┌                                                        ┐
        │  ∂t₀/∂xt₀/∂y  │    =    │  t₀(x+1,y) - t₀(x,y) + 1      t₀(x,y+1) - t₀(x,y)      │
        │  ∂t₁/∂xt₁/∂y  │         │  t₁(x+1,y) - t₁(x,y)          t₁(x,y+1) - t₁(x,y) + 1  │
        └                   ┘         └                                                        ┘
        Extrapolations
        Derivatives must be consistent with interpolate­In­Cell(double, double, double[]) even when the given coordinates are outside the grid. The interpolate­In­Cell(…) 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 grid­X and grid­Y are outside the grid, then the derivative is the identity matrix.
        • If only grid­X 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 at grid­Y position.
        • If only grid­Y 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 at grid­X position.
        Parameters:
        grid­X - first grid coordinate of the point for which to get the translation.
        grid­Y - second grid coordinate of the point for which to get the translation.
        Returns:
        the derivative at the given location.
        See Also:
        is­Cell­In­Grid(double, double), interpolate­In­Cell(double, double, double[])
      • 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 the is­Cell­Value­Ratio() boolean:
        • If false, the value returned by this method shall be in the unit of measurement given by get­Translation­Unit().
        • 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).
        Caller must ensure that all arguments given to this method are in their expected ranges. The behavior of this method is undefined if any argument value is out-of-range. (this method is not required to validate arguments, for performance reasons).
        Parameters:
        dim - the dimension of the translation vector component to get, from 0 inclusive to get­Translation­Dimensions() exclusive.
        grid­X - the grid index on the x axis, from 0 inclusive to grid­Size[0] exclusive.
        grid­Y - the grid index on the y axis, from 0 inclusive to grid­Size[1] exclusive.
        Returns:
        the translation for the given dimension in the grid cell at the given index.
        Throws:
        Index­Out­Of­Bounds­Exception - may be thrown (but is not guaranteed to be throw) if an argument is out of range.
      • 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 by get­Cell­Value(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 to get­Translation­Dimensions() 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.
        The output unit of measurement is the same than the one documented in get­Cell­Value(int, int, int). In particular if is­Cell­Value­Ratio() returns true, 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()
        Returns true if the translation values in the cells are divided by the cell size. If true, then the values returned by get­Cell­Value(…), get­Cell­Mean(…) and interpolate­In­Cell(…) 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)
        Returns true if the given grid coordinates is inside this grid.
        Parameters:
        grid­X - first grid coordinate of the point to test.
        grid­Y - second grid coordinate of the point to test.
        Returns:
        whether the given point is inside this grid.
        Since:
        1.0
        See Also:
        interpolate­In­Cell(double, double, double[])
      • getParameterDescriptors

        public abstract ParameterDescriptorGroup 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 by Matrix instances, and grids with more than 2 are represented by tensors.
        • The number of dimensions of translation vectors determines how many matrix or tensor parameters appear.
        Example 1: if this Datum­Shift­Grid 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 this Datum­Shift­Grid instance has been created by Localization­Grid­Builder, then this method returns a group named "Localization grid" with four parameters:
        • A parameter of type Integer named “num_row” for the number of rows in each matrix.
        • A parameter of type Integer named “num_col” for the number of columns in each matrix.
        • A parameter of type Matrix named “grid_x”.
        • A parameter of type Matrix named “grid_y”.
        Returns:
        a description of the values in this grid.
        Since:
        1.0
      • getParameterValues

        public abstract void getParameterValues​(Parameters parameters)
        Gets the parameter values for the grids and stores them in the provided parameters group. The given parameters must have the descriptor returned by get­Parameter­Descriptors(). The matrices, tensors or file names are stored in the given parameters instance.
        Implementation note: this method is invoked by Interpolated­Transform 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

        public String toString()
        Returns a string representation of this Datum­Shift­Grid. The default implementation formats the parameter values.
        Overrides:
        to­String in class Object
        Returns:
        a string representation of the grid parameters.
        Since:
        1.0
      • equals

        public boolean equals​(Object other)
        Returns true 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.
        Overrides:
        equals in class Object
        Parameters:
        other - the other object to compare with this datum shift grid.
        Returns:
        true if the given object is non-null, of the same class than this Datum­Shift­Grid and contains the same data.
      • 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
        Overrides:
        hash­Code in class Object
        Returns:
        a hash code based on metadata.