org.jgrasstools.gears.libs.modules.FlowNode Maven / Gradle / Ivy
The newest version!
/*
* This file is part of JGrasstools (http://www.jgrasstools.org)
* (C) HydroloGIS - www.hydrologis.com
*
* JGrasstools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.jgrasstools.gears.libs.modules;
import static org.jgrasstools.gears.libs.modules.JGTConstants.doubleNovalue;
import java.util.ArrayList;
import java.util.List;
import javax.media.jai.iterator.RandomIter;
import org.jgrasstools.gears.utils.math.NumericsUtilities;
/**
* A node in the grid environment of a digital elevation model.
*
* @author Andrea Antonello (www.hydrologis.com)
* @since 0.7.6
*/
public class FlowNode extends Node {
/**
* The outlet value of flow.
*/
public static final double OUTLET = 10.0;
/**
* The defaut value used for marking a network.
*/
public static double NETVALUE = 2.0;
public final double flow;
private boolean isMarkedAsOutlet = false;
private boolean isHeadingOutside = false;
private boolean wasHeadingOutsideChecked = false;
private double eFlow;
private double enFlow;
private double nFlow;
private double nwFlow;
private double wFlow;
private double wsFlow;
private double sFlow;
private double seFlow;
private List enteringNodes;
/**
* The constructor.
*
* @param flowIter the elevation model raster iter.
* @param cols the cols of the raster.
* @param rows the rows of the raster.
* @param col the col of the current {@link FlowNode node}.
* @param row the row of the current {@link FlowNode node}.
*/
public FlowNode( RandomIter flowIter, int cols, int rows, int col, int row ) {
super(flowIter, cols, rows, col, row);
if (!isInRaster(col, row)) {
isValid = false;
flow = doubleNovalue;
} else {
flow = gridIter.getSampleDouble(col, row, 0);
if (JGTConstants.isNovalue(flow)) {
isValid = false;
} else {
isValid = true;
}
}
if ((int) flow == (int) OUTLET) {
isMarkedAsOutlet = true;
}
int index = -1;
for( int c = -1; c <= 1; c++ ) {
for( int r = -1; r <= 1; r++ ) {
index++;
if (c == 0 && r == 0) {
continue;
}
int newC = col + c;
int newR = row + r;
double tmp = doubleNovalue;
if (!isInRaster(newC, newR)) {
touchesBound = true;
} else {
tmp = gridIter.getSampleDouble(newC, newR, 0);
}
switch( index ) {
case 0:
nwFlow = tmp;
break;
case 1:
wFlow = tmp;
break;
case 2:
wsFlow = tmp;
break;
case 3:
nFlow = tmp;
break;
case 4:
throw new RuntimeException();
case 5:
sFlow = tmp;
break;
case 6:
enFlow = tmp;
break;
case 7:
eFlow = tmp;
break;
case 8:
seFlow = tmp;
break;
default:
throw new RuntimeException();
}
if (JGTConstants.isNovalue(tmp)) {
touchesBound = true;
}
}
}
}
@Override
public String toString() {
return "FlowNode [\n\tcol=" + col + //
", \n\trow=" + row + //
", \n\tflow=" + flow + //
", \n\tisValid=" + isValid() + //
", \n\ttouchesBounds=" + touchesBound + //
"\n]";
}
/**
* @return true
if this node has a {@value #OUTLET} value in the flow map.
*/
public boolean isMarkedAsOutlet() {
return isMarkedAsOutlet;
}
/**
* @return true
if this node is a pixel that will flow outside of the valid flow map.
*/
public boolean isHeadingOutside() {
if (!wasHeadingOutsideChecked) {
if (touchesBound) {
Node goDownstream = goDownstream();
if (goDownstream == null || !goDownstream.isValid()) {
isHeadingOutside = true;
} else {
isHeadingOutside = false;
}
} else {
isHeadingOutside = false;
}
wasHeadingOutsideChecked = true;
}
return isHeadingOutside;
}
/**
* Checks if it is a source node, i.e. no others entering.
*
* @return true if it is valid and a source node.
*/
public boolean isSource() {
if (!isValid()) {
return false;
}
List enteringNodes = getEnteringNodes();
return enteringNodes.size() == 0;
}
/**
* Get the value of the flow in one of the surrounding direction.
*
* @param direction the {@link Direction}.
* @return the elevation value.
*/
public double getFlowAt( Direction direction ) {
switch( direction ) {
case E:
return eFlow;
case W:
return wFlow;
case N:
return nFlow;
case S:
return sFlow;
case EN:
return enFlow;
case NW:
return nwFlow;
case WS:
return wsFlow;
case SE:
return seFlow;
default:
throw new IllegalArgumentException();
}
}
/**
* Get the next downstream node.
*
* @return the next downstream node or null
if the end has been reached.
*/
public FlowNode goDownstream() {
if (isValid) {
Direction direction = Direction.forFlow((int) flow);
if (direction != null) {
FlowNode nextNode = new FlowNode(gridIter, cols, rows, col + direction.col, row + direction.row);
if (nextNode.isValid) {
return nextNode;
}
}
}
return null;
}
/**
* Gets all surrounding {@link FlowNode nodes} that DO flow into this node.
*
* @return the nodes that flow into this node.
*/
public List getEnteringNodes() {
if (enteringNodes == null) {
enteringNodes = new ArrayList();
Direction[] orderedDirs = Direction.getOrderedDirs();
for( Direction direction : orderedDirs ) {
switch( direction ) {
case E:
if ((int) eFlow == Direction.E.getEnteringFlow()) {
int newCol = col + direction.col;
int newRow = row + direction.row;
FlowNode node = new FlowNode(gridIter, cols, rows, newCol, newRow);
enteringNodes.add(node);
}
break;
case N:
if ((int) nFlow == Direction.N.getEnteringFlow()) {
int newCol = col + direction.col;
int newRow = row + direction.row;
FlowNode node = new FlowNode(gridIter, cols, rows, newCol, newRow);
enteringNodes.add(node);
}
break;
case W:
if ((int) wFlow == Direction.W.getEnteringFlow()) {
int newCol = col + direction.col;
int newRow = row + direction.row;
FlowNode node = new FlowNode(gridIter, cols, rows, newCol, newRow);
enteringNodes.add(node);
}
break;
case S:
if ((int) sFlow == Direction.S.getEnteringFlow()) {
int newCol = col + direction.col;
int newRow = row + direction.row;
FlowNode node = new FlowNode(gridIter, cols, rows, newCol, newRow);
enteringNodes.add(node);
}
break;
case EN:
if ((int) enFlow == Direction.EN.getEnteringFlow()) {
int newCol = col + direction.col;
int newRow = row + direction.row;
FlowNode node = new FlowNode(gridIter, cols, rows, newCol, newRow);
enteringNodes.add(node);
}
break;
case NW:
if ((int) nwFlow == Direction.NW.getEnteringFlow()) {
int newCol = col + direction.col;
int newRow = row + direction.row;
FlowNode node = new FlowNode(gridIter, cols, rows, newCol, newRow);
enteringNodes.add(node);
}
break;
case WS:
if ((int) wsFlow == Direction.WS.getEnteringFlow()) {
int newCol = col + direction.col;
int newRow = row + direction.row;
FlowNode node = new FlowNode(gridIter, cols, rows, newCol, newRow);
enteringNodes.add(node);
}
break;
case SE:
if ((int) seFlow == Direction.SE.getEnteringFlow()) {
int newCol = col + direction.col;
int newRow = row + direction.row;
FlowNode node = new FlowNode(gridIter, cols, rows, newCol, newRow);
enteringNodes.add(node);
}
break;
default:
throw new IllegalArgumentException();
}
}
}
return enteringNodes;
}
/**
* Get the upstream node based on the max tca value.
*
* @param tcaIter the tca map.
* @param hacklengthIter the optional hacklength map, if available
* it is used in cases with multiple equal in coming tcas.
* @return the upstream node.
*/
public FlowNode getUpstreamTcaBased( RandomIter tcaIter, RandomIter hacklengthIter ) {
Direction[] orderedDirs = Direction.getOrderedDirs();
double maxTca = Double.NEGATIVE_INFINITY;
double maxHacklength = Double.NEGATIVE_INFINITY;
int maxCol = 0;
int maxRow = 0;
boolean gotOne = false;
for( Direction direction : orderedDirs ) {
int newCol = 0;
int newRow = 0;
switch( direction ) {
case E:
if ((int) eFlow == Direction.E.getEnteringFlow()) {
newCol = col + direction.col;
newRow = row + direction.row;
gotOne = true;
}
break;
case N:
if ((int) nFlow == Direction.N.getEnteringFlow()) {
newCol = col + direction.col;
newRow = row + direction.row;
gotOne = true;
}
break;
case W:
if ((int) wFlow == Direction.W.getEnteringFlow()) {
newCol = col + direction.col;
newRow = row + direction.row;
gotOne = true;
}
break;
case S:
if ((int) sFlow == Direction.S.getEnteringFlow()) {
newCol = col + direction.col;
newRow = row + direction.row;
gotOne = true;
}
break;
case EN:
if ((int) enFlow == Direction.EN.getEnteringFlow()) {
newCol = col + direction.col;
newRow = row + direction.row;
gotOne = true;
}
break;
case NW:
if ((int) nwFlow == Direction.NW.getEnteringFlow()) {
newCol = col + direction.col;
newRow = row + direction.row;
gotOne = true;
}
break;
case WS:
if ((int) wsFlow == Direction.WS.getEnteringFlow()) {
newCol = col + direction.col;
newRow = row + direction.row;
gotOne = true;
}
break;
case SE:
if ((int) seFlow == Direction.SE.getEnteringFlow()) {
newCol = col + direction.col;
newRow = row + direction.row;
gotOne = true;
}
break;
default:
throw new IllegalArgumentException();
}
if (isInRaster(newCol, newRow)) {
double flowValue = gridIter.getSampleDouble(newCol, newRow, 0);
if (JGTConstants.isNovalue(flowValue)) {
continue;
}
double tcaValue = tcaIter.getSampleDouble(newCol, newRow, 0);
double hacklengthValue = 0.0;
if (hacklengthIter != null)
hacklengthValue = tcaIter.getSampleDouble(newCol, newRow, 0);
if (NumericsUtilities.dEq(tcaValue, maxTca) && hacklengthIter != null) {
/*
* if there are two equal tca values around
* and info about hacklength is available,
* use that one to choose
*/
if (hacklengthValue > maxHacklength) {
// this has larger hacklength, use this one as max tca
maxTca = tcaValue;
maxCol = newCol;
maxRow = newRow;
maxHacklength = hacklengthValue;
}
} else if (tcaValue > maxTca) {
maxTca = tcaValue;
maxCol = newCol;
maxRow = newRow;
maxHacklength = hacklengthValue;
}
}
}
if (!gotOne) {
return null;
}
FlowNode node = new FlowNode(gridIter, cols, rows, maxCol, maxRow);
return node;
}
private boolean isInRaster( int col, int row ) {
if (col < 0 || col >= cols || row < 0 || row >= rows) {
return false;
}
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + col;
long temp;
temp = Double.doubleToLongBits(flow);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + row;
return result;
}
@Override
public boolean equals( Object obj ) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
FlowNode other = (FlowNode) obj;
if (col != other.col || row != other.row)
return false;
if (Double.doubleToLongBits(flow) != Double.doubleToLongBits(other.flow))
return false;
return true;
}
}