All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy