Class TransformJoiner

Object
TransformJoiner

public final class TransformJoiner extends Object
Handler used during concatenations for simplifying a single transform with its neighbor transforms. Instances of Transform­Joiner may be created automatically by Apache SIS at any step during a concatenation. Those Transform­Joiner instances give to Abstract­Math­Transform implementations an opportunity to replace the default concatenation algorithm by an optimized alternative. Some examples of optimizations are:
  • detecting when two consecutive non-linear operations are equivalent to a third non-linear operation,
  • modifying the linear transforms that are executed before or after the transform to optimize (it is sometime possible to remove a transform step that way).
Because the simplifications that are possible depend on the transform's formulas, each Abstract­Math­Transform subclass uses Transform­Joiner in a different way.

Usage

This class is used by overriding the Abstract­Math­Transform​.try­Concatenate(Transform­Joiner) method. The implementation can inspect the surrounding transforms with calls to get­Transform(int). The argument given to get­Transform(…) is a relative index: value 0 identifies the transform on which try­Concatenate(…) has been invoked, -1 the transform immediately before it, and +1 the transform immediately after it. The try­Concatenate(…) implementation can replace the transform at relative index 0 together with a neighbor transform (at relative index -1 or +1) by a call to replace(int, Math­Transform).
Since:
1.5
See Also:
  • Field Details

    • factory

      public final MathTransformFactory factory
      The factory to use for creating the transform to use as a replacement of the transforms to simplify. Never null.
  • Method Details

    • getTransform

      public Optional<MathTransform> getTransform(int relativeIndex)
      Returns a transform before or after the transform to simplify. By definition, invoking this method with a relative index of 0 always returns the transform on which try­Concatenate(…) has been invoked. A relative index of -1 returns the previous transform while a relative index of +1 returns the next transform.

      This method may return an empty value if the requested transform is not available from this Transform­Joiner instance. It does not mean that the transform does not exist at all in the complete chain of transforms, because each Transform­Joiner instance may know only a fragment of that chain.

      Parameters:
      relative­Index - index relative to the transform on which try­Concatenate(…) has been invoked.
      Returns:
      the requested transform. An empty value does not necessarily mean that the transform does not exist.
    • replace

      public boolean replace(int firstOrLast, MathTransform concatenation) throws FactoryException
      Tries to replace the transform at relative index 0, together with a neighbor, by the given transform. The first­Or­Last argument specifies which neighbors are replaced by the specified transform:
      • If -1, then transforming a point p by concatenation shall be equivalent to first transforming p by get­Transform(-1), and then transforming the result by get­Transform(0).
      • If +1, then transforming a point p by concatenation shall be equivalent to first transforming p by get­Transform(0), and then transforming the result by get­Transform(+1).
      • If 0, then only the transform at index 0 is replaced. Neighbor are unchanged.
      Parameters:
      first­Or­Last - relative index of first (if negative) or last (if positive) transform to replace.
      concatenation - the transform to use instead of the one at index 0 and the specified neighbors.
      Returns:
      whether the replacement has been done. May be false if first­Or­Last is too large.
      Throws:
      Factory­Exception - if the given concatenation is not a valid replacement.
    • replace

      public boolean replace(int from, int to, MathTransform concatenation) throws FactoryException
      Tries to replace the transforms in the given range of relative indices, which must include 0. This method is similar to replace(int, Math­Transform), but allowing to replace the neighbor transforms on both sides.

      Example

      A call to transform(-1, +1, tr) replaces the transform on which try­Concatenate(…) has been invoked (this is the transform at relative index 0) together with the transforms located immediately before (-1) and immediately after (+1) by the given tr transform.
      Parameters:
      from - relative index of the first transform to replace, inclusive. This is usually -1 or 0.
      to - relative index of the last transform to replace, inclusive. This is usually 0 or +1.
      concatenation - the transform to use instead of the transforms in the given range.
      Returns:
      whether the replacement has been done.
      Throws:
      Factory­Exception - if the given concatenation is not a valid replacement.
    • replaceRoundtrip

      public boolean replaceRoundtrip(int middle, UnaryOperator<MathTransform> mapper) throws FactoryException
      Tries to replace a forwardmiddleinverse chain by a new transform. The transform that determines whether a replacement is possible is the middle transform, and its relative index is given by the middle argument in this method. There is two main scenarios:
      • If middle is +1, then forward is the transform at relative index 0 and inverse is the transform at relative index 2.
      • If middle is -1, then forward is the transform at relative index -2 and inverse is the transform at relative index 0.
      If inverse is the
      invalid reference
      inverse
      of forward, then this method invokes mapper​.apply(middle) where middle is the transform between the forward and inverse steps. If the mapper returns a non-null value, then that value replaces the three steps of above-cited chain.

      Example

      In a chain of Reverse MercatorLongitude rotationForward Mercator transforms where the forward and reverse Mercator steps operate on coordinates in radians on an ellipsoid having a semi-major axis length of one (this is how Apache SIS implements map projections), then because these Mercator steps just pass-through the longitude value unchanged and without using them in calculation, these three steps can be replaced by the Longitude rotation step alone (it is mathematically equivalent because of the radian units and the semi-major axis length of 1).
      Parameters:
      middle - relative index of the transform which is potentially a middle step. Value can be -1 or +1 for the step before or after the transform on which try­Concatenate(…) has been invoked.
      mapper - a function receiving the middle transform in argument and returning the replacement for the forwardmiddleinverse chain. May return null if the replacement cannot be done.
      Returns:
      whether the replacement has been done as a result of this method call.
      Throws:
      Factory­Exception - if the mapped transform is not a valid replacement.
    • replacePassThrough

      public boolean replacePassThrough(Map<Integer,Integer> dimensions) throws FactoryException
      Tries to simplify the transform chain when some coordinates are passed-through the transform at index 0. A "pass-through" coordinate is an input coordinate which is copied in the output tuple by the transform with no change and without using this coordinate value in calculation. In such cases, for any Math­Transform that use or modify this coordinate value immediately before or after the transform at relative index 0, it does not matter if the order of those operations is modified.

      Abstract­Math­Transform​.try­Concatenate(Transform­Joiner) Implementations can invoke this method with a map containing the zero-based indexes of the dimensions that are passed-through by the transform on which try­Concatenate(…) has been invoked. Each map key is the index of a pass-through coordinate in source tuples, while the associated map value is the index for the same coordinate but in target tuples. In the common case where the transform does not change the coordinate order, the keys and the values are the same numbers.

      Example

      In a spherical to geographic conversion using a biaxial ellipsoid, the longitude value is a pass-through coordinate because the spherical longitude and the geodetic longitude are the same value. Since the implementation of Ellipsoid­To­Centric­Transform does not use the longitude value in its calculation (in spherical case only), it does not matter if longitudes values are converted from degrees to something else before or after the spherical to geographic conversion. By invoking the following method, unit conversion of longitude values (at dimension 0) may be moved before or after the transform if this method detects that such move would simplify the transform chain:
      @Override
      protected void tryConcatenate(TransformJoiner context) throws FactoryException {
          if (!joiner.replacePassThrough(Map.of(0, 0))) {
              super.tryConcatenate(joiner);
          }
      }
      
      Parameters:
      dimensions - the pass-through dimensions in source tuples (map keys) and target tuples (map values).
      Returns:
      whether the transform chain has been modified as a result of this method call.
      Throws:
      Factory­Exception - if the concatenation is not a valid replacement.
      Illegal­Argument­Exception - if a value is repeated twice in the given map.