Revision: 6566 http://sourceforge.net/p/jump-pilot/code/6566 Author: michaudm Date: 2020-10-03 17:59:16 +0000 (Sat, 03 Oct 2020) Log Message: ----------- Add Shreve, Horton and Hack Orders to Stream Order plugin
Modified Paths: -------------- plug-ins/GraphToolboxPlugin/trunk/build.xml plug-ins/GraphToolboxPlugin/trunk/doc/GraphToolboxExtension4OJ.odt plug-ins/GraphToolboxPlugin/trunk/doc/GraphToolboxExtension4OJ_fr.odt plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/GraphExtension.java plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/StrahlerNumberPlugIn.java plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph.properties plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph_cz.properties plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph_fi.properties plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph_fr.properties plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph_it.properties Added Paths: ----------- plug-ins/GraphToolboxPlugin/trunk/lib/jump-jgrapht-0.7.1.jar Removed Paths: ------------- plug-ins/GraphToolboxPlugin/trunk/lib/jump-jgrapht-0.7.jar Modified: plug-ins/GraphToolboxPlugin/trunk/build.xml =================================================================== --- plug-ins/GraphToolboxPlugin/trunk/build.xml 2020-10-02 14:29:59 UTC (rev 6565) +++ plug-ins/GraphToolboxPlugin/trunk/build.xml 2020-10-03 17:59:16 UTC (rev 6566) @@ -17,7 +17,7 @@ <property name="dist" value="dist" /> <property name="javadoc" value="javadoc" /> - <property name="version" value="0.7.0" /> + <property name="version" value="0.8.0" /> <!-- =================================================================== --> <!-- Defines the classpath used for compilation and test. --> Modified: plug-ins/GraphToolboxPlugin/trunk/doc/GraphToolboxExtension4OJ.odt =================================================================== (Binary files differ) Modified: plug-ins/GraphToolboxPlugin/trunk/doc/GraphToolboxExtension4OJ_fr.odt =================================================================== (Binary files differ) Added: plug-ins/GraphToolboxPlugin/trunk/lib/jump-jgrapht-0.7.1.jar =================================================================== (Binary files differ) Index: plug-ins/GraphToolboxPlugin/trunk/lib/jump-jgrapht-0.7.1.jar =================================================================== --- plug-ins/GraphToolboxPlugin/trunk/lib/jump-jgrapht-0.7.1.jar 2020-10-02 14:29:59 UTC (rev 6565) +++ plug-ins/GraphToolboxPlugin/trunk/lib/jump-jgrapht-0.7.1.jar 2020-10-03 17:59:16 UTC (rev 6566) Property changes on: plug-ins/GraphToolboxPlugin/trunk/lib/jump-jgrapht-0.7.1.jar ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Deleted: plug-ins/GraphToolboxPlugin/trunk/lib/jump-jgrapht-0.7.jar =================================================================== (Binary files differ) Modified: plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/GraphExtension.java =================================================================== --- plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/GraphExtension.java 2020-10-02 14:29:59 UTC (rev 6565) +++ plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/GraphExtension.java 2020-10-03 17:59:16 UTC (rev 6566) @@ -35,8 +35,9 @@ * <li>CycleFinderPlugIn : computes a graph from a linear network and find base cycles</li> * </ul> * @author Michaël Michaud - * @version 0.7.0 (2020-09-18) + * @version 0.8.0 (2020-10-03) */ +//version 0.8.0 (2020-10-03) Add Shreve, Horton and Hack Orders to Stream Order plugin //version 0.7.0 (2020-09-23) Make strahlerNumber plugin more memory friendly and way faster //version 0.6.3 (2019-06-24) SkeletonPlugIn : improve meanWidth calculation //version 0.6.2 (2019-05-22) fix bug in SkeletonPlugIn : incorrect relativeMinForkLength @@ -64,7 +65,7 @@ } public String getVersion() { - return "0.7.0 (2020-09-23)"; + return "0.8.0 (2020-10-03)"; } public void configure(PlugInContext context) throws Exception { Modified: plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/StrahlerNumberPlugIn.java =================================================================== --- plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/StrahlerNumberPlugIn.java 2020-10-02 14:29:59 UTC (rev 6565) +++ plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/StrahlerNumberPlugIn.java 2020-10-03 17:59:16 UTC (rev 6566) @@ -8,6 +8,7 @@ import com.vividsolutions.jump.workbench.model.Layer; import com.vividsolutions.jump.workbench.model.StandardCategoryNames; import com.vividsolutions.jump.workbench.plugin.*; +import com.vividsolutions.jump.workbench.ui.AttributeTypeFilter; import com.vividsolutions.jump.workbench.ui.GUIUtil; import com.vividsolutions.jump.workbench.ui.MenuNames; import com.vividsolutions.jump.workbench.ui.MultiInputDialog; @@ -19,6 +20,8 @@ import javax.swing.*; import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.util.*; /** @@ -29,16 +32,44 @@ private static String LAYER; - private static String STRAHLER_NUMBERS; + private static String STREAM_ORDER; private static String GRAPH_COMPUTATION; private static String OLD_ALGO; + private static String LENGTH_ATTRIBUTE; - private static final String STREAM_ORDER = "StreamOrder"; - private static final String SEGMENT_ORIGIN = "SegmentOrig"; + private static final String STRAHLER = "Strahler"; + private static String STRAHLER_TT; + private static final String SEGMENT_ORIGIN = "SegmentOrig"; + private static final String SHREVE = "Shreve"; + private static String SHREVE_TT; + private static String METRICS; + private static final String FLOW_ACC = "FlowAcc"; + private static final String MAX_DIST = "MaxDist"; + private static final String HORTON = "Horton"; + private static String HORTON_TT; + private static final String HACK = "Hack"; + private static String HACK_TT; + private static final String HACK_DIST_ORDER = "HackDistO"; + private static final String HACK_DIST = "HackDist"; + private static final String HACK_FLOW_ORDER = "HackFlowO"; + private static final String HACK_FLOW = "HackFlow"; + private static final String HACK_DF_ORDER = "HackDFO"; + private static final String HACK_DF = "HackDF"; + private static final String MOUTH_DISTANCE = "MouthDist"; + Layer layer; boolean old_algo = false; + boolean shreve = false; + boolean metrics = false; + boolean horton = false; + boolean hack = false; + String lengthAttribute; + int lengthAttributeIndex; + boolean lengthAttributeIsGeometry; + + public String getName() {return "Graph nodes PlugIn";} public void initialize(final PlugInContext context) throws Exception { @@ -47,12 +78,18 @@ LAYER = I18N.get("ui.GenericNames.LAYER"); GRAPH_COMPUTATION = I18NPlug.getI18N("Graph-computation"); - STRAHLER_NUMBERS = I18NPlug.getI18N("StrahlerNumberPlugIn.strahler-numbers"); + STREAM_ORDER = I18NPlug.getI18N("StrahlerNumberPlugIn.stream-order"); + STRAHLER_TT = I18NPlug.getI18N("StrahlerNumberPlugIn.strahler-tt"); + SHREVE_TT = I18NPlug.getI18N("StrahlerNumberPlugIn.shreve-tt"); OLD_ALGO = I18NPlug.getI18N("StrahlerNumberPlugIn.old-algorithm"); + METRICS = I18NPlug.getI18N("StrahlerNumberPlugIn.metrics"); + LENGTH_ATTRIBUTE = I18NPlug.getI18N("StrahlerNumberPlugIn.length-attribute"); + HORTON_TT = I18NPlug.getI18N("StrahlerNumberPlugIn.horton-tt"); + HACK_TT = I18NPlug.getI18N("StrahlerNumberPlugIn.hack-tt"); context.getFeatureInstaller().addMainMenuPlugin( this, new String[]{MenuNames.PLUGINS, GRAPH}, - STRAHLER_NUMBERS + "...", + STREAM_ORDER + "...", false, null, new MultiEnableCheck() .add(context.getCheckFactory().createTaskWindowMustBeActiveCheck()) .add(context.getCheckFactory().createAtLeastNLayersMustExistCheck(1))); @@ -60,19 +97,23 @@ @Override public boolean execute(PlugInContext context) throws Exception { - final MultiInputDialog dialog = new MultiInputDialog( - context.getWorkbenchFrame(), STRAHLER_NUMBERS, true); - dialog.setSideBarImage(new ImageIcon(this.getClass().getResource("StrahlerNumber.png"))); - dialog.setSideBarDescription(I18NPlug.getI18N("StrahlerNumberPlugIn.description")); - dialog.addLayerComboBox(LAYER, context.getCandidateLayer(0), null, context.getLayerManager()); - dialog.addCheckBox(OLD_ALGO, old_algo); + final MultiInputDialog dialog = initDialog(context); - GUIUtil.centreOnWindow(dialog); - dialog.setPreferredSize(new Dimension(400,480)); + GUIUtil.centreOnWindow(dialog); + dialog.setPreferredSize(new Dimension(480,480)); dialog.setVisible(true); if (dialog.wasOKPressed()) { layer = dialog.getLayer(LAYER); old_algo = dialog.getBoolean(OLD_ALGO); + shreve = dialog.getBoolean(SHREVE); + metrics = dialog.getBoolean(METRICS); + lengthAttribute = dialog.getText(LENGTH_ATTRIBUTE); + lengthAttributeIndex = layer.getFeatureCollectionWrapper() + .getFeatureSchema().getAttributeIndex(lengthAttribute); + lengthAttributeIsGeometry = lengthAttributeIndex == + layer.getFeatureCollectionWrapper().getFeatureSchema().getGeometryIndex(); + hack = dialog.getBoolean(HACK); + horton = dialog.getBoolean(HORTON); return true; } else return false; @@ -86,8 +127,21 @@ // Creates the schema for the output dataset (nodes) final FeatureSchema newSchema = sourceFC.getFeatureSchema().clone(); - newSchema.addAttribute(STREAM_ORDER, AttributeType.INTEGER); + newSchema.addAttribute(STRAHLER, AttributeType.INTEGER); newSchema.addAttribute(SEGMENT_ORIGIN, AttributeType.OBJECT); + if (shreve) newSchema.addAttribute(SHREVE, AttributeType.DOUBLE); + if (metrics) newSchema.addAttribute(MAX_DIST, AttributeType.DOUBLE); + if (metrics) newSchema.addAttribute(FLOW_ACC, AttributeType.DOUBLE); + if (hack) { + newSchema.addAttribute(HACK_DIST_ORDER, AttributeType.INTEGER); + newSchema.addAttribute(HACK_DIST, AttributeType.DOUBLE); + newSchema.addAttribute(HACK_FLOW_ORDER, AttributeType.INTEGER); + newSchema.addAttribute(HACK_FLOW, AttributeType.DOUBLE); + newSchema.addAttribute(MOUTH_DISTANCE, AttributeType.DOUBLE); + newSchema.addAttribute(HACK_DF_ORDER, AttributeType.INTEGER); + newSchema.addAttribute(HACK_DF, AttributeType.DOUBLE); + } + if (horton) newSchema.addAttribute(HORTON, AttributeType.INTEGER); FeatureCollection resultFC = new FeatureDataset(newSchema); for (Object o : sourceFC.getFeatures()) { Feature f = (Feature)o; @@ -105,18 +159,41 @@ int count = 0; int total = resultFC.size(); for (FeatureAsEdge arc : graph.edgeSet()) { - if (arc.getAttribute(STREAM_ORDER) != null) continue; - if (old_algo) computeOldStreamOrder(graph, arc); - else computeNewStreamOrder(graph, arc); - monitor.report(count++, total, " features processed"); + if (arc.getAttribute(STRAHLER) != null) continue; + if (old_algo) computeLegacyStrahlerOrder(graph, arc); + else computeNewStrahlerOrder(graph, arc); + monitor.report(count++, total, " features processed (Strahler)"); } // Change -1 (cycles or cycle successors) to null for (FeatureAsEdge arc : graph.edgeSet()) { - Object order = arc.getAttribute(STREAM_ORDER); + Object order = arc.getAttribute(STRAHLER); if (order != null && (Integer)order == -1) { - arc.setAttribute(STREAM_ORDER,null); + arc.setAttribute(STRAHLER,null); } } + if (metrics) { + int maxDistIdx = newSchema.getAttributeIndex(MAX_DIST); + int flowAccIdx = newSchema.getAttributeIndex(FLOW_ACC); + for (FeatureAsEdge arc : graph.edgeSet()) { + if (arc.getAttribute(MAX_DIST) != null) continue; + computeMaxLengthAndFlowAcc(graph, arc, maxDistIdx, flowAccIdx); + monitor.report(count++, total, " features processed (max dist /flow accumulation)"); + } + if (horton) { + for (FeatureAsEdge arc : graph.edgeSet()) { + if (arc.getAttribute(HORTON) != null) continue; + computeHortonStreamOrder(graph, arc); + monitor.report(count++, total, " features processed (Horton)"); + } + } + if (hack) { + for (FeatureAsEdge arc : graph.edgeSet()) { + if (arc.getAttribute(HACK_DIST_ORDER) != null) continue; + computeHackStreamOrder(graph, arc); + monitor.report(count++, total, " features processed (Hack)"); + } + } + } context.getLayerManager().addLayer(StandardCategoryNames.RESULT, layer.getName()+"-strahler",resultFC); Layer resultLayer = context.getLayerManager().getLayer(layer.getName() + "-strahler"); @@ -142,7 +219,7 @@ * @param graph * @param arc */ - private void computeOldStreamOrder(DirectedWeightedPseudograph<INode,FeatureAsEdge> graph, + private void computeLegacyStrahlerOrder(DirectedWeightedPseudograph<INode,FeatureAsEdge> graph, FeatureAsEdge arc) { int maxOrder = 0; @@ -149,7 +226,7 @@ int occ = 0; FeatureAsEdge maxUpstream = null; for (FeatureAsEdge upstream : graph.incomingEdgesOf(graph.getEdgeSource(arc))) { - Object att = upstream.getAttribute(STREAM_ORDER); + Object att = upstream.getAttribute(STRAHLER); // Process current stream only if all upstreams are already processed if (att == null) return; int upstreamOrder = (Integer)att; @@ -172,12 +249,16 @@ } // Head water of a stream (edge without predecessor) has order 1 if (maxOrder == 0) { - arc.setAttribute(STREAM_ORDER, 1); + arc.setAttribute(STRAHLER, 1); + if (shreve) arc.setAttribute(SHREVE, 1.0); + //if (metrics) arc.setAttribute(FLOW_ACC, getLength(arc)); } else { // Stream order of the current edge is incremented if it has 2 or more // predecessors = maxOrder - arc.setAttribute(STREAM_ORDER, occ>1?maxOrder+1:maxOrder); + arc.setAttribute(STRAHLER, occ>1?maxOrder+1:maxOrder); + if (shreve) arc.setAttribute(SHREVE, calculateShreveNumber(graph, arc)); + //if (metrics) arc.setAttribute(FLOW_ACC, calculateFlowAccumulation(graph, arc)); } // Try to compute stream order recursively on all downstream edges Set<FeatureAsEdge> downStreams = graph.outgoingEdgesOf(graph.getEdgeTarget(arc)); @@ -184,12 +265,24 @@ for (FeatureAsEdge downStream : downStreams) { // In case of anastomosis, compute the downstream edge only once - if (downStream.getAttribute(STREAM_ORDER) == null) { - computeOldStreamOrder(graph, downStream); + if (downStream.getAttribute(STRAHLER) == null) { + computeLegacyStrahlerOrder(graph, downStream); } } } + private double calculateShreveNumber(DirectedWeightedPseudograph<INode,FeatureAsEdge> graph, + FeatureAsEdge arc) { + INode n = graph.getEdgeSource(arc); + double s = 0.0; + int attIdx = arc.getSchema().getAttributeIndex(SHREVE); + for (FeatureAsEdge e : graph.incomingEdgesOf(n)) { + s += e.getDouble(attIdx); + } + return s / graph.outDegreeOf(graph.getEdgeSource(arc)); + } + + private Set<Integer> getAncestors(DirectedWeightedPseudograph<INode,FeatureAsEdge> graph, FeatureAsEdge arc, Set<Integer> ancestors) { for (FeatureAsEdge e : graph.incomingEdgesOf(graph.getEdgeSource(arc))) { @@ -214,18 +307,18 @@ // A FAST RECURSIVE GIS ALGORITHM FOR COMPUTING STRAHLER STREAM ORDER // IN BRAIDED AND NON BRAIDED NETWORKS // Alexander Gleyzer, Michael Denisyuk, Alon Rimmer, and Yigal Salingar (2004) - private void computeNewStreamOrder(DirectedWeightedPseudograph<INode,FeatureAsEdge> graph, + private void computeNewStrahlerOrder(DirectedWeightedPseudograph<INode,FeatureAsEdge> graph, FeatureAsEdge arc) { - Object streamOrder = arc.getAttribute(STREAM_ORDER); + Object strahlerOrder = arc.getAttribute(STRAHLER); // If arc already has a positive stream order, don't process it again // If its stream-order = -1, it means it belongs to a cycle, or it belongs // to an ancestor and has been pre-set to -1 to detect cycles // In all of these case, we don't want to process it - if (streamOrder != null) return; + if (strahlerOrder != null) return; // Flag current edge to be able to identify cycles while exploring ancestors // recursively - arc.setAttribute(STREAM_ORDER, -1); + arc.setAttribute(STRAHLER, -1); // Stream order of the current edge has not yet been computed. // Compute predecessors stream order first (recursion) @@ -233,11 +326,11 @@ boolean cycle = false; for (FeatureAsEdge upStream : upStreams) { // Visit/compute ancestors recursively - computeNewStreamOrder(graph, upStream); + computeNewStrahlerOrder(graph, upStream); // Post order : check that current stream is not part of a cycle // if upstream == -1, it means it has already been initialized in the context // of this recursive process (cycle) - Object upStreamOrder = upStream.getAttribute(STREAM_ORDER); + Object upStreamOrder = upStream.getAttribute(STRAHLER); assert upStreamOrder != null; // stream order is initialized to -1 in pre-order if ((Integer)upStreamOrder == -1) { cycle = true; @@ -254,7 +347,7 @@ INode maxOrderOrigin = null; int occ = 0; for (FeatureAsEdge upstream : graph.incomingEdgesOf(graph.getEdgeSource(arc))) { - Object att1 = upstream.getAttribute(STREAM_ORDER); + Object att1 = upstream.getAttribute(STRAHLER); assert att1 != null; int upstreamOrder = (Integer)att1; @@ -274,22 +367,306 @@ } // Set the stream order and segment origin of the current edge if (maxOrder == 0) { - arc.setAttribute(STREAM_ORDER, 1); + arc.setAttribute(STRAHLER, 1); arc.setAttribute(SEGMENT_ORIGIN, graph.getEdgeSource(arc)); + if (shreve) arc.setAttribute(SHREVE, 1.0); + //if (flowAcc) arc.setAttribute(FLOW_ACC, getLength(arc)); } else if (occ > 1) { - arc.setAttribute(STREAM_ORDER, maxOrder+1); + arc.setAttribute(STRAHLER, maxOrder+1); arc.setAttribute(SEGMENT_ORIGIN, graph.getEdgeSource(arc)); + if (shreve) arc.setAttribute(SHREVE, calculateShreveNumber(graph, arc)); + //if (flowAcc) arc.setAttribute(FLOW_ACC, calculateFlowAccumulation(graph, arc)); } else { - arc.setAttribute(STREAM_ORDER, maxOrder); + arc.setAttribute(STRAHLER, maxOrder); arc.setAttribute(SEGMENT_ORIGIN, maxOrderOrigin); + if (shreve) arc.setAttribute(SHREVE, calculateShreveNumber(graph, arc)); + //if (flowAcc) arc.setAttribute(FLOW_ACC, calculateFlowAccumulation(graph, arc)); } } + private void computeMaxLengthAndFlowAcc(DirectedWeightedPseudograph<INode,FeatureAsEdge> graph, + FeatureAsEdge arc, int maxDistIdx, int flowAccIdx) { + // Interrupt infinite recursion in case of cycle + if (arc.getAttribute(MAX_DIST) != null) return; + arc.setAttribute(MAX_DIST, getLength(arc)); + arc.setAttribute(FLOW_ACC, getLength(arc)); + // Compute predecessors stream order first (recursion) + Set<FeatureAsEdge> upStreams = graph.incomingEdgesOf(graph.getEdgeSource(arc)); + // Visit/compute ancestors recursively + for (FeatureAsEdge upStream : upStreams) { + computeMaxLengthAndFlowAcc(graph, upStream, maxDistIdx, flowAccIdx); + } + // We are now in the normal situation of an edge whith all its ancestors computed + double maxMaxDist = 0; + double maxFlowAcc = 0; + for (FeatureAsEdge upstream : graph.incomingEdgesOf(graph.getEdgeSource(arc))) { + double maxDist = upstream.getDouble(maxDistIdx); + double flowAcc = upstream.getDouble(flowAccIdx); + if (maxDist > maxMaxDist) maxMaxDist = maxDist; + if (flowAcc > maxFlowAcc) maxFlowAcc = flowAcc; + } + arc.setAttribute(MAX_DIST, arc.getDouble(maxDistIdx) + maxMaxDist); + arc.setAttribute(FLOW_ACC, calculateFlowAccumulation(graph, arc)); + } + private double getLength(FeatureAsEdge arc) { + return lengthAttributeIsGeometry ? + arc.getGeometry().getLength() : arc.getDouble(lengthAttributeIndex); + } + private double calculateFlowAccumulation(DirectedWeightedPseudograph<INode,FeatureAsEdge> graph, + FeatureAsEdge arc) { + INode n = graph.getEdgeSource(arc); + double s = 0.0; + for (FeatureAsEdge e : graph.incomingEdgesOf(n)) { + s += (Double)e.getAttribute(FLOW_ACC); + } + return getLength(arc) + + s / graph.outDegreeOf(graph.getEdgeSource(arc)); + } + + private void computeHortonStreamOrder(DirectedWeightedPseudograph<INode,FeatureAsEdge> graph, + FeatureAsEdge arc) { + // Horton's computation needs Strahler order + if (arc.getAttribute(STRAHLER) == null) return; + if (graph.outDegreeOf(graph.getEdgeTarget(arc)) == 0) { + arc.setAttribute(HORTON, arc.getAttribute(STRAHLER)); + } else { + Set<FeatureAsEdge> downStreams = graph.outgoingEdgesOf(graph.getEdgeTarget(arc)); + int maxDownStreamOrder = 0; + for (FeatureAsEdge d : downStreams) { + // Calculate Horton order of successors first + if (d.getAttribute(HORTON) == null) { + computeHortonStreamOrder(graph, d); + } + Object h = d.getAttribute(HORTON); + if (h == null) continue; + if ((Integer)h > maxDownStreamOrder) maxDownStreamOrder = (Integer)h; + } + // When Horton order of all successors have been calculated, + // iterate through all tributaries flowing this arc's successors + // to find the main one + int maxOrder = 0; + double maxFlow = 0.0; + FeatureAsEdge maxEdge = null; + // Compare all incoming edges arriving at the same node as arc + Set<FeatureAsEdge> upStreams = graph.incomingEdgesOf(graph.getEdgeTarget(arc)); + for (FeatureAsEdge e : upStreams) { + if (e.getAttribute(STRAHLER) == null) continue; + int order = (Integer)e.getAttribute(STRAHLER); + double flow = (Double)e.getAttribute(FLOW_ACC); + if (order > maxOrder || (order == maxOrder && flow > maxFlow)) { + maxOrder = order; + maxFlow = flow; + maxEdge = e; + } + } + for (FeatureAsEdge e : upStreams) { + if (e == maxEdge) { + e.setAttribute(HORTON, maxDownStreamOrder); + } else { + e.setAttribute(HORTON, e.getAttribute(STRAHLER)); + } + } + } + } + + private void computeHackStreamOrder(DirectedWeightedPseudograph<INode,FeatureAsEdge> graph, + FeatureAsEdge arc) { + arc.setAttribute(HACK_DIST_ORDER, Integer.MAX_VALUE); + arc.setAttribute(HACK_FLOW_ORDER, Integer.MAX_VALUE); + if (graph.outDegreeOf(graph.getEdgeTarget(arc)) == 0) { + arc.setAttribute(HACK_DIST_ORDER, 1); + arc.setAttribute(HACK_FLOW_ORDER, 1); + arc.setAttribute(HACK_DF_ORDER, 1); + arc.setAttribute(HACK_DIST, arc.getAttribute(MAX_DIST)); + arc.setAttribute(HACK_FLOW, arc.getAttribute(FLOW_ACC)); + arc.setAttribute(HACK_DF, (double)arc.getAttribute(FLOW_ACC)*(double)arc.getAttribute(MAX_DIST)); + arc.setAttribute(MOUTH_DISTANCE, getLength(arc)); + } else { + Set<FeatureAsEdge> downStreams = graph.outgoingEdgesOf(graph.getEdgeTarget(arc)); + boolean cycle = false; + for (FeatureAsEdge d : downStreams) { + // Calculate Hack order of successors first + if (d.getAttribute(HACK_DIST_ORDER) == null || d.getAttribute(HACK_FLOW_ORDER) == null) { + computeHackStreamOrder(graph, d); + } + // cycle detection + Object hdo = d.getAttribute(HACK_DIST_ORDER); + Object hfo = d.getAttribute(HACK_FLOW_ORDER); + assert hdo != null && hfo != null; + if ((Integer)hdo == -1 || (Integer)hfo == -1) { + cycle = true; + } + + } + FeatureAsEdge mainDownStreamD = null; + FeatureAsEdge mainDownStreamF = null; + FeatureAsEdge mainDownStreamDF = null; + double maxMouthDist = 0.0; + // In case of division, choose the downstream which is the farthest from the river mouth + // to avoid little forks which do not throw into the sea + double maxProductD = 0.0; + double maxProductF = 0.0; + double maxProductDF = 0.0; + for (FeatureAsEdge d : downStreams) { + //Object hackD = d.getAttribute(HACK_DIST_ORDER); + //Object hackF = d.getAttribute(HACK_FLOW_ORDER); + Object mouthDist = d.getAttribute(MOUTH_DISTANCE); + Object hackDistOrder = d.getAttribute(HACK_DIST_ORDER); + if (hackDistOrder != null && mouthDist != null && + (double)mouthDist / (int)hackDistOrder > maxProductD) { + maxProductD = (double)mouthDist / (int)hackDistOrder; + mainDownStreamD = d; + } + Object hackFlowOrder = d.getAttribute(HACK_FLOW_ORDER); + if (hackFlowOrder != null && mouthDist != null && + (double)mouthDist / (int)hackFlowOrder > maxProductF) { + maxProductF = (double)mouthDist / (int)hackFlowOrder; + mainDownStreamF = d; + } + Object hackDFOrder = d.getAttribute(HACK_DF_ORDER); + if (hackDFOrder != null && mouthDist != null && + (double)mouthDist / (int)hackDFOrder > maxProductDF) { + maxProductDF = (double)mouthDist / (int)hackDFOrder; + mainDownStreamDF = d; + } + if (mouthDist != null && (Double)mouthDist > maxMouthDist) { + maxMouthDist = (Double) d.getAttribute(MOUTH_DISTANCE); + } + } + arc.setAttribute(MOUTH_DISTANCE, getLength(arc) + maxMouthDist); + int downStreamHackDistanceOrder = 1; + double downStreamDistance = 0.0; + if (mainDownStreamD != null && mainDownStreamD.getAttribute(HACK_DIST_ORDER) != null) { + downStreamHackDistanceOrder = (int)mainDownStreamD.getAttribute(HACK_DIST_ORDER); + downStreamDistance = (double)mainDownStreamD.getAttribute(HACK_DIST); + } + int downStreamHackFlowOrder = 1; + double downStreamFlow = 0.0; + if (mainDownStreamF != null && mainDownStreamF.getAttribute(HACK_FLOW_ORDER) != null) { + downStreamHackFlowOrder = (int)mainDownStreamF.getAttribute(HACK_FLOW_ORDER); + downStreamFlow = (double)mainDownStreamF.getAttribute(HACK_FLOW); + } + int downStreamHackDFOrder = 1; + double downStreamDF = 0.0; + if (mainDownStreamDF != null && mainDownStreamDF.getAttribute(HACK_DF_ORDER) != null) { + downStreamHackDFOrder = (int)mainDownStreamDF.getAttribute(HACK_DF_ORDER); + downStreamDF = (double)mainDownStreamDF.getAttribute(HACK_DF); + } + //int downStreamHackD = 1; + //int downStreamHackF = 1; + //Object downStreamDist = arc.getAttribute(MAX_DIST); + //Object downStreamFlow = arc.getAttribute(FLOW_ACC); + //if (mainDownStreamD != null) { + // downStreamHackD = (int)mainDownStreamD.getAttribute(HACK_DIST_ORDER); + // downStreamHackF = (int)mainDownStreamF.getAttribute(HACK_FLOW_ORDER); + // downStreamDist = mainDownStreamD.getAttribute(HACK_DIST); + // downStreamFlow = mainDownStreamF.getAttribute(HACK_FLOW); + //} + // Process cycles as a stream start + //if (minHackD == Integer.MAX_VALUE) { + // minHackD = 1; + // downStreamDist = (Double)arc.getAttribute(MAX_DIST); + //} + //if (minHackF == Integer.MAX_VALUE) { + // minHackF = 1; + // downStreamFlow = (Double)arc.getAttribute(FLOW_ACC); + //} + + //maxDownStreamMouthDist = (Double)arc.getAttribute(MOUTH_DISTANCE); + + // When Horton order of all successors have been calculated, + // search the main tributary + double maxFlow = 0.0; + double maxDist = 0.0; + double maxDF = 0.0; + FeatureAsEdge maxFlowEdge = null; + FeatureAsEdge maxDistEdge = null; + FeatureAsEdge maxDFEdge = null; + // Compare all incoming edges arriving at the same node as arc + Set<FeatureAsEdge> upStreams = graph.incomingEdgesOf(graph.getEdgeTarget(arc)); + for (FeatureAsEdge e : upStreams) { + double flow = (Double)e.getAttribute(FLOW_ACC); + double dist = (Double)e.getAttribute(MAX_DIST); + double df = (Double)e.getAttribute(MAX_DIST)*(Double)e.getAttribute(FLOW_ACC); + if (flow > maxFlow) { + maxFlow = flow; + maxFlowEdge = e; + } + if (dist > maxDist) { + maxDist = dist; + maxDistEdge = e; + } + if (df > maxDF) { + maxDF = df; + maxDFEdge = e; + } + } + for (FeatureAsEdge e : upStreams) { + if (e == maxFlowEdge) { + e.setAttribute(HACK_FLOW_ORDER, downStreamHackFlowOrder); + e.setAttribute(HACK_FLOW, downStreamFlow); + } else { + e.setAttribute(HACK_FLOW_ORDER, downStreamHackFlowOrder + 1); + e.setAttribute(HACK_FLOW, e.getAttribute(FLOW_ACC)); + } + if (e == maxDistEdge) { + e.setAttribute(HACK_DIST_ORDER, downStreamHackDistanceOrder); + e.setAttribute(HACK_DIST, downStreamDistance); + } else { + e.setAttribute(HACK_DIST_ORDER, downStreamHackDistanceOrder + 1); + e.setAttribute(HACK_DIST, e.getAttribute(MAX_DIST)); + } + if (e == maxDFEdge) { + e.setAttribute(HACK_DF_ORDER, downStreamHackDFOrder); + e.setAttribute(HACK_DF, downStreamDF); + } else { + e.setAttribute(HACK_DF_ORDER, downStreamHackDFOrder + 1); + e.setAttribute(HACK_DF, (double)e.getAttribute(MAX_DIST)*(double)e.getAttribute(FLOW_ACC)); + } + e.setAttribute(MOUTH_DISTANCE, getLength(e) + maxMouthDist); + } + } + } + + + public MultiInputDialog initDialog(final PlugInContext context) { + final MultiInputDialog dialog = new MultiInputDialog( + context.getWorkbenchFrame(), STREAM_ORDER, true); + dialog.setSideBarImage(new ImageIcon(this.getClass().getResource("StrahlerNumber.png"))); + dialog.setSideBarDescription(I18NPlug.getI18N("StrahlerNumberPlugIn.description")); + dialog.addLayerComboBox(LAYER, context.getCandidateLayer(0), null, context.getLayerManager()); + dialog.addCheckBox(OLD_ALGO, old_algo); + dialog.addCheckBox(SHREVE, shreve, SHREVE_TT); + + // ---------- orders based on cumulative stream length flows ---------- + dialog.addSeparator(); + JCheckBox flowAccumulationCB = dialog.addCheckBox(METRICS, metrics); + JComboBox lengthAttributeCB = dialog.addAttributeComboBox(LENGTH_ATTRIBUTE, LAYER, + new AttributeTypeFilter(AttributeTypeFilter.GEOMETRY + AttributeTypeFilter.DOUBLE), + null); + lengthAttributeCB.setEnabled(metrics); + JCheckBox hortonCB = dialog.addCheckBox(HORTON, horton, HORTON_TT); + hortonCB.setEnabled(metrics); + JCheckBox hackCB = dialog.addCheckBox(HACK, hack, HACK_TT); + hackCB.setEnabled(metrics); + flowAccumulationCB.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent actionEvent) { + lengthAttributeCB.setEnabled(flowAccumulationCB.isSelected()); + hortonCB.setEnabled(flowAccumulationCB.isSelected()); + hackCB.setEnabled(flowAccumulationCB.isSelected()); + } + }); + return dialog; + } + + + ColorThemingStyle getColorThemingStyle() { BasicStyle dbs = new BasicStyle(); dbs.setLineColor(new Color(255,0,0)); dbs.setLineWidth(2); BasicStyle bs1 = new BasicStyle(); bs1.setLineColor(new Color(120,240,255)); bs1.setLineWidth(1); @@ -304,7 +681,7 @@ BasicStyle bs10 = new BasicStyle(); bs10.setLineColor(new Color(180, 0,255)); bs10.setLineWidth(13); BasicStyle bs11 = new BasicStyle(); bs11.setLineColor(new Color(210, 0,255)); bs11.setLineWidth(15); BasicStyle bs12 = new BasicStyle(); bs12.setLineColor(new Color(240, 0,255)); bs12.setLineWidth(18); - ColorThemingStyle cts = new ColorThemingStyle(STREAM_ORDER, + ColorThemingStyle cts = new ColorThemingStyle(STRAHLER, CollectionUtil.createMap(new Object[]{ 1, bs1, 2, bs2, Modified: plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph.properties =================================================================== --- plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph.properties 2020-10-02 14:29:59 UTC (rev 6565) +++ plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph.properties 2020-10-03 17:59:16 UTC (rev 6566) @@ -117,8 +117,9 @@ CycleFinderPlugIn.homogeneous-cycles = Homogeneous cycles CycleFinderPlugIn.heterogeneous-cycles = Heterogeneous cycles -StrahlerNumberPlugIn.strahler-numbers = Strahler Numbers -StrahlerNumberPlugIn.description = This plugin adds an attribute containing the Strahler Number.\ +StrahlerNumberPlugIn.stream-order = Stream order +StrahlerNumberPlugIn.description = This plugin creates a new Layer containing the Strahler Stream Order \ + and optionally, some other stream ordering of a hydrographic network.\ The Strahler number qualifies each edge of a hierarchical network. The value is 1 for source edges, \ and n+1 for an edge with at least two parents of value n.\nWarning : presence of cycles in the \ graph break the algorithm which will set a null value to all edges flowing from a cycle edge.\n\ @@ -127,6 +128,12 @@ braided networks (see documentation), and is way more efficient (both from a memory and from a speed \ perspective). StrahlerNumberPlugIn.old-algorithm = Old algorithm (legacy) +StrahlerNumberPlugIn.strahler-tt = Strahler's stream order +StrahlerNumberPlugIn.shreve-tt = Shreve's stream magnitude +StrahlerNumberPlugIn.metrics = Flow accumulation and max distance to the source +StrahlerNumberPlugIn.length-attribute = Length attribute +StrahlerNumberPlugIn.horton-tt = Horton's stream order +StrahlerNumberPlugIn.hack-tt = Hack's stream order SkeletonPlugIn = Skeleton SkeletonPlugIn.skeletonize = Skeletonize Modified: plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph_cz.properties =================================================================== --- plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph_cz.properties 2020-10-02 14:29:59 UTC (rev 6565) +++ plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph_cz.properties 2020-10-03 17:59:16 UTC (rev 6566) @@ -111,8 +111,9 @@ CycleFinderPlugIn.homogeneous-cycles = Stejnorod\u00E9 kruhy CycleFinderPlugIn.heterogeneous-cycles = Nestejnorod\u00E9 kruhy -StrahlerNumberPlugIn.strahler-numbers = Strahler Numbers -StrahlerNumberPlugIn.description = This plugin adds an attribute containing the Strahler Number.\ +StrahlerNumberPlugIn.stream-order = Stream order +StrahlerNumberPlugIn.description = This plugin creates a new Layer containing the Strahler Stream Order \ + and optionally, some other stream ordering of a hydrographic network.\ The Strahler number qualifies each edge of a hierarchical network. The value is 1 for source edges, \ and n+1 for an edge with at least two parents of value n.\nWarning : presence of cycles in the \ graph break the algorithm which will set a null value to all edges flowing from a cycle edge.\n\ @@ -121,6 +122,13 @@ braided networks (see documentation), and is way more efficient (both from a memory and from a speed \ perspective). StrahlerNumberPlugIn.old-algorithm = Old algorithm (legacy) +StrahlerNumberPlugIn.old-algorithm = Old algorithm (legacy) +StrahlerNumberPlugIn.strahler-tt = Strahler's stream order +StrahlerNumberPlugIn.shreve-tt = Shreve's stream magnitude +StrahlerNumberPlugIn.metrics = Flow accumulation and max distance to the source +StrahlerNumberPlugIn.length-attribute = Length attribute +StrahlerNumberPlugIn.horton-tt = Horton's stream order +StrahlerNumberPlugIn.hack-tt = Hack's stream order SkeletonPlugIn = #T: Skeleton SkeletonPlugIn.skeletonize = #T: Skeletonize Modified: plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph_fi.properties =================================================================== --- plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph_fi.properties 2020-10-02 14:29:59 UTC (rev 6565) +++ plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph_fi.properties 2020-10-03 17:59:16 UTC (rev 6566) @@ -105,7 +105,7 @@ use-attribute=K\u00E4yt\u00E4 ominaisuustietoa use-attribute-tooltip=Luo erilliset graafit jokaiselle ominaisuustiedon arvolle -StrahlerNumberPlugIn.strahler-numbers = Strahlerin numerot +StrahlerNumberPlugIn.stream-order = Stream order StrahlerNumberPlugIn.description = T\u00E4m\u00E4 laajennus lis\u00E4\u00E4 tasolle ominaisuuden johon tallennetaan Strahlerin numerot.\ Strahlerin numero luokittelee hierarkisen verkon jokaisen s\u00E4rm\u00E4n. Arvo on 1 l\u00E4hdes\u00E4rmille, \ ja n+1 jokaiselle s\u00E4rm\u00E4lle jolla on v\u00E4hint\u00E4\u00E4n 2 emoa, jonka arvo on n.\n Varoitus : Jos tasossa on piirej\u00E4 niin \ @@ -115,6 +115,13 @@ braided networks (see documentation), and is way more efficient (both from a memory and from a speed \ perspective). StrahlerNumberPlugIn.old-algorithm = Old algorithm (legacy) +StrahlerNumberPlugIn.old-algorithm = Old algorithm (legacy) +StrahlerNumberPlugIn.strahler-tt = Strahler's stream order +StrahlerNumberPlugIn.shreve-tt = Shreve's stream magnitude +StrahlerNumberPlugIn.metrics = Flow accumulation and max distance to the source +StrahlerNumberPlugIn.length-attribute = Length attribute +StrahlerNumberPlugIn.horton-tt = Horton's stream order +StrahlerNumberPlugIn.hack-tt = Hack's stream order SkeletonPlugIn=Luurankograafi SkeletonPlugIn.skeletonize = Luo luurankograafi Modified: plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph_fr.properties =================================================================== --- plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph_fr.properties 2020-10-02 14:29:59 UTC (rev 6565) +++ plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph_fr.properties 2020-10-03 17:59:16 UTC (rev 6566) @@ -117,9 +117,10 @@ CycleFinderPlugIn.homogeneous-cycles = Cycles homog\u00E8nes CycleFinderPlugIn.heterogeneous-cycles = Cycles h\u00E9t\u00E9rog\u00E8nes -StrahlerNumberPlugIn.strahler-numbers = Nombres de Strahler -StrahlerNumberPlugIn.description = Ce plugin ajoute un attribut contenant le nombre de Strahler.\ - Le nombre de Strahler qualifie chaque arc d'un r\xE9seau hi\xE9rarchique. Un arc situ\xE9 \xE0 une source du r\xE9seau \ +StrahlerNumberPlugIn.stream-order = Ordre d'un cours d'eau +StrahlerNumberPlugIn.description = Ce plugin cr\xE9e une nouvelle couche contenant l'ordre de Strahler \ + de chaque tron\xE7on ainsi que d'autres ordres en option.\ + L'ordre de Strahler qualifie chaque arc d'un r\xE9seau hi\xE9rarchique. Un arc situ\xE9 \xE0 une source du r\xE9seau \ prend la valeur 1 tandis qu'un arc ayant au moins 2 ant\xE9c\xE9dents de valeur n prend la valeur n+1.\ \nAttention : la pr\xE9sence de cycles casse l'algorithme qui affecte une valeur nulle \xE0 tous les arcs \ descendant de l'un des arcs du cycle.\n\ @@ -127,6 +128,12 @@ in braided and nonbraided networks". Il diff\xE8re de l'ancien par la fa\xE7on de g\xE9rer les r\xE9seaux anastamos\xE9s.\n\ (cf. documentation), ainsi que par son efficacit\xE9 (tant en m\xE9moire qu'en vitesse d'ex\xE9cution). StrahlerNumberPlugIn.old-algorithm = Ancien algorithme (historique) +StrahlerNumberPlugIn.strahler-tt = Ordre de Strahler +StrahlerNumberPlugIn.shreve-tt = Magnitude de Shreve +StrahlerNumberPlugIn.metrics = Flux cumul\xE9 et distance maximum \xE0 la source +StrahlerNumberPlugIn.length-attribute = Attribut longueur +StrahlerNumberPlugIn.horton-tt = Ordre de Horton +StrahlerNumberPlugIn.hack-tt = Ordre de Hack SkeletonPlugIn = Squelettisation SkeletonPlugIn.skeletonize = Squelettiser Modified: plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph_it.properties =================================================================== --- plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph_it.properties 2020-10-02 14:29:59 UTC (rev 6565) +++ plug-ins/GraphToolboxPlugin/trunk/src/fr/michaelm/jump/plugin/graph/graph_it.properties 2020-10-03 17:59:16 UTC (rev 6566) @@ -101,8 +101,9 @@ Layer=Livello Node=Nodo Nodes=Nodi -StrahlerNumberPlugIn.strahler-numbers = Strahler Numbers -StrahlerNumberPlugIn.description = This plugin adds an attribute containing the Strahler Number.\ +StrahlerNumberPlugIn.stream-order = Stream order +StrahlerNumberPlugIn.description = This plugin creates a new Layer containing the Strahler Stream Order \ + and optionally, some other stream ordering of a hydrographic network.\ The Strahler number qualifies each edge of a hierarchical network. The value is 1 for source edges, \ and n+1 for an edge with at least two parents of value n.\nWarning : presence of cycles in the \ graph break the algorithm which will set a null value to all edges flowing from a cycle edge.\n\ @@ -111,6 +112,12 @@ braided networks (see documentation), and is way more efficient (both from a memory and from a speed \ perspective). StrahlerNumberPlugIn.old-algorithm = Old algorithm (legacy) +StrahlerNumberPlugIn.strahler-tt = Strahler's stream order +StrahlerNumberPlugIn.shreve-tt = Shreve's stream magnitude +StrahlerNumberPlugIn.metrics = Flow accumulation and max distance to the source +StrahlerNumberPlugIn.length-attribute = Length attribute +StrahlerNumberPlugIn.horton-tt = Horton's stream order +StrahlerNumberPlugIn.hack-tt = Hack's stream order (based on max length acc and total flow acc) use-attribute=Usa un attributo use-attribute-tooltip=Crea grafici distinti per didtinti valori di attributo _______________________________________________ Jump-pilot-devel mailing list Jump-pilot-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel