Class TransformJoiner
TransformJoiner
may be created automatically by Apache SIS at any
step during a concatenation.
Those TransformJoiner
instances give to AbstractMathTransform
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).
AbstractMathTransform
subclass uses TransformJoiner
in a different way.
Usage
This class is used by overriding theAbstractMathTransform.tryConcatenate(TransformJoiner)
method.
The implementation can inspect the surrounding transforms with calls to getTransform(int)
.
The argument given to getTransform(…)
is a relative index:
value 0 identifies the transform on which tryConcatenate(…)
has been invoked,
-1 the transform immediately before it, and +1 the transform immediately after it.
The tryConcatenate(…)
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, MathTransform)
.- Since:
- 1.5
- See Also:
-
Field Summary
FieldsModifier and TypeFieldDescriptionfinal MathTransformFactory
The factory to use for creating the transform to use as a replacement of the transforms to simplify. -
Method Summary
Modifier and TypeMethodDescriptiongetTransform
(int relativeIndex) Returns a transform before or after the transform to simplify.boolean
replace
(int from, int to, MathTransform concatenation) Tries to replace the transforms in the given range of relative indices, which must include 0.boolean
replace
(int firstOrLast, MathTransform concatenation) Tries to replace the transform at relative index 0, together with a neighbor, by the given transform.boolean
replacePassThrough
(Map<Integer, Integer> dimensions) Tries to simplify the transform chain when some coordinates are passed-through the transform at index 0.boolean
replaceRoundtrip
(int middle, UnaryOperator<MathTransform> mapper) Tries to replace a forward → middle → inverse chain by a new transform.
-
Field Details
-
factory
The factory to use for creating the transform to use as a replacement of the transforms to simplify. Never null.
-
-
Method Details
-
getTransform
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 whichtryConcatenate(…)
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
TransformJoiner
instance. It does not mean that the transform does not exist at all in the complete chain of transforms, because eachTransformJoiner
instance may know only a fragment of that chain.- Parameters:
relativeIndex
- index relative to the transform on whichtryConcatenate(…)
has been invoked.- Returns:
- the requested transform. An empty value does not necessarily mean that the transform does not exist.
-
replace
Tries to replace the transform at relative index 0, together with a neighbor, by the given transform. ThefirstOrLast
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 bygetTransform(-1)
, and then transforming the result bygetTransform(0)
. - If +1, then transforming a point p by
concatenation
shall be equivalent to first transforming p bygetTransform(0)
, and then transforming the result bygetTransform(+1)
. - If 0, then only the transform at index 0 is replaced. Neighbor are unchanged.
- Parameters:
firstOrLast
- 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
iffirstOrLast
is too large. - Throws:
FactoryException
- if the given concatenation is not a valid replacement.
- If -1, then transforming a point p by
-
replace
Tries to replace the transforms in the given range of relative indices, which must include 0. This method is similar toreplace(int, MathTransform)
, but allowing to replace the neighbor transforms on both sides.Example
A call totransform(-1, +1, tr)
replaces the transform on whichtryConcatenate(…)
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 giventr
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:
FactoryException
- if the given concatenation is not a valid replacement.
-
replaceRoundtrip
public boolean replaceRoundtrip(int middle, UnaryOperator<MathTransform> mapper) throws FactoryException Tries to replace a forward → middle → inverse 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 themiddle
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.
invalid reference
inverse
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 Mercator → Longitude rotation → Forward 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 whichtryConcatenate(…)
has been invoked.mapper
- a function receiving the middle transform in argument and returning the replacement for the forward → middle → inverse chain. May returnnull
if the replacement cannot be done.- Returns:
- whether the replacement has been done as a result of this method call.
- Throws:
FactoryException
- if the mapped transform is not a valid replacement.
- If
-
replacePassThrough
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 anyMathTransform
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.AbstractMathTransform.tryConcatenate(TransformJoiner)
Implementations can invoke this method with a map containing the zero-based indexes of the dimensions that are passed-through by the transform on whichtryConcatenate(…)
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 aspherical 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 ofEllipsoidToCentricTransform
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:
FactoryException
- if the concatenation is not a valid replacement.IllegalArgumentException
- if a value is repeated twice in the given map.
-