Source code for physdes.steiner_forest.congestion_map

[docs] def draw_congestion_map( grid: list[list[int]], filename: str = "congestion_map.svg" ) -> None: """ Generate an SVG visualization of a network congestion map. This function creates an SVG image showing a grid where each cell's color represents its congestion level (0-100%), using a green-yellow-red gradient. :param grid: A 2D list of integers representing congestion values (0-100). :type grid: list[list[int]] :param filename: The output filename for the SVG file. :type filename: str Examples: >>> grid = [[0, 50, 100], [25, 75, 50]] >>> draw_congestion_map(grid, "test.svg") Congestion map saved to test.svg """ if not grid or not grid[0]: raise ValueError("Grid must not be empty") rows = len(grid) cols = len(grid[0]) cell_size = 50 padding = 20 legend_width = 100 title_height = 60 width = cols * cell_size + 2 * padding + legend_width height = rows * cell_size + 2 * padding + title_height def interpolate_color(value: int) -> str: """Interpolate from green (0) -> yellow (50) -> red (100)""" if value <= 50: # Green to Yellow red_val = int(255 * (value / 50)) green_val = 255 blue_val = 0 else: # Yellow to Red red_val = 255 green_val = int(255 * ((50 - (value - 50)) / 50)) blue_val = 0 return f"#{red_val:02x}{green_val:02x}{blue_val:02x}" svg = [ f'<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg">' ] svg.append("<defs>") svg.append('<linearGradient id="grad" x1="0%" y1="0%" x2="0%" y2="100%">') svg.append('<stop offset="0%" style="stop-color:#00ff00"/>') svg.append('<stop offset="50%" style="stop-color:#ffff00"/>') svg.append('<stop offset="100%" style="stop-color:#ff0000"/>') svg.append("</linearGradient>") svg.append("</defs>") # Background svg.append(f'<rect width="{width}" height="{height}" fill="#ffffff"/>') # Title svg.append( f'<text x="{width // 2}" y="{padding + 20}" font-size="24" text-anchor="middle" font-family="Arial">Network Congestion Map</text>' ) # Draw grid cells for row_idx in range(rows): for col_idx in range(cols): value = grid[row_idx][col_idx] if not (0 <= value <= 100): raise ValueError(f"Congestion value {value} out of range [0,100]") x_pos = padding + col_idx * cell_size y_pos = title_height + padding + row_idx * cell_size color = interpolate_color(value) svg.append( f'<rect x="{x_pos}" y="{y_pos}" width="{cell_size}" height="{cell_size}" fill="{color}" stroke="#cccccc" stroke-width="1"/>' ) # Optional: show value in center svg.append( f'<text x="{x_pos + cell_size // 2}" y="{y_pos + cell_size // 2 + 5}" font-size="14" text-anchor="middle" fill="black" font-family="Arial">{value}</text>' ) # Legend legend_x = padding + cols * cell_size + 30 legend_y = title_height + padding + 50 svg.append( f'<text x="{legend_x}" y="{legend_y - 20}" font-size="16" font-family="Arial">Congestion %</text>' ) svg.append( f'<rect x="{legend_x}" y="{legend_y}" width="30" height="{rows * cell_size}" fill="url(#grad)"/>' ) # Legend labels for label_val in range(0, 101, 25): y_pos = legend_y + int((100 - label_val) / 100 * (rows * cell_size)) svg.append( f'<text x="{legend_x + 40}" y="{y_pos + 5}" font-size="12" font-family="Arial">{label_val}</text>' ) svg.append( f'<line x1="{legend_x - 5}" y1="{y_pos}" x2="{legend_x}" y2="{y_pos}" stroke="black" stroke-width="1"/>' ) svg.append("</svg>") with open(filename, "w", encoding="utf-8") as f: f.write("\n".join(svg)) print(f"Congestion map saved to {filename}")
grid = [ [0, 20, 40, 80, 100], [10, 30, 60, 90, 70], [25, 50, 75, 95, 50], [0, 15, 35, 55, 30], ] draw_congestion_map(grid, "my_congestion.svg")