/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.jgrasstools.hortonmachine.modules.network.networkattributes;
import static org.jgrasstools.gears.libs.modules.JGTConstants.isNovalue;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSNETWORKATTRIBUTESBUILDER_AUTHORCONTACTS;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSNETWORKATTRIBUTESBUILDER_AUTHORNAMES;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSNETWORKATTRIBUTESBUILDER_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSNETWORKATTRIBUTESBUILDER_KEYWORDS;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSNETWORKATTRIBUTESBUILDER_LABEL;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSNETWORKATTRIBUTESBUILDER_LICENSE;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSNETWORKATTRIBUTESBUILDER_NAME;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSNETWORKATTRIBUTESBUILDER_STATUS;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSNETWORKATTRIBUTESBUILDER_doHack_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSNETWORKATTRIBUTESBUILDER_inDem_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSNETWORKATTRIBUTESBUILDER_inFlow_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSNETWORKATTRIBUTESBUILDER_inNet_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSNETWORKATTRIBUTESBUILDER_inTca_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSNETWORKATTRIBUTESBUILDER_outHack_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSNETWORKATTRIBUTESBUILDER_outNet_DESCRIPTION;
import java.awt.geom.Point2D;
import java.awt.image.WritableRaster;
import java.util.ArrayList;
import java.util.List;
import javax.media.jai.iterator.RandomIter;
import javax.media.jai.iterator.WritableRandomIter;
import oms3.annotations.Author;
import oms3.annotations.Description;
import oms3.annotations.Execute;
import oms3.annotations.In;
import oms3.annotations.Keywords;
import oms3.annotations.Label;
import oms3.annotations.License;
import oms3.annotations.Name;
import oms3.annotations.Out;
import oms3.annotations.Status;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.jgrasstools.gears.libs.exceptions.ModelsIllegalargumentException;
import org.jgrasstools.gears.libs.modules.FlowNode;
import org.jgrasstools.gears.libs.modules.JGTConstants;
import org.jgrasstools.gears.libs.modules.JGTModel;
import org.jgrasstools.gears.libs.modules.Node;
import org.jgrasstools.gears.libs.monitor.IJGTProgressMonitor;
import org.jgrasstools.gears.utils.RegionMap;
import org.jgrasstools.gears.utils.coverage.CoverageUtilities;
import org.jgrasstools.gears.utils.geometry.GeometryUtilities;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Point;
@Description(OMSNETWORKATTRIBUTESBUILDER_DESCRIPTION)
@Author(name = OMSNETWORKATTRIBUTESBUILDER_AUTHORNAMES, contact = OMSNETWORKATTRIBUTESBUILDER_AUTHORCONTACTS)
@Keywords(OMSNETWORKATTRIBUTESBUILDER_KEYWORDS)
@Label(OMSNETWORKATTRIBUTESBUILDER_LABEL)
@Name(OMSNETWORKATTRIBUTESBUILDER_NAME)
@Status(OMSNETWORKATTRIBUTESBUILDER_STATUS)
@License(OMSNETWORKATTRIBUTESBUILDER_LICENSE)
public class OmsNetworkAttributesBuilder extends JGTModel {
@Description(OMSNETWORKATTRIBUTESBUILDER_inNet_DESCRIPTION)
@In
public GridCoverage2D inNet = null;
@Description(OMSNETWORKATTRIBUTESBUILDER_inFlow_DESCRIPTION)
@In
public GridCoverage2D inFlow = null;
@Description(OMSNETWORKATTRIBUTESBUILDER_inTca_DESCRIPTION)
@In
public GridCoverage2D inTca = null;
@Description(OMSNETWORKATTRIBUTESBUILDER_inDem_DESCRIPTION)
@In
public GridCoverage2D inDem = null;
@Description(OMSNETWORKATTRIBUTESBUILDER_doHack_DESCRIPTION)
@In
public boolean doHack = false;
@Description(OMSNETWORKATTRIBUTESBUILDER_outNet_DESCRIPTION)
@Out
public SimpleFeatureCollection outNet = null;
@Description(OMSNETWORKATTRIBUTESBUILDER_outHack_DESCRIPTION)
@Out
public GridCoverage2D outHack = null;
private int cols;
private int rows;
private List<SimpleFeature> networkList = new ArrayList<SimpleFeature>();
private GridGeometry2D gridGeometry;
private RandomIter netIter;
private GeometryFactory gf = GeometryUtilities.gf();
private SimpleFeatureBuilder networkBuilder;
private RandomIter tcaIter;
private int maxHack = 0;
private WritableRandomIter hackWIter;
private List<NetworkChannel> channels;
@Execute
public void process() throws Exception {
checkNull(inFlow, inNet, inTca);
if (!concatOr(outNet == null, doReset)) {
return;
}
RegionMap regionMap = CoverageUtilities.getRegionParamsFromGridCoverage(inFlow);
cols = regionMap.getCols();
rows = regionMap.getRows();
gridGeometry = inFlow.getGridGeometry();
RandomIter flowIter = CoverageUtilities.getRandomIterator(inFlow);
tcaIter = CoverageUtilities.getRandomIterator(inTca);
netIter = CoverageUtilities.getRandomIterator(inNet);
WritableRaster hackWR = null;
if (doHack) {
hackWR = CoverageUtilities.createDoubleWritableRaster(cols, rows, null, null, JGTConstants.doubleNovalue);
hackWIter = CoverageUtilities.getWritableRandomIterator(hackWR);
}
pm.beginTask("Find outlets...", rows); //$NON-NLS-1$
List<FlowNode> exitsList = new ArrayList<FlowNode>();
for( int r = 0; r < rows; r++ ) {
for( int c = 0; c < cols; c++ ) {
double netValue = netIter.getSampleDouble(c, r, 0);
if (isNovalue(netValue)) {
// we make sure that we pick only outlets that are on the net
continue;
}
FlowNode flowNode = new FlowNode(flowIter, cols, rows, c, r);
if (flowNode.isMarkedAsOutlet()) {
exitsList.add(flowNode);
} else if (flowNode.touchesBound() && flowNode.isValid()) {
// check if the flow exits
Node goDownstream = flowNode.goDownstream();
if (goDownstream == null) {
// flowNode is exit
exitsList.add(flowNode);
}
}
}
pm.worked(1);
}
pm.done();
if (exitsList.size()==0) {
throw new ModelsIllegalargumentException("No outlet has been found in the network. Check your data.", this, pm);
}
SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder();
b.setName("net");
b.setCRS(inFlow.getCoordinateReferenceSystem());
b.add("the_geom", LineString.class);
String hackName = NetworkChannel.HACKNAME;
b.add(hackName, Integer.class);
String strahlerName = NetworkChannel.STRAHLERNAME;
b.add(strahlerName, Integer.class);
b.add(NetworkChannel.PFAFNAME, String.class);
if (inDem != null) {
b.add(NetworkChannel.STARTELEVNAME, Double.class);
b.add(NetworkChannel.ENDELEVNAME, Double.class);
}
SimpleFeatureType type = b.buildFeatureType();
networkBuilder = new SimpleFeatureBuilder(type);
pm.beginTask("Extract vectors...", exitsList.size());
for( FlowNode exitNode : exitsList ) {
/*
* - first hack order is 1
*/
handleTrail(exitNode, null, 1);
pm.worked(1);
}
pm.done();
outNet = new DefaultFeatureCollection();
((DefaultFeatureCollection) outNet).addAll(networkList);
/*
* connect channels
*/
channels = new ArrayList<NetworkChannel>();
for( SimpleFeature network : networkList ) {
channels.add(new NetworkChannel(network));
}
pm.beginTask("Connect channels...", channels.size());
for( NetworkChannel channel : channels ) {
for( NetworkChannel checkChannel : channels ) {
if (channel.equals(checkChannel)) {
continue;
}
channel.checkAndAdd(checkChannel);
}
pm.worked(1);
}
pm.done();
/*
* calculate strahler
*/
pm.beginTask("Calculate Strahler...", IJGTProgressMonitor.UNKNOWN);
calculateStrahler();
pm.done();
/*
* calculate pfaf
*/
pm.beginTask("Calculate Pfafstetter...", IJGTProgressMonitor.UNKNOWN);
calculatePfafstetter();
pm.done();
if (hackWIter != null) {
outHack = CoverageUtilities.buildCoverage("hack", hackWR, regionMap, inFlow.getCoordinateReferenceSystem());
}
}
private void calculatePfafstetter() {
for( int i = 1; i <= maxHack; i++ ) {
// find a channel of that order
List<NetworkChannel> startChannels = new ArrayList<NetworkChannel>();
for( NetworkChannel channel : channels ) {
int hack = channel.getHack();
NetworkChannel nextChannel = channel.getNextChannel();
if (hack == i && (nextChannel == null || nextChannel.getHack() != i)) {
startChannels.add(channel);
}
}
for( NetworkChannel startChannel : startChannels ) {
NetworkChannel nextChannel = startChannel.getNextChannel();
String base = "";
if (nextChannel != null) {
base = nextChannel.getPfaf();
int lastDot = base.lastIndexOf('.');
if (lastDot == -1) {
int lastInt = Integer.parseInt(base);
lastInt = lastInt + 1;
base = lastInt + ".";
} else {
String prefix = base.substring(0, lastDot + 1);
String last = base.substring(lastDot + 1);
int lastInt = Integer.parseInt(last);
lastInt = lastInt + 1;
base = prefix + lastInt + ".";
}
}
int index = 1;
startChannel.setPfafstetter(base + index);
index = index + 2;
while( startChannel.getPreviousChannels().size() > 0 ) {
List<NetworkChannel> previousChannels = startChannel.getPreviousChannels();
for( NetworkChannel networkChannel : previousChannels ) {
if (networkChannel.getHack() == i) {
startChannel = networkChannel;
startChannel.setPfafstetter(base + index);
index = index + 2;
break;
}
}
}
}
}
}
private void calculateStrahler() {
// calculate Strahler
List<NetworkChannel> sourceChannels = new ArrayList<NetworkChannel>();
for( NetworkChannel channel : channels ) {
if (channel.isSource()) {
sourceChannels.add(channel);
// set start
channel.setStrahler(1);
}
}
List<NetworkChannel> nextsList = new ArrayList<NetworkChannel>();
List<NetworkChannel> toKeepList = new ArrayList<NetworkChannel>();
while( true ) {
sourceChannels.addAll(nextsList);
nextsList.clear();
sourceChannels.addAll(toKeepList);
toKeepList.clear();
for( NetworkChannel sourceChannel : sourceChannels ) {
NetworkChannel nextChannel = sourceChannel.getNextChannel();
if (nextChannel != null) {
if (!nextsList.contains(nextChannel))
nextsList.add(nextChannel);
}
}
if (nextsList.size() == 0) {
break;
}
for( NetworkChannel networkChannel : nextsList ) {
List<NetworkChannel> previousChannels = networkChannel.getPreviousChannels();
if (previousChannels.size() == 0) {
throw new RuntimeException();
}
int maxStrahler = 0;
boolean allEqual = true;
int previousStrahler = -1;
boolean doContinue = false;
for( NetworkChannel channel : previousChannels ) {
int strahler = channel.getStrahler();
if (strahler < 0) {
// has not been set yet, keep it
toKeepList.add(channel);
doContinue = true;
break;
} else {
if (strahler > maxStrahler)
maxStrahler = strahler;
if (previousStrahler > 0) {
// was set already
if (previousStrahler != strahler) {
allEqual = false;
}
}
previousStrahler = strahler;
}
}
if (doContinue) {
continue;
}
if (allEqual) {
networkChannel.setStrahler(maxStrahler + 1);
} else {
networkChannel.setStrahler(maxStrahler);
}
}
sourceChannels.clear();
}
}
private void handleTrail( FlowNode runningNode, Coordinate startCoordinate, int hackIndex ) {
List<Coordinate> lineCoordinatesList = new ArrayList<Coordinate>();
if (startCoordinate != null) {
lineCoordinatesList.add(startCoordinate);
// write hack if needed
if (doHack)
runningNode.setValueInMap(hackWIter, hackIndex);
}
// if there are entering nodes
while( runningNode.getEnteringNodes().size() > 0 ) {
int col = runningNode.col;
int row = runningNode.row;
Coordinate coord = CoverageUtilities.coordinateFromColRow(col, row, gridGeometry);
double netValue = netIter.getSampleDouble(col, row, 0);
if (!isNovalue(netValue)) {
// if a net value is available, then it needs to be vector net
lineCoordinatesList.add(coord);
// write hack if needed
if (doHack)
runningNode.setValueInMap(hackWIter, hackIndex);
} else {
/*
* the line is finished
*/
if (lineCoordinatesList.size() < 2) {
throw new RuntimeException();
}
// create a line and finish this trail
createLine(lineCoordinatesList, hackIndex);
break;
}
List<FlowNode> enteringNodes = runningNode.getEnteringNodes();
List<FlowNode> checkedNodes = new ArrayList<FlowNode>();
// we need to check which ones are really net nodes
for( FlowNode tmpNode : enteringNodes ) {
int tmpCol = tmpNode.col;
int tmpRow = tmpNode.row;
double tmpNetValue = netIter.getSampleDouble(tmpCol, tmpRow, 0);
if (!isNovalue(tmpNetValue)) {
checkedNodes.add(tmpNode);
}
}
if (checkedNodes.size() == 1) {
// normal, get the next upstream node and go on
runningNode = checkedNodes.get(0);
} else if (checkedNodes.size() == 0) {
// it was an exit
createLine(lineCoordinatesList, hackIndex);
break;
} else if (checkedNodes.size() > 1) {
createLine(lineCoordinatesList, hackIndex);
if (tcaIter == null) {
// we just extract the vector line
for( FlowNode flowNode : checkedNodes ) {
handleTrail(flowNode, coord, hackIndex + 1);
}
} else {
// we want also hack numbering and friends
FlowNode mainUpstream = runningNode.getUpstreamTcaBased(tcaIter, null);
// the main channel keeps the same index
handleTrail(mainUpstream, coord, hackIndex);
// the others jump up one
for( FlowNode flowNode : checkedNodes ) {
if (!flowNode.equals(mainUpstream)) {
handleTrail(flowNode, coord, hackIndex + 1);
}
}
}
break;
} else {
throw new RuntimeException();
}
}
}
private void createLine( List<Coordinate> lineCoordinatesList, int hackindex ) {
if (lineCoordinatesList.size() < 2) {
return;
}
if (hackindex > maxHack) {
maxHack = hackindex;
}
LineString newNetLine = gf.createLineString(lineCoordinatesList.toArray(new Coordinate[0]));
newNetLine = (LineString) newNetLine.reverse();
Object[] values;
if (inDem == null) {
values = new Object[]{newNetLine, hackindex, 0, "-"};
} else {
Point startPoint = newNetLine.getStartPoint();
Point endPoint = newNetLine.getEndPoint();
Point2D p = new Point2D.Double();
double[] value = new double[1];
p.setLocation(startPoint.getX(), startPoint.getY());
inDem.evaluate(p, value);
double startElev = value[0];
p.setLocation(endPoint.getX(), endPoint.getY());
inDem.evaluate(p, value);
double endElev = value[0];
values = new Object[]{newNetLine, hackindex, 0, "-", startElev, endElev};
}
networkBuilder.addAll(values);
SimpleFeature netFeature = networkBuilder.buildFeature(null);
synchronized (networkList) {
networkList.add(netFeature);
}
}
}