Package org.apache.sis.referencing.operation.builder

# Class LinearTransformBuilder

Object
TransformBuilder
LinearTransformBuilder

public class LinearTransformBuilder extends TransformBuilder
Creates an affine transform which will map approximately the given source positions to the given target positions. In many cases, the source positions are grid indices and the target positions are geographic or projected coordinates, but this is not mandatory. If the source positions are known to be grid indices, then a builder created by the `LinearTransformBuilder(int...)` constructor will be more efficient. Otherwise a builder created by the `LinearTransformBuilder()` constructor will be able to handle randomly distributed coordinates.

Builders are not thread-safe. Builders can be used only once; points can not be added or modified after `create(MathTransformFactory)` has been invoked. The transform coefficients are determined using a least squares estimation method, with the assumption that source positions are exact and all the uncertainty is in the target positions.

## Linearizers

Consider the following situation (commonly found with netCDF files): the sources coordinates are pixel indices and the targets are (longitude, latitude) coordinates, but we suspect that the sources to targets transform is some undetermined map projection, maybe Mercator. A linear approximation between those coordinates will give poor results; the results would be much better if all (longitude, latitude) coordinates were converted to the right projection first. However that map projection may not be known, but we can try to guess it by trials-and-errors using a set of plausible projections. That set can be specified by `addLinearizers(Map, int...)`. If the `create(MathTransformFactory)` method finds that one of the specified projections seems a good fit, it will automatically convert all target coordinates to that projection. That selected projection is given by `linearizer()`.

Since:
  0.5
See Also:
  - `LocalizationGridBuilder`
  - `LinearTransform`
  - `Line`
  - `Plane`

Defined in the `sis-referencing` module

- ## Constructor Summary

  Constructors
  Constructor
  Description
  `LinearTransformBuilder()`
  Creates a new linear transform builder for randomly distributed positions.
  `LinearTransformBuilder(int... gridSize)`
  Creates a new linear transform builder for source positions distributed on a regular grid.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `void`
  `addLinearizers(Map<String,MathTransform> projections, int... projToGrid)`
  Adds transforms to potentially apply on target control points before to compute the linear transform.
  `static LinearTransform`
  `approximate(MathTransform gridToCRS, Envelope domain)`
  Returns a linear approximation of the given transform for the specified domain.
  `double[]`
  `correlation()`
  Returns the Pearson correlation coefficients of the transform created by `create(…)`.
  `LinearTransform`
  `create(MathTransformFactory factory)`
  Creates a linear transform approximation from the source positions to the target positions.
  `double[]`
  `getControlPoint(int[] source)`
  Returns a single target coordinate for the given source coordinate, or `null` if none.
  `Map<DirectPosition,DirectPosition>`
  `getControlPoints()`
  Returns all control points as a map.
  `int`
  `getSourceDimensions()`
  Returns the number of dimensions in source positions.
  `Envelope`
  `getSourceEnvelope()`
  Returns the envelope of source points (keys of the map returned by `getControlPoints()`).
  `int`
  `getTargetDimensions()`
  Returns the number of dimensions in target positions.
  `Envelope`
  `getTargetEnvelope()`
  Returns the envelope of target points (values of the map returned by `getControlPoints()`).
  `Optional<Map​.Entry<String,MathTransform>>`
  `linearizer()`
  If target coordinates have been projected to another space, returns that projection.
  `void`
  `setControlPoint(int[] source, double[] target)`
  Sets a single matching control point pair.
  `void`
  `setControlPoints(Map<? extends Position,? extends Position> sourceToTarget)`
  Sets all control point (source, target) pairs, overwriting any previous setting.
  `void`
  `setControlPoints(MathTransform gridToCRS)`
  Sets all control point (source, target) pairs, overwriting any previous setting.
  `String`
  `toString()`
  Returns a string representation of this builder for debugging purpose.

  ### Methods inherited from class Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait`

- ## Constructor Details

  - ### LinearTransformBuilder

    public LinearTransformBuilder()
    Creates a new linear transform builder for randomly distributed positions.
    Tip: if the source coordinates are grid indices, then the `LinearTransformBuilder(int...)` constructor will create a more efficient builder.

  - ### LinearTransformBuilder

    public LinearTransformBuilder(int... gridSize)
    Creates a new linear transform builder for source positions distributed on a regular grid. This constructor notifies `LinearTransformBuilder` that coordinate values of all source positions will be integers in the [0 … `gridSize[0]`-1] range for the first dimension (typically column indices), in the [0 … `gridSize[1]`-1] range for the second dimension (typically row indices), etc. The dimension of all source positions is the length of the given `gridSize` array.

    An empty array is equivalent to invoking the no-argument constructor, i.e. no restriction is put on the source coordinates.

    Parameters:
      `gridSize` - the number of integer coordinate values in each grid dimension.
    Throws:
      `IllegalArgumentException` - if a grid size is not strictly positive, or if the product of all values (∏`gridSize`) is greater than `Integer​.MAX_VALUE`.
    Since:
      0.8

- ## Method Details

  - ### approximate

    public static LinearTransform approximate(MathTransform gridToCRS, Envelope domain) throws FactoryException
    Returns a linear approximation of the given transform for the specified domain. The source positions are integer coordinates included in the given envelope. The target positions are the results of transforming source coordinates with the given `gridToCRS` transform.
    Parameters:
      `gridToCRS` - the transform from source coordinates (grid indices) to target coordinates.
      `domain` - domain of integer source coordinates for which to get a linear approximation.
    Returns:
      a linear approximation of given transform for the specified domain.
    Throws:
      `FactoryException` - if the transform approximation can not be computed.
    Since:
      1.1
    See Also:
      - `setControlPoints(MathTransform)`
      - `DomainLinearizer`

  - ### getSourceDimensions

    public int getSourceDimensions()
    Returns the number of dimensions in source positions. This is the length of the `source` array given in argument to `get`/`setControlPoint(int[], …)` methods. This is also the number of dimensions of the `DirectPosition` keys in the `Map` exchanged by `get`/`setControlPoints(Map)` methods.
    Returns:
      the dimension of source points.
    Throws:
      `IllegalStateException` - if the number of source dimensions is not yet known.
    Since:
      0.8
    See Also:
      - `MathTransform​.getSourceDimensions()`

  - ### getTargetDimensions

    public int getTargetDimensions()
    Returns the number of dimensions in target positions. This is the length of the `target` array exchanged by `get`/`setControlPoint(…, double[])` methods. This is also the number of dimensions of the `DirectPosition` values in the `Map` exchanged by `get`/`setControlPoints(Map)` methods.
    Returns:
      the dimension of target points.
    Throws:
      `IllegalStateException` - if the number of target dimensions is not yet known.
    Since:
      0.8
    See Also:
      - `MathTransform​.getTargetDimensions()`

  - ### getSourceEnvelope

    public Envelope getSourceEnvelope()
    Returns the envelope of source points (keys of the map returned by `getControlPoints()`). The number of dimensions is equal to `getSourceDimensions()`. This method returns the known minimum and maximum values (inclusive) for each dimension, not expanded to encompass full cell surfaces. In other words, the returned envelope encompasses only cell centers.

    If a grid size was `specified at construction time`, then those minimums and maximums are inferred from the grid size and are always integer values. Otherwise, the minimums and maximums are extracted from the control points and may be any floating point values. In any cases, the lower and upper values are inclusive.

    Returns:
      the envelope of source points (cell centers), inclusive.
    Throws:
      `IllegalStateException` - if the source points are not yet known.
    Since:
      1.0

  - ### getTargetEnvelope

    public Envelope getTargetEnvelope()
    Returns the envelope of target points (values of the map returned by `getControlPoints()`). The number of dimensions is equal to `getTargetDimensions()`. The lower and upper values are inclusive. If a linearizer has been applied, then coordinates of the returned envelope are projected by that linearizer.
    Returns:
      the envelope of target points.
    Throws:
      `IllegalStateException` - if the target points are not yet known.
    Since:
      1.0

  - ### setControlPoints

    public void setControlPoints(MathTransform gridToCRS) throws TransformException
    Sets all control point (source, target) pairs, overwriting any previous setting. The source positions are all integer coordinates in a rectangle from (0, 0, …) inclusive to `gridSize` exclusive where `gridSize` is an `int[]` array specified at construction time. The target positions are the results of transforming all source coordinates with the given `gridToCRS` transform.
    Parameters:
      `gridToCRS` - the transform from source coordinates (grid indices) to target coordinates.
    Throws:
      `TransformException` - if a coordinate value can not be transformed.
    Since:
      1.1
    See Also:
      - `approximate(MathTransform, Envelope)`

  - ### setControlPoints

    public void setControlPoints(Map<? extends Position,? extends Position> sourceToTarget) throws MismatchedDimensionException
    Sets all control point (source, target) pairs, overwriting any previous setting. The source positions are the keys in given map, and the target positions are the associated values. The map should not contain two entries with the same source position. Coordinate reference systems are ignored. Null positions are silently ignored. Positions with NaN or infinite coordinates cause an exception to be thrown.

    All source positions shall have the same number of dimensions (the source dimension), and all target positions shall have the same number of dimensions (the target dimension). However the source dimension does not need to be the same than the target dimension. Apache SIS currently supports only one- or two-dimensional source positions, together with arbitrary target dimension.

    If this builder has been created with the `LinearTransformBuilder(int...)` constructor, then the coordinate values of all source positions shall be integers in the [0 … `gridSize[0]`-1] range for the first dimension (typically column indices), in the [0 … `gridSize[1]`-1] range for the second dimension (typically row indices), etc. This constraint does not apply for builders created with the `LinearTransformBuilder()` constructor.

    Parameters:
      `sourceToTarget` - a map of source positions to target positions. Source positions are assumed precise and target positions are assumed uncertain.
    Throws:
      `IllegalStateException` - if `create(…)` has already been invoked.
      `IllegalArgumentException` - if the given positions contain NaN or infinite coordinate values.
      `IllegalArgumentException` - if this builder has been created for a grid but some source coordinates are not indices in that grid.
      `MismatchedDimensionException` - if some positions do not have the expected number of dimensions.
    Since:
      0.8

  - ### getControlPoints

    public Map<DirectPosition,DirectPosition> getControlPoints()
    Returns all control points as a map. Values are source coordinates and keys are target coordinates. The map is unmodifiable and is guaranteed to contain only non-null keys and values. The map is a view: changes in this builder are immediately reflected in the returned map.

    If `linearizer()` returns a non-empty value, then the values in the returned map are projected using that linearizer. This may happen only after `create(…)` has been invoked.

    Returns:
      all control points in this builder.
    Since:
      1.0

  - ### setControlPoint

    public void setControlPoint(int[] source, double[] target)
    Sets a single matching control point pair. Source position is assumed precise and target position is assumed uncertain. If the given source position was already associated with another target position, then the old target position is discarded.
    Performance note: current implementation is efficient for builders created for a grid but inefficient for builders created for randomly distributed points. In the later case, the `setControlPoints(Map)` method is a more efficient alternative.
    Parameters:
      `source` - the source coordinates. If this builder has been created with the `LinearTransformBuilder(int...)` constructor, then for every index i the `source[i]` value shall be in the [0 … `gridSize[i]`-1] range inclusive. If this builder has been created with the `LinearTransformBuilder()` constructor, then no constraint apply.
      `target` - the target coordinates, assumed uncertain.
    Throws:
      `IllegalStateException` - if `create(…)` has already been invoked.
      `IllegalArgumentException` - if this builder has been created for a grid but some source coordinates are out of index range, or if `target` contains NaN of infinite numbers.
      `MismatchedDimensionException` - if the source or target position does not have the expected number of dimensions.
    Since:
      0.8

  - ### getControlPoint

    public double[] getControlPoint(int[] source)
    Returns a single target coordinate for the given source coordinate, or `null` if none. This method can be used for retrieving points set by previous calls to `setControlPoint(int[], double[])` or `setControlPoints(Map)`.

    If `linearizer()` returns a non-empty value, then the returned values are projected using that linearizer. This may happen only if this method is invoked after `create(…)`.

    Performance note: current implementation is efficient for builders created for a grid but inefficient for builders created for randomly distributed points.
    Parameters:
      `source` - the source coordinates. If this builder has been created with the `LinearTransformBuilder(int...)` constructor, then for every index i the `source[i]` value shall be in the [0 … `gridSize[i]`-1] range inclusive. If this builder has been created with the `LinearTransformBuilder()` constructor, then no constraint apply.
    Returns:
      the target coordinates associated to the given source, or `null` if none.
    Throws:
      `IllegalArgumentException` - if this builder has been created for a grid but some source coordinates are out of index range.
      `MismatchedDimensionException` - if the source position does not have the expected number of dimensions.
    Since:
      0.8

  - ### addLinearizers

    public void addLinearizers(Map<String,MathTransform> projections, int... projToGrid)
    Adds transforms to potentially apply on target control points before to compute the linear transform. This method can be invoked when the source to target transform would possibly be more linear if target was another space than the current one. If linearizers have been specified, then the `create(MathTransformFactory)` method will try to apply each transform on target coordinates and check which one get the best correlation coefficients.

    Exactly one of the specified transforms will be selected. If applying no transform is an acceptable solution, then an identity transform should be included in the given `projections` map. The transform selected by `LinearTransformBuilder` will be given by `linearizer()`.

    Linearizers are specified as a collection of `MathTransform`s from current target coordinates to some other spaces where sources to new targets transforms may be more linear. Keys in the map are arbitrary identifiers. Values in the map should be non-linear transforms; `LinearTransform`s (other than identity) should be avoided because they will consume processing power for no correlation improvement.

    #### Error handling

    If a `TransformException` occurred or if some transform results were NaN or infinite, then the `MathTransform` that failed will be ignored. If all transforms fail, then a `FactoryException` will be thrown by the `create(…)` method.

    #### Dimensions mapping

    The `projToGrid` argument maps `projections` dimensions to target dimensions of this builder. For example if `projToGrid` array is `{2,1}`, then coordinate values in target dimensions 2 and 1 of this grid will be used as source coordinates in dimensions 0 and 1 respectively for all given projections. Likewise, the projection results in dimensions 0 and 1 of all projections will be stored in target dimensions 2 and 1 respectively of this grid.

    The `projToGrid` argument can be omitted or null, in which case {0, 1, 2 … `getTargetDimensions()` - 1} is assumed. All given `projections` shall have a number of source and target dimensions equals to the length of the `projToGrid` array. It is possible to invoke this method many times with different `projToGrid` argument values.

    Parameters:
      `projections` - projections from current target coordinates to other spaces which may result in more linear transforms.
      `projToGrid` - the target dimensions to project, or null or omitted for projecting all target dimensions in same order.
    Throws:
      `IllegalStateException` - if `create(…)` has already been invoked.
      `MismatchedDimensionException` - if a projection does not have the expected number of dimensions.
    Since:
      1.0
    See Also:
      - `linearizer()`
      - `correlation()`

  - ### create

    public LinearTransform create(MathTransformFactory factory) throws FactoryException
    Creates a linear transform approximation from the source positions to the target positions. This method assumes that source positions are precise and that all uncertainties are in target positions. If linearizers have been specified, then this method will project all target coordinates using one of those linearizers in order to get a more linear transform. If such projection is applied, then `linearizer()` will return a non-empty value after this method call.

    If this method is invoked more than once, the previously created transform instance is returned.

    Specified by:
      `create` in class `TransformBuilder`
    Parameters:
      `factory` - the factory to use for creating the transform, or `null` for the default factory. The `MathTransformFactory​.createAffineTransform(Matrix)` method of that factory shall return `LinearTransform` instances.
    Returns:
      the fitted linear transform.
    Throws:
      `FactoryException` - if the transform can not be created, for example because the source or target points have not be specified.
    Since:
      0.8

  - ### linearizer

    public Optional<Map.Entry<String,MathTransform>> linearizer()
    If target coordinates have been projected to another space, returns that projection. This method returns a non-empty value if `addLinearizers(Map, int...)` has been invoked with a non-empty map, followed by a `create(MathTransformFactory)` call. In such case, `LinearTransformBuilder` selects a linearizer identified by the returned key - value entry. The entry key is one of the keys of the maps given to `addLinearizers(…)`. The entry value is the associated `MathTransform`, possibly modified as described in the axis order section below.

    The envelope returned by `getTargetEnvelope()` and all control points returned by `getControlPoint(int[])` are projected by the selected transform. Consequently if the target coordinates of original control points are desired, then the transform returned by `create(…)` needs to be concatenated with the inverse of the transform returned by this `linearizer()` method.

    #### Axis order

    The source coordinates expected by the returned transform are the control points target coordinates. The returned transform will contain an operation step performing axis filtering and swapping implied by the `projToGrid` argument that was given to the `addLinearizers(…, projToGrid)}` method. Consequently if the `projToGrid` argument was not an arithmetic progression, then the transform returned by this method will not be one of the instances given to `addLinearizers(…)`.
    Returns:
      the projection applied on target coordinates before to compute a linear transform.
    Since:
      1.1

  - ### correlation

    public double[] correlation()
    Returns the Pearson correlation coefficients of the transform created by `create(…)`. The closer those coefficients are to +1 or -1, the better the fit. This method returns `null` if `create(…)` has not yet been invoked. If non-null, the array length is equal to the number of target dimensions.
    Returns:
      estimation of Pearson correlation coefficients for each target dimension, or `null` if `create(…)` has not been invoked yet.

  - ### toString

    public String toString()
    Returns a string representation of this builder for debugging purpose. Current implementation shows the following information:
    - Number of points.
    - Linearizers and their correlation coefficients (if available).
    - The linear transform (if already computed).
    The string representation may change in any future version.
    Overrides:
      `toString` in class `Object`
    Returns:
      a string representation of this builder.