package edu.oregonstate.cartography.grid.operators;
import edu.oregonstate.cartography.grid.Grid;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A base class for multi-threaded grid operators. Allocates as many threads
* for operating on the grid as CPU cores are available.
* @author Bernhard Jenny, Institute of Cartography, ETH Zurich
*/
public abstract class ThreadedGridOperator implements GridOperator {
/**
* Operate row-wise on the passed source grid and store the result in the passed
* destination grid. The source and the destination can be the same object
* if isOverwrittingSupported() returns true.
* @param src The source grid.
* @param dst The destination grid.
* @param startRow The index of the first row to operate on.
* @param endRow The index of the last row. Derived classes should not
* operate on this row. It may be equal to src.getRows().
*/
protected abstract void operate(Grid src, Grid dst, int startRow, int endRow);
/**
* Returns whether the source and destination grids can be the same object.
* Defaults to true, that is, the source grid can be overwritten. Needs to
* be overridden by derived classes that access multiple cells for computing
* a single new cell value. In this case, false needs to be returned.
* @return True if the source and destination can be identical, false otherwise.
*/
public boolean isOverwritingSupported() {
return true;
}
/**
* Creates a new grid that will store the results of the operator. This
* method creates a new grid of the same size as the source grid. It must be
* overridden if the derived operator generates a grid that has a different
* dimension or position than the source grid.
* @param src The source grid.
* @return The new grid of the same size and position as the source grid.
*/
protected Grid initDestinationGrid(Grid src) {
if (src == null || !src.isWellFormed()) {
throw new IllegalArgumentException(getName() + ": invalid source grid");
}
int nrows = src.getRows();
int ncols = src.getCols();
Grid newGrid = new Grid(ncols, nrows, src.getCellSize());
newGrid.setWest(src.getWest());
newGrid.setSouth(src.getSouth());
return newGrid;
}
/**
* Apply the filter and store the result in a new grid that is returned.
* @param src The source grid.
* @return The new grid storing the result.
*/
@Override
public Grid operate(Grid src) {
Grid dst = initDestinationGrid(src);
return operate(src, dst);
}
/**
* Apply the filter and store the result in the passed destination grid.
* @param src The source grid.
* @param dst The destination grid. May not be null.
* @return The passed dst grid is returned.
*/
public Grid operate(Grid src, Grid dst) {
if (src == null || !src.isWellFormed()) {
throw new IllegalArgumentException(getName() + ": invalid source grid");
}
if (dst == null || !dst.isWellFormed()) {
throw new IllegalArgumentException(getName() + ": invalid destination grid");
}
if (!isOverwritingSupported() && src.getGrid() == dst.getGrid()) {
throw new IllegalArgumentException(getName() + ": overwriting source grid is not possible");
}
int nRows = src.getRows();
int nThreads = Runtime.getRuntime().availableProcessors();
ArrayList<GridOperatorThread> threads = new ArrayList(nThreads);
int rowChunk = (nRows / nThreads) + 1;
for (int i = 0; i < nThreads; i++) {
int startRow = i * rowChunk;
int endRow = Math.min(nRows, startRow + rowChunk);
GridOperatorThread t = new GridOperatorThread(src, dst, startRow, endRow);
t.setName(this.getName() + " " + i);
threads.add(t);
t.start();
}
for (Thread t : threads) {
try {
t.join();
} catch (InterruptedException ex) {
Logger.getLogger(ThreadedGridOperator.class.getName()).log(Level.SEVERE, null, ex);
}
}
return dst;
}
/**
* A private utility class for wrapping a thread.
*/
private class GridOperatorThread extends Thread {
final Grid srcGrid;
final Grid dstGrid;
final int startRow;
final int endRow;
public GridOperatorThread(Grid srcGrid,
Grid dstGrid,
int startRow,
int endRow) {
this.srcGrid = srcGrid;
this.dstGrid = dstGrid;
this.startRow = startRow;
this.endRow = endRow;
}
@Override
public void run() {
operate(srcGrid, dstGrid, startRow, endRow);
}
}
}