/*
* GridSlopeLineOperator.java
*
* Created on May 25, 2006, 2:23 PM
*
*/
package ika.geo.grid;
import ika.geo.GeoGrid;
import ika.geo.GeoObject;
import ika.geo.GeoPath;
import ika.geo.VectorSymbol;
import java.awt.geom.Rectangle2D;
/**
* Generates a GeoPath following the line of maximum slope. The line jumps
* from one cell to the next, i.e. the line will have corners with an angle
* that is a mulitple of 45 degrees.
* @author Bernhard Jenny, Institute of Cartography, ETH Zurich
*/
public class GridFastFalllineOperator implements GridOperator {
public enum Direction {DIRUP, DIRDOWN, DIRBILATERAL};
private Direction direction = Direction.DIRBILATERAL;
private float minVerDiff;
private double startX;
private double startY;
/** Creates a new instance of GridSlopeLineOperator */
public GridFastFalllineOperator() {
}
public String getName() {
return "Slope Line";
}
public GeoObject operate(GeoGrid geoGrid) {
if (geoGrid == null)
throw new IllegalArgumentException();
// make sure start point is on grid
Rectangle2D bounds = geoGrid.getBounds2D(GeoObject.UNDEFINED_SCALE);
if (bounds.contains(getStartX(), getStartY()) == false)
return null;
int col = (int)((getStartX() - geoGrid.getWest()) / geoGrid.getCellSize());
int row = (int)((geoGrid.getNorth() - getStartY()) / geoGrid.getCellSize());
GeoPath geoPath = new GeoPath();
VectorSymbol vs = new VectorSymbol();
vs.setScaleInvariant(true);
geoPath.setVectorSymbol(vs);
switch (getDirection()) {
case DIRUP:
this.nextUp(geoGrid, geoPath, col, row);
break;
case DIRDOWN:
this.nextDown(geoGrid, geoPath, col, row);
break;
case DIRBILATERAL:
this.nextUp(geoGrid, geoPath, col, row);
geoPath.invertDirection();
this.nextDown(geoGrid, geoPath, col, row);
break;
}
return geoPath;
}
private void nextUp(GeoGrid elevationGrid, GeoPath path,
int currentCol, int currentRow) {
long elevRows = elevationGrid.getRows();
long elevCols = elevationGrid.getCols();
float maxDiff = 0f;
float hCenter = elevationGrid.getValue(currentCol, currentRow);
float h, diff;
int nextRow = currentRow;
int nextCol = currentCol;
final float sqrt2 = (float)Math.sqrt(2);
// compute difference to 8 neighbors
h = elevationGrid.getValue(currentCol + 1, currentRow);
diff = h - hCenter;
if (diff > maxDiff && diff >= this.minVerDiff) {
maxDiff = diff;
nextCol = currentCol + 1;
}
h = elevationGrid.getValue(currentCol - 1, currentRow);
diff = h - hCenter;
if (diff > maxDiff && diff >= this.minVerDiff) {
maxDiff = diff;
nextCol = currentCol - 1;
}
h = elevationGrid.getValue(currentCol, currentRow + 1);
diff = h - hCenter;
if (diff > maxDiff && diff >= this.minVerDiff) {
maxDiff = diff;
nextRow = currentRow + 1;
}
h = elevationGrid.getValue(currentCol, currentRow - 1);
diff = h - hCenter;
if (diff > maxDiff && diff >= this.minVerDiff) {
maxDiff = diff;
nextRow = currentRow - 1;
}
h = elevationGrid.getValue(currentCol + 1, currentRow + 1);
diff = h - hCenter;
if (diff > maxDiff * sqrt2 && diff >= this.minVerDiff * sqrt2) {
maxDiff = diff;
nextCol = currentCol + 1;
nextRow = currentRow + 1;
}
h = elevationGrid.getValue(currentCol + 1, currentRow - 1);
diff = h - hCenter;
if (diff > maxDiff * sqrt2 && diff >= this.minVerDiff * sqrt2) {
maxDiff = diff;
nextCol = currentCol + 1;
nextRow = currentRow - 1;
}
h = elevationGrid.getValue(currentCol - 1, currentRow + 1);
diff = h - hCenter;
if (diff > maxDiff * sqrt2 && diff >= this.minVerDiff * sqrt2) {
maxDiff = diff;
nextCol = currentCol - 1;
nextRow = currentRow + 1;
}
h = elevationGrid.getValue(currentCol - 1, currentRow - 1);
diff = h - hCenter;
if (diff > maxDiff * sqrt2 && diff >= this.minVerDiff * sqrt2) {
maxDiff = diff;
nextCol = currentCol - 1;
nextRow = currentRow - 1;
}
// test if a neighbor was found with a difference in elevation that is big enough.
if (maxDiff == 0) {
return;
}
// make sure we don't leave the grid
if (nextRow == 0 || nextRow == elevRows - 1
|| nextCol == 0 || nextCol == elevCols - 1) {
return;
}
double cellSize = elevationGrid.getCellSize();
double x = elevationGrid.getWest() + currentCol * cellSize;
double y = elevationGrid.getNorth() - currentRow * cellSize;
path.moveOrLineTo(x, y);
nextUp(elevationGrid, path, nextCol, nextRow);
}
private void nextDown(GeoGrid elevationGrid, GeoPath path,
int currentCol, int currentRow) {
final float sqrt2 = (float)Math.sqrt(2);
long elevRows = elevationGrid.getRows();
long elevCols = elevationGrid.getCols();
float maxDiff = 0f;
float hCenter = elevationGrid.getValue(currentCol, currentRow);
// find biggest elevation difference to all 8 neighbors
float h, diff;
int nextRow = currentRow;
int nextCol = currentCol;
h = elevationGrid.getValue(currentCol + 1, currentRow);
diff = h - hCenter;
if (diff < maxDiff && -diff >= this.minVerDiff) {
maxDiff = diff;
nextCol = currentCol + 1;
}
h = elevationGrid.getValue(currentCol - 1, currentRow);
diff = h - hCenter;
if (diff < maxDiff && -diff >= this.minVerDiff) {
maxDiff = diff;
nextCol = currentCol - 1;
}
h = elevationGrid.getValue(currentCol, currentRow + 1);
diff = h - hCenter;
if (diff < maxDiff && -diff >= this.minVerDiff) {
maxDiff = diff;
nextRow = currentRow + 1;
}
h = elevationGrid.getValue(currentCol, currentRow - 1);
diff = h - hCenter;
if (diff < maxDiff && -diff >= this.minVerDiff) {
maxDiff = diff;
nextRow = currentRow - 1;
}
h = elevationGrid.getValue(currentCol + 1, currentRow + 1);
diff = h - hCenter;
if (diff < maxDiff * sqrt2 && -diff >= this.minVerDiff * sqrt2) {
maxDiff = diff;
nextCol = currentCol + 1;
nextRow = currentRow + 1;
}
h = elevationGrid.getValue(currentCol + 1, currentRow - 1);
diff = h - hCenter;
if (diff < maxDiff * sqrt2 && -diff >= this.minVerDiff * sqrt2) {
maxDiff = diff;
nextCol = currentCol + 1;
nextRow = currentRow - 1;
}
h = elevationGrid.getValue(currentCol - 1, currentRow + 1);
diff = h - hCenter;
if (diff < maxDiff * sqrt2 && -diff >= this.minVerDiff * sqrt2) {
maxDiff = diff;
nextCol = currentCol - 1;
nextRow = currentRow + 1;
}
h = elevationGrid.getValue(currentCol - 1, currentRow - 1);
diff = h - hCenter;
if (diff < maxDiff * sqrt2 && -diff >= this.minVerDiff * sqrt2) {
maxDiff = diff;
nextCol = currentCol - 1;
nextRow = currentRow - 1;
}
// test if a neighbor was found with a difference in elevation that is big enough.
if (maxDiff == 0) {
return;
}
// make sure we don't leave the grid
if (nextRow == 0 || nextRow == elevRows - 1
|| nextCol == 0 || nextCol == elevCols - 1) {
return;
}
double cellSize = elevationGrid.getCellSize();
double x = elevationGrid.getWest() + currentCol * cellSize;
double y = elevationGrid.getNorth() - currentRow * cellSize;
path.moveOrLineTo(x, y);
nextDown(elevationGrid, path, nextCol, nextRow);
}
public float getMinVerDiff() {
return minVerDiff;
}
public void setMinVerDiff(float minVerDiff) {
this.minVerDiff = minVerDiff;
}
public double getStartX() {
return startX;
}
public void setStartX(double startX) {
this.startX = startX;
}
public double getStartY() {
return startY;
}
public void setStartY(double startY) {
this.startY = startY;
}
public Direction getDirection() {
return direction;
}
public void setDirection(Direction direction) {
this.direction = direction;
}
}