java/com.sap.sailing.windestimation/src/com/sap/sailing/windestimation/aggregator/msthmm/MstGraphExporter.java
... ...
@@ -8,10 +8,6 @@ import java.util.HashSet;
8 8
import java.util.Map;
9 9
import java.util.Set;
10 10
11
-import com.sap.sailing.windestimation.aggregator.graph.DijkstraShortestPathFinderImpl;
12
-import com.sap.sailing.windestimation.aggregator.graph.DijsktraShortestPathFinder;
13
-import com.sap.sailing.windestimation.aggregator.graph.ElementAdjacencyQualityMetric;
14
-import com.sap.sailing.windestimation.aggregator.graph.InnerGraphSuccessorSupplier;
15 11
import com.sap.sailing.windestimation.aggregator.hmm.GraphLevelInference;
16 12
import com.sap.sailing.windestimation.aggregator.hmm.GraphNode;
17 13
import com.sap.sailing.windestimation.aggregator.hmm.WindCourseRange;
... ...
@@ -19,8 +15,6 @@ import com.sap.sailing.windestimation.aggregator.msthmm.MstManeuverGraphGenerato
19 15
import com.sap.sailing.windestimation.data.ManeuverForEstimation;
20 16
import com.sap.sailing.windestimation.data.ManeuverTypeForClassification;
21 17
22
-import java.util.function.Supplier;
23
-
24 18
/**
25 19
* Exports MST graph data to JSON format for visualization.
26 20
* The output can be consumed by a Python visualization script.
... ...
@@ -44,22 +38,31 @@ public class MstGraphExporter {
44 38
* @throws IOException if writing fails
45 39
*/
46 40
public void exportToJson(MstManeuverGraphComponents graphComponents, Writer writer) throws IOException {
47
- // Collect all best paths for highlighting
48
- final Set<String> bestPathEdges = collectBestPathEdges(graphComponents);
49
- final Map<String, String> bestNodePerLevel = collectBestNodePerLevel(graphComponents);
41
+ // First, assign node IDs and collect best nodes per level
42
+ final MstGraphLevel root = graphComponents.getRoot();
43
+ final Map<MstGraphLevel, Integer> levelToId = new HashMap<>();
44
+ final int[] nodeIdCounter = {0};
45
+ assignNodeIds(root, levelToId, nodeIdCounter);
46
+
47
+ // Collect best (disambiguated) classification per node
48
+ final Map<String, String> bestNodePerLevel = collectBestNodePerLevel(graphComponents, levelToId);
49
+
50
+ // Derive best path edges from the best node selections
51
+ // This ensures edges connect nodes with red frames (best classifications)
52
+ final Set<String> bestPathEdges = collectBestPathEdges(graphComponents, bestNodePerLevel, levelToId);
53
+
50 54
writer.write("{\n");
51 55
writer.write(" \"nodes\": [\n");
52 56
// Export all nodes starting from root
53
- final MstGraphLevel root = graphComponents.getRoot();
54 57
final boolean[] firstNode = {true};
55
- final Map<MstGraphLevel, Integer> levelToId = new HashMap<>();
56
- final int[] nodeIdCounter = {0};
57
- exportNode(root, writer, firstNode, levelToId, nodeIdCounter, 0);
58
+ final int[] exportNodeIdCounter = {0};
59
+ final Map<MstGraphLevel, Integer> exportLevelToId = new HashMap<>();
60
+ exportNode(root, writer, firstNode, exportLevelToId, exportNodeIdCounter, 0);
58 61
writer.write("\n ],\n");
59 62
writer.write(" \"edges\": [\n");
60 63
// Export edges between nodes
61 64
final boolean[] firstEdge = {true};
62
- exportEdges(root, writer, firstEdge, levelToId, bestPathEdges);
65
+ exportEdges(root, writer, firstEdge, exportLevelToId, bestPathEdges);
63 66
writer.write("\n ],\n");
64 67
// Export best path information
65 68
writer.write(" \"bestPaths\": {\n");
... ...
@@ -178,54 +181,43 @@ public class MstGraphExporter {
178 181
}
179 182
}
180 183
181
- private Set<String> collectBestPathEdges(MstManeuverGraphComponents graphComponents) {
182
- final Set<String> bestPathEdges = new HashSet<>();
183
- final Map<MstGraphLevel, Integer> levelToId = new HashMap<>();
184
- final int[] nodeIdCounter = {0};
185
- assignNodeIds(graphComponents.getRoot(), levelToId, nodeIdCounter);
186
- final ElementAdjacencyQualityMetric<GraphNode<MstGraphLevel>> edgeQualityMetric = (previousNode, currentNode) -> {
187
- return transitionProbabilitiesCalculator.getTransitionProbability(currentNode, previousNode,
188
- previousNode.getGraphLevel() == null ? 0.0 : previousNode.getGraphLevel().getDistanceToParent());
189
- };
190
- for (final MstGraphLevel leaf : graphComponents.getLeaves()) {
191
- final InnerGraphSuccessorSupplier<GraphNode<MstGraphLevel>, MstGraphLevel> innerGraphSuccessorSupplier =
192
- new InnerGraphSuccessorSupplier<>(graphComponents,
193
- (final Supplier<String> nameSupplier) -> new GraphNode<MstGraphLevel>(
194
- null, null, new WindCourseRange(0, 360), 1.0, 0, null) {
195
- @Override
196
- public String toString() {
197
- return nameSupplier.get();
198
- }
199
- });
200
- final DijsktraShortestPathFinder<GraphNode<MstGraphLevel>> dijkstra =
201
- new DijkstraShortestPathFinderImpl<>(
202
- innerGraphSuccessorSupplier.getArtificialLeaf(leaf),
203
- innerGraphSuccessorSupplier.getArtificialRoot(),
204
- innerGraphSuccessorSupplier, edgeQualityMetric);
205
- GraphNode<MstGraphLevel> prev = null;
206
- for (final GraphNode<MstGraphLevel> node : dijkstra.getShortestPath()) {
207
- if (prev != null && prev.getGraphLevel() != null && node.getGraphLevel() != null) {
208
- Integer prevId = levelToId.get(prev.getGraphLevel());
209
- Integer nodeId = levelToId.get(node.getGraphLevel());
210
- if (prevId != null && nodeId != null) {
211
- // Dijkstra goes from leaf to root (child to parent)
212
- // We export edges from parent to child, so store the edge in that direction
213
- String edgeKey = nodeId + "_" + node.getManeuverType().ordinal() +
214
- "_" + prevId + "_" + prev.getManeuverType().ordinal();
215
- bestPathEdges.add(edgeKey);
216
- }
217
- }
218
- prev = node;
184
+ /**
185
+ * Collects the best path edges based on the disambiguated best node selections.
186
+ * An edge is marked as "best" if it connects two nodes where both endpoints
187
+ * have their best (disambiguated) classification matching the edge's from/to types.
188
+ */
189
+ private Set<String> collectBestPathEdges(MstManeuverGraphComponents graphComponents,
190
+ Map<String, String> bestNodePerLevel, Map<MstGraphLevel, Integer> levelToId) {
191
+ Set<String> bestPathEdges = new HashSet<>();
192
+ collectBestPathEdgesRecursive(graphComponents.getRoot(), bestPathEdges, bestNodePerLevel, levelToId);
193
+ return bestPathEdges;
194
+ }
195
+
196
+ private void collectBestPathEdgesRecursive(MstGraphLevel parent, Set<String> bestPathEdges,
197
+ Map<String, String> bestNodePerLevel, Map<MstGraphLevel, Integer> levelToId) {
198
+ Integer parentId = levelToId.get(parent);
199
+ String parentBestType = bestNodePerLevel.get(String.valueOf(parentId));
200
+
201
+ for (MstGraphLevel child : parent.getChildren()) {
202
+ Integer childId = levelToId.get(child);
203
+ String childBestType = bestNodePerLevel.get(String.valueOf(childId));
204
+
205
+ // Mark the edge between the best classifications as the best path edge
206
+ if (parentBestType != null && childBestType != null) {
207
+ int parentTypeOrdinal = ManeuverTypeForClassification.valueOf(parentBestType).ordinal();
208
+ int childTypeOrdinal = ManeuverTypeForClassification.valueOf(childBestType).ordinal();
209
+ String edgeKey = parentId + "_" + parentTypeOrdinal + "_" + childId + "_" + childTypeOrdinal;
210
+ bestPathEdges.add(edgeKey);
219 211
}
212
+
213
+ // Recurse to children
214
+ collectBestPathEdgesRecursive(child, bestPathEdges, bestNodePerLevel, levelToId);
220 215
}
221
- return bestPathEdges;
222 216
}
223 217
224
- private Map<String, String> collectBestNodePerLevel(MstManeuverGraphComponents graphComponents) {
218
+ private Map<String, String> collectBestNodePerLevel(MstManeuverGraphComponents graphComponents,
219
+ Map<MstGraphLevel, Integer> levelToId) {
225 220
final Map<String, String> bestNodePerLevel = new HashMap<>();
226
- final Map<MstGraphLevel, Integer> levelToId = new HashMap<>();
227
- final int[] nodeIdCounter = {0};
228
- assignNodeIds(graphComponents.getRoot(), levelToId, nodeIdCounter);
229 221
// Use the MstBestPathsCalculatorImpl to get the best nodes
230 222
final MstBestPathsCalculatorImpl calculator = new MstBestPathsCalculatorImpl(transitionProbabilitiesCalculator);
231 223
for (final GraphLevelInference<MstGraphLevel> inference : calculator.getBestNodes(graphComponents)) {