The supplied LatticeNavigator determines how to step through the Lattice. It can, for instance, be line by line, but it can also be in a more complicated way. When no navigator is supplied, a default navigator will be used which steps in the optimum way.
A cursor (which is an Array object) is
used to return the data for each step in the iteration process.
Depending on the navigator used the cursor can have a different shape
for each step of the iteration. This is especially true when the
end of an axis is reached for a non-integrally fitting cursor shape.
The cursor() function returns an Array which has the same
dimensionality as the Lattice. It is, however, also possible to get
an Array with a lower dimensionality by using the correct function
in the group vectorCursor(), matrixCursor(), and
cubeCursor(). Those functions remove (some) degenerated axes
resulting in a vector, matrix or cube.
When, for example, a LatticeStepper with shape [64,1,1] is used, the
vectorCursor() can be used. It will remove the degenerated
axes (length 1) and return the cursor as a Vector object. Note that
matrixCursor() cannot be used, because removing the degenerated
axes results in a 1D array.
Generally iterators should not be long-lived objects - create new ones when needed rather than keeping one around for a long time to be reused. This is because the cache memory used by the cursor will be released when the iterator is destroyed.
The purpose of this class is to hide the possibly complicated implementation and structure of the Lattice classes, and allow you to iterate through a Lattice with the same ease as one iterates through a Fortran or C vector. For example, and assuming that initialization has been done properly, here's a typical 'for' loop:
// code omitted which associates Lattice object and the iterator for (iterator.reset(); !iterator.atEnd(); iterator++) { meanValue = mean(iterator.cursor()); }The iterator's cursor() member function returns a reference to that part of the Lattice data which is presently "seen" by the LatticeIterator.
Before explaining the initialization of an iterator, the LatticeNavigator class must be further introduced. This is an abstract base class, from which concrete navigators are derived. After one of these is created, you attach it to the LatticeIterator, and it provides a specific technique for navigating through the Lattice. Different navigators deliver different traversal schemes. The most basic is LatticeStepper, which moves a specified shape sequentially through the Lattice -- for example, by moving one plane at a time, front to back, through a cube. Another (future) navigator might be designed to move a small, 2-dimensional plane through a cube, centering each iteration on the brightest pixel of the cube's plane, and ignoring the darker regions of the cube.
The performance and memory usage of an iteration through a lattice (in particular through a PagedArray) depends very heavily on the navigator used. Currently there are three navigators available:
Here's a typical iterator declaration:
RO_LatticeIterator<Float> iterator(pagedArray, stepper);The template identifier Float defines the data type of Array object that will be the iterator's cursor.
void demonstrateIterator (const String& filename) { PagedArray<Float> pagedArray(filename); IPosition latticeShape = pagedArray.shape(); cout << "paged array has shape: " << latticeShape << endl; // Construct the iterator. since we only want to read the PagedArray, // use the read-only class, which disallows writing back to the cursor. // No navigator is given, so the default TileStepper is used // which ensures optimum performance. RO_LatticeIterator<Float> iterator(pagedArray); // Add for each iteration step the sum of the cursor elements to the sum. // Note that the cursor is an Array object and that the function sum // is defined in ArrayMath.h. Float runningSum = 0.0; for (iterator.reset(); !iterator.atEnd(); iterator++) { runningSum += sum(iterator.cursor()); } cout << "average value, from demonstrateIterator: " << runningSum / latticeShape.product() << endl; }
Construct the Iterator with the supplied data. It uses a TileStepper as the default iteration strategy. useRef=True means that if possible the cursor arrays returned reference the data in the underlying lattice. This is only possible for ArrayLattice objects (or e.g. a SubLattice using it).
Construct the Iterator with the supplied data, and iteration strategy
Construct the Iterator with the supplied data. It uses a LatticeStepper with the supplied cursor shape as the iteration strategy.
The copy constructor uses reference semantics (ie. NO real copy is made). The function copy can be used to make a true copy.
Destructor (cleans up dangling references and releases memory)
Assignment uses reference semantics (ie. NO real copy is made). The function copy can be used to make a true copy.
Make a copy of the iterator object.
This means that an independent navigator object is created to
be able to iterate independently through the same Lattice.
The position in the copied navigator is the same as the original.
The reset function has to be used to start at the beginning.
Note that if the Lattice uses a cache (e.g. PagedArray), the
cache is shared by the iterators.
Is the iterator object empty?
Return the underlying lattice.
Increment operator - increment the cursor to the next position. These
functions are forwarded to the current LatticeNavigator and both
postfix and prefix versions will do the same thing.
They return True if the cursor moved (which should always be the
case if the iterator is not at the end).
Decrement operator - decrement the cursor to the previous
position. These functions are forwarded to the current LatticeNavigator
and both postfix and prefix versions will do the same thing.
They return True if the cursor moved (which should always be the
case if the iterator is not at the start).
Function which resets the cursor to the beginning of the Lattice and resets the number of steps taken to zero.
Function which returns a value of "True" if the cursor is at the beginning of the Lattice, otherwise, returns "False".
Function which returns a value of "True" if an attempt has been made to move the cursor beyond the end of the Lattice.
Function to return the number of steps (increments or decrements) taken since construction (or since last reset). This is a running count of all cursor movement, thus doing N increments followed by N decrements results in 2N steps.
Function which returns the current position of the beginning of the cursor within the Lattice. The returned IPosition will have the same number of axes as the underlying Lattice.
Function which returns the current position of the end of the cursor. The returned IPosition will have the same number of axes as the underlying Lattice.
Function which returns the shape of the Lattice being iterated through. The returned IPosition will always have the same number of axes as the underlying Lattice.
Function which returns the shape of the cursor which is iterating through the Lattice. The returned IPosition will have the same number of axes as the underlying Lattice.
Functions which returns a window to the data in the Lattice. These are used to read the data within the Lattice. Use the function that is appropriate to the current cursor dimension, AFTER REMOVING DEGENERATE AXES, or use the cursor function which works with any number of dimensions in the cursor. A call of the function whose return value is inappropriate with respect to the current cursor dimension will throw an exception (AipsError).
Function which checks the internals of the class for consistency. Returns True if everything is fine otherwise returns False.
LatticeIterator can be used in 3 ways:
- For readonly purposes using the cursor() functions. Note that if
the entire iteration is readonly, it is better to use an
RO_LatticeIterator object.
- To update (part of)the contents of the lattice (e.g. clip the value
of some pixels). For this purpose the rwCursor functions
should be used. They read the data (if not read yet) and mark the
cursor for write.
- To fill the lattice. For this purpose the woCursor
functions should be used. They do not read the data, but only mark the
cursor for write.
When needed, writing the cursor data is done automatically when the cursor position changes or when the iterator is destructed.
PagedArray<Float> pa("someName"); IPosition windowShape(2,pa.shape(0), pa.shape(1)); LatticeStepper stepper(pa.shape(), windowShape); LatticeIterator<Float> iterator(pa, stepper); Int planeNumber = 0; for (iterator.reset(); !iterator.atEnd(); iterator++) { iterator.woCursor() = planeNumber++; }
Here's an iterator that runs through a cube, subtracting the mean from each line of the cube with a mean < 0. See TiledLineStepper for an explanation of the navigator used here.
PagedArray<Float> pa("someName"); TiledLineStepper stepper(pa.shape(), pa.niceCursorShape(), 0); LatticeIterator<Float> iterator(pa, stepper); Int planeNumber = 0; for (iterator.reset(); !iterator.atEnd(); iterator++) { Float meanLine = mean(iterator.cursor()); if (meanLine < 0) { iterator.rwCursor() -= meanLine; } }Note that in this last example no more vectors than required are written. This is achieved by using the readonly function cursor in the test and using rwCursor only when data needs to be changed.
Construct the Iterator with the supplied data. It uses a TileStepper as the default iteration strategy. useRef=True means that if possible the cursor arrays returned reference the data in the underlying lattice. This is only possible for ArrayLattice objects (or e.g. a SubLattice using it).
Construct the Iterator with the supplied data, and iteration strategy
Iterate through the data with a LatticeStepper that has uses the supplied cursorShape.
The copy constructor uses reference semantics (ie. NO real copy is made). The function copy can be used to make a true copy.
destructor (cleans up dangling references and releases memory)
Assignment uses reference semantics (ie. NO real copy is made). The function copy can be used to make a true copy.
Make a copy of the iterator object.
This means that an independent navigator object is created to
be able to iterate independently through the same Lattice.
The position in the copied navigator is the same as the original.
The reset function has to be used to start at the beginning.
Note that if the Lattice uses a cache (e.g. PagedArray), the
cache is shared by the iterators.
Functions to return a window to the data in the Lattice. Use the function
that is appropriate to the current cursor dimension, AFTER REMOVING
DEGENERATE AXES, or use the cursor function which works with
any number of dimensions in the cursor. A call of the function whose
return value is inappropriate with respect to the current cursor
dimension will throw an exception (AipsError) (e.g. VectorCursor
cannot be used when the cursor is 2D).
When the iterator state changes (e.g. by moving, destruction) the
data are automatically rewritten before the iterator state is changed.
The rw (read/write) versions should be used to read the
data first. They are useful to update a lattice.
The wo (writeonly) versions do not read the data.
They only return a cursor of the correct shape and are useful to
fill a lattice. Note that it sets the state to 'data read'. I.e.,
a subsequent call to, say, cursor() does not read the
data, which would destroy the contents of the cursor which may
just be filled by the user.
Function which checks the internals of the class for consistency. Returns True if everything is fine. Otherwise returns False.