9b98f8c17283f348a1f5073cb37fc7d23c85ebfc
java/com.sap.sailing.windestimation.lab/python/mst_graph_visualizer_graphviz.py
| ... | ... | @@ -101,8 +101,9 @@ def format_distance(node): |
| 101 | 101 | def create_node_label_with_ports(node, best_type=None): |
| 102 | 102 | """ |
| 103 | 103 | Create HTML-like label for a node with 4 compartments. |
| 104 | - Each compartment has a PORT attribute for edge connections. |
|
| 105 | - Distance to parent is shown next to timestamp. |
|
| 104 | + Each compartment has a PORT attribute for incoming edges (from above). |
|
| 105 | + The footer row has additional ports for outgoing edges (going down). |
|
| 106 | + This prevents edges from crossing through the timestamp/distance section. |
|
| 106 | 107 | """ |
| 107 | 108 | compartments = {c['type']: c for c in node['compartments']} |
| 108 | 109 | |
| ... | ... | @@ -118,8 +119,9 @@ def create_node_label_with_ports(node, best_type=None): |
| 118 | 119 | |
| 119 | 120 | # Build HTML table with PORT attributes |
| 120 | 121 | html = '<<TABLE BORDER="1" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">' |
| 121 | - html += '<TR>' |
|
| 122 | 122 | |
| 123 | + # Main row with compartments - ports here are for INCOMING edges (from above) |
|
| 124 | + html += '<TR>' |
|
| 123 | 125 | for type_name in TYPE_ORDER: |
| 124 | 126 | comp = compartments.get(type_name) |
| 125 | 127 | if comp is None: |
| ... | ... | @@ -143,7 +145,7 @@ def create_node_label_with_ports(node, best_type=None): |
| 143 | 145 | wind_str = format_wind(comp) |
| 144 | 146 | abbrev = TYPE_ABBREV[type_name] |
| 145 | 147 | |
| 146 | - # PORT attribute allows edges to connect to this specific cell |
|
| 148 | + # PORT for incoming edges (named after type) |
|
| 147 | 149 | port_name = type_name |
| 148 | 150 | |
| 149 | 151 | # Create cell content with PORT |
| ... | ... | @@ -154,14 +156,25 @@ def create_node_label_with_ports(node, best_type=None): |
| 154 | 156 | ) |
| 155 | 157 | |
| 156 | 158 | html += f'<TD PORT="{port_name}"{border_attr}>{cell_content}</TD>' |
| 157 | - |
|
| 158 | 159 | html += '</TR>' |
| 159 | - # Footer row with timestamp and distance to parent |
|
| 160 | + |
|
| 161 | + # Footer row with timestamp/distance - spans all columns |
|
| 160 | 162 | if dist_str: |
| 161 | 163 | footer_content = f'{time_part} <FONT COLOR="blue">↑{dist_str}</FONT>' |
| 162 | 164 | else: |
| 163 | 165 | footer_content = time_part |
| 164 | 166 | html += f'<TR><TD COLSPAN="4" BGCOLOR="white"><FONT POINT-SIZE="9">{footer_content}</FONT></TD></TR>' |
| 167 | + |
|
| 168 | + # Bottom row with ports for OUTGOING edges (minimal height) |
|
| 169 | + # Each cell corresponds to one compartment position for proper horizontal alignment |
|
| 170 | + html += '<TR>' |
|
| 171 | + for type_name in TYPE_ORDER: |
|
| 172 | + # Port for outgoing edges (named type_out) |
|
| 173 | + out_port_name = f'{type_name}_out' |
|
| 174 | + # Minimal height cells just for port positioning |
|
| 175 | + html += f'<TD PORT="{out_port_name}" BGCOLOR="white" HEIGHT="1"></TD>' |
|
| 176 | + html += '</TR>' |
|
| 177 | + |
|
| 165 | 178 | html += '</TABLE>>' |
| 166 | 179 | |
| 167 | 180 | return html |
| ... | ... | @@ -279,8 +292,10 @@ def visualize_mst_graph(data, output_file=None, max_nodes=100, min_edge_prob=0.0 |
| 279 | 292 | edge_label = f'{from_abbrev}→{to_abbrev}\\n{prob_str}' |
| 280 | 293 | |
| 281 | 294 | # Use PORT to connect to specific compartments |
| 282 | - from_port = f'{from_id}:{from_type}:s' # :s = south (bottom) of cell |
|
| 283 | - to_port = f'{to_id}:{to_type}:n' # :n = north (top) of cell |
|
| 295 | + # Outgoing edges use the _out ports in the footer row (below timestamp) |
|
| 296 | + # Incoming edges use the compartment ports (top of compartment cells) |
|
| 297 | + from_port = f'{from_id}:{from_type}_out:s' # :s = south (bottom) of footer cell |
|
| 298 | + to_port = f'{to_id}:{to_type}:n' # :n = north (top) of compartment cell |
|
| 284 | 299 | |
| 285 | 300 | dot.edge(from_port, to_port, |
| 286 | 301 | label=edge_label, |