/* * 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.hacklength; import static java.lang.Math.pow; import static java.lang.Math.sqrt; import static org.jgrasstools.gears.libs.modules.JGTConstants.doubleNovalue; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSHACKLENGTH_AUTHORCONTACTS; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSHACKLENGTH_AUTHORNAMES; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSHACKLENGTH_DESCRIPTION; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSHACKLENGTH_KEYWORDS; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSHACKLENGTH_LABEL; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSHACKLENGTH_LICENSE; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSHACKLENGTH_NAME; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSHACKLENGTH_STATUS; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSHACKLENGTH_inElevation_DESCRIPTION; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSHACKLENGTH_inFlow_DESCRIPTION; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSHACKLENGTH_inTca_DESCRIPTION; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSHACKLENGTH_outHacklength_DESCRIPTION; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.util.List; import javax.media.jai.iterator.RandomIter; import javax.media.jai.iterator.RandomIterFactory; 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.jgrasstools.gears.libs.modules.Direction; import org.jgrasstools.gears.libs.modules.FlowNode; import org.jgrasstools.gears.libs.modules.JGTModel; import org.jgrasstools.gears.libs.modules.Node; import org.jgrasstools.gears.utils.RegionMap; import org.jgrasstools.gears.utils.coverage.CoverageUtilities; import org.jgrasstools.gears.utils.math.NumericsUtilities; import org.jgrasstools.hortonmachine.i18n.HortonMessageHandler; @Description(OMSHACKLENGTH_DESCRIPTION) @Author(name = OMSHACKLENGTH_AUTHORNAMES, contact = OMSHACKLENGTH_AUTHORCONTACTS) @Keywords(OMSHACKLENGTH_KEYWORDS) @Label(OMSHACKLENGTH_LABEL) @Name(OMSHACKLENGTH_NAME) @Status(OMSHACKLENGTH_STATUS) @License(OMSHACKLENGTH_LICENSE) public class OmsHackLength extends JGTModel { @Description(OMSHACKLENGTH_inFlow_DESCRIPTION) @In public GridCoverage2D inFlow = null; @Description(OMSHACKLENGTH_inTca_DESCRIPTION) @In public GridCoverage2D inTca = null; @Description(OMSHACKLENGTH_inElevation_DESCRIPTION) @In public GridCoverage2D inElevation = null; @Description(OMSHACKLENGTH_outHacklength_DESCRIPTION) @Out public GridCoverage2D outHacklength = null; private HortonMessageHandler msg = HortonMessageHandler.getInstance(); private int nCols; private int nRows; private double xRes; private double yRes; private RegionMap regionMap; @Execute public void process() { if (!concatOr(outHacklength == null, doReset)) { return; } checkNull(inFlow, inTca); regionMap = CoverageUtilities.getRegionParamsFromGridCoverage(inFlow); nCols = regionMap.getCols(); nRows = regionMap.getRows(); xRes = regionMap.getXres(); yRes = regionMap.getYres(); RandomIter tcaIter = CoverageUtilities.getRandomIterator(inTca); RenderedImage flowRI = inFlow.getRenderedImage(); WritableRaster flowWR = CoverageUtilities.renderedImage2WritableRaster(flowRI, true); WritableRandomIter flowIter = RandomIterFactory.createWritable(flowWR, null); // if inElevation isn't null then work in 3d. RandomIter elevIter = null; if (inElevation != null) { elevIter = CoverageUtilities.getRandomIterator(inElevation); } hacklength(flowIter, tcaIter, elevIter); tcaIter.done(); flowIter.done(); if (elevIter != null) { elevIter.done(); } } private void hacklength( RandomIter flowIter, RandomIter tcaIter, RandomIter elevIter ) { double runningDistance = 0.0; double maxTca = 0.0; WritableRaster hacklengthWR = CoverageUtilities.createDoubleWritableRaster(nCols, nRows, null, null, doubleNovalue); WritableRandomIter hacklengthIter = RandomIterFactory.createWritable(hacklengthWR, null); pm.beginTask(msg.message("hacklength.calculating"), nRows); //$NON-NLS-1$ for( int r = 0; r < nRows; r++ ) { for( int c = 0; c < nCols; c++ ) { FlowNode flowNode = new FlowNode(flowIter, nCols, nRows, c, r); if (flowNode.isSource() && !flowNode.isHeadingOutside()) { runningDistance = 0; flowNode.setValueInMap(hacklengthIter, runningDistance); maxTca = 1; FlowNode oldNode = flowNode; FlowNode runningNode = oldNode.goDownstream(); while( runningNode != null && runningNode.isValid() && !runningNode.isMarkedAsOutlet() ) { boolean isMax = tcaMax(runningNode, tcaIter, hacklengthIter, maxTca, runningDistance); if (isMax) { double distance = Direction.forFlow((int) oldNode.flow).getDistance(xRes, yRes); if (elevIter != null) { double d1 = oldNode.getValueFromMap(elevIter); double d2 = runningNode.getValueFromMap(elevIter); double dz = d1 - d2; runningDistance += sqrt(pow(distance, 2) + pow(dz, 2)); } else { runningDistance += distance; } runningNode.setValueInMap(hacklengthIter, runningDistance); maxTca = runningNode.getValueFromMap(tcaIter); } oldNode = runningNode; runningNode = runningNode.goDownstream(); } if (runningNode != null && runningNode.isMarkedAsOutlet()) { if (tcaMax(runningNode, tcaIter, hacklengthIter, maxTca, runningDistance)) { double distance = Direction.forFlow((int) oldNode.flow).getDistance(xRes, yRes); if (elevIter != null) { double d1 = oldNode.getValueFromMap(elevIter); double d2 = runningNode.getValueFromMap(elevIter); double dz = d1 - d2; runningDistance += sqrt(pow(distance, 2) + pow(dz, 2)); } else { runningDistance += distance; } runningNode.setValueInMap(hacklengthIter, runningDistance); } } } } pm.worked(1); } pm.done(); hacklengthIter.done(); outHacklength = CoverageUtilities.buildCoverage("Hacklength", hacklengthWR, regionMap, inFlow.getCoordinateReferenceSystem()); } /** * Compare two value of tca and distance. * * <p> * It's used to evaluate some special distance (as hacklength). * In these case, the value of the distance is a property of * the path, and so when two pixel drain in a same pixel the * actual value is calculate from the pixel that have the * maximum value. So this method evaluate if the distance is * already evaluate, throghout another path, and * if the value of the old path is greater than the next path. * </p> */ public static boolean tcaMax( FlowNode flowNode, RandomIter tcaIter, RandomIter hacklengthIter, double maxTca, double maxDistance ) { List<FlowNode> enteringNodes = flowNode.getEnteringNodes(); for( Node node : enteringNodes ) { double tca = node.getValueFromMap(tcaIter); if (tca >= maxTca) { if (NumericsUtilities.dEq(tca, maxTca)) { if (node.getValueFromMap(hacklengthIter) > maxDistance) return false; } else return false; } } return true; } }