Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AsBuiltReport.Chart/AsBuiltReport.Chart.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
# NestedModules = @()

# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = 'New-PieChart', 'New-BarChart', 'New-StackedBarChart', 'New-SignalChart', 'New-SingleStackedBarChart', 'New-DonutChart', 'New-RadarChart'
FunctionsToExport = 'New-PieChart', 'New-BarChart', 'New-StackedBarChart', 'New-SignalChart', 'New-SingleStackedBarChart', 'New-DonutChart', 'New-RadarChart', 'New-HeatMapChart'

# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
# CmdletsToExport = '*'
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add SkiaSharp.NativeAssets.macOS dependency to the module to support macOS platform
- Add support for macOS intel in the Pester workflow to test the module on macOS intel platform
- Add Pester test for missing OutputFolderPath in New-PieChart
- Add Heatmap chart support:
- Implement New-HeatmapChart cmdlet and associated classes
- Add pester test to validate the functionality of the New-HeatmapChart cmdlet
- Add example 16 to document on how to use the New-HeatmapChart cmdlet

### Changed

Expand Down
70 changes: 70 additions & 0 deletions Examples/Example16.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<#
.SYNOPSIS
Example 16 - Heat Map Chart

.DESCRIPTION
This example demonstrates how to create a Heat Map Chart using the AsBuiltReport.Chart module.
The chart displays cluster utilization across resource types for multiple clusters.
#>

[CmdletBinding()]
param (
[System.IO.DirectoryInfo] $Path = (Get-Location).Path,
[string] $Format = 'png'
)

<#
Starting with PowerShell v3, modules are auto-imported when needed. Importing the module here
ensures clarity and avoids ambiguity.
#>

# Import-Module AsBuiltReport.Chart -Force -Verbose:$false

<#
Since the chart output is a file, specify the output folder path using $OutputFolderPath.
#>

$OutputFolderPath = Resolve-Path $Path

<#
Define the data to be displayed in the chart.
In a real-world scenario these values would come from your infrastructure query.
#>

$ChartTitle = 'Cluster Utilization'
$Values = @(
@(78, 81, 74),
@(66, 72, 80),
@(88, 91, 85)
)
$RowLabels = @('Cluster A', 'Cluster B', 'Cluster C')
$ColumnLabels = @('CPU', 'Memory', 'Storage')

<#
The New-HeatMapChart cmdlet generates the Heat Map image.

-Title : Sets the chart title displayed at the top of the image.
-Values : 2D matrix of numeric values for the heat map.
-RowLabels : Labels for each row in the heat map.
-ColumnLabels : Labels for each column in the heat map.
-Format : Output file format (e.g. png, jpg, svg).
-OutputFolderPath : Directory where the generated chart file will be saved.
-Width : Width of the chart image in pixels.
-Height : Height of the chart image in pixels.
-Filename : Name of the output file (without extension).
-EnableLegend : Boolean to enable or disable the legend on the chart.
#>

New-HeatMapChart `
-Title $ChartTitle `
-Values $Values `
-RowLabels $RowLabels `
-ColumnLabels $ColumnLabels `
-LabelXAxis 'Resources' `
-LabelYAxis 'Clusters' `
-EnableLegend `
-Format $Format `
-OutputFolderPath $OutputFolderPath `
-Width 700 `
-Height 500 `
-Filename 'Example16-HeatMapChart'
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,16 @@ New-RadarChart -Title 'Test' -Values @(@(1, 2, 5, 8),@(3,5,4,2)) -LegendLabels @
```
![RadarChart](./Samples/RadarChart.png)

### Heat Map Chart
```powershell
# Generate a Heat Map Chart showing utilization across clusters and metrics.
New-HeatMapChart -Title 'Cluster Utilization' -Values @(
@(78, 81, 74),
@(66, 72, 80),
@(88, 91, 85)
) -RowLabels @('Cluster A', 'Cluster B', 'Cluster C') -ColumnLabels @('CPU', 'Memory', 'Storage') -Format 'png' -OutputFolderPath $PWD
```

### :blue_book: Example Index

All examples in the latest release of AsBuiltReport.Chart can be found in the table below.
Expand All @@ -143,6 +153,7 @@ All examples in the latest release of AsBuiltReport.Chart can be found in the ta
| [Example13](./Examples/Example13.ps1) | Donut Chart with Legend, Custom Colors and Border |
| [Example14](./Examples/Example14.ps1) | Basic Radar Chart with Legend |
| [Example15](./Examples/Example15.ps1) | Radar Chart with Legend, Custom Colors and Border |
| [Example16](./Examples/Example16.ps1) | Heat Map Chart for cluster utilization |

## Watermark Support

Expand Down
129 changes: 129 additions & 0 deletions Sources/HeatMapChart.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using System;
using System.IO;
using System.Linq;
using ScottPlot;

namespace AsBuiltReportChart
{
internal class HeatMap : Chart
{
public object Chart(double[][] values, string[] rowLabels, string[] columnLabels, string filename = "output", int width = 400, int height = 300)
{
if (values == null || values.Length == 0 || values[0] == null || values[0].Length == 0)
{
throw new ArgumentException("Error: Values cannot be null or empty.");
}

int rows = values.Length;
int columns = values[0].Length;

for (int rowIndex = 0; rowIndex < rows; rowIndex++)
{
if (values[rowIndex] == null || values[rowIndex].Length != columns)
{
throw new ArgumentException("Error: All rows in Values must have the same length.");
}
}

if (rowLabels != null && rowLabels.Length != rows)
{
throw new ArgumentException("Error: Row labels length must match the number of rows.");
}

if (columnLabels != null && columnLabels.Length != columns)
{
throw new ArgumentException("Error: Column labels length must match the number of columns.");
}

Plot myPlot = new Plot();
double[,] matrix = new double[rows, columns];

for (int rowIndex = 0; rowIndex < rows; rowIndex++)
{
for (int columnIndex = 0; columnIndex < columns; columnIndex++)
{
matrix[rowIndex, columnIndex] = values[rowIndex][columnIndex];
}
}

var heatmap = myPlot.Add.Heatmap(matrix);

if (EnableLegend)
{
var colorBar = myPlot.Add.ColorBar(heatmap, ScottPlot.Edge.Right);
colorBar.Width = 20;
colorBar.Label = "Value";
}

myPlot.Axes.Left.Label.Text = LabelYAxis;
myPlot.Axes.Bottom.Label.Text = LabelXAxis;
myPlot.Axes.Left.Label.FontSize = LabelFontSize;
myPlot.Axes.Left.Label.ForeColor = GetDrawingColor(LabelFontColor);
myPlot.Axes.Left.Label.FontName = FontName;
myPlot.Axes.Left.Label.Bold = LabelBold;
myPlot.Axes.Bottom.Label.FontSize = LabelFontSize;
myPlot.Axes.Bottom.Label.ForeColor = GetDrawingColor(LabelFontColor);
myPlot.Axes.Bottom.Label.FontName = FontName;
myPlot.Axes.Bottom.Label.Bold = LabelBold;

myPlot.Axes.Left.TickLabelStyle.FontSize = LabelFontSize;
myPlot.Axes.Left.TickLabelStyle.ForeColor = GetDrawingColor(LabelFontColor);
myPlot.Axes.Left.TickLabelStyle.FontName = FontName;
myPlot.Axes.Bottom.TickLabelStyle.FontSize = LabelFontSize;
myPlot.Axes.Bottom.TickLabelStyle.ForeColor = GetDrawingColor(LabelFontColor);
myPlot.Axes.Bottom.TickLabelStyle.FontName = FontName;

if (rowLabels != null && rowLabels.Length > 0)
{
double[] positions = Enumerable.Range(0, rowLabels.Length).Select(i => (double)i).ToArray();
myPlot.Axes.Left.SetTicks(positions, rowLabels);
}

if (columnLabels != null && columnLabels.Length > 0)
{
double[] positions = Enumerable.Range(0, columnLabels.Length).Select(i => (double)i).ToArray();
myPlot.Axes.Bottom.SetTicks(positions, columnLabels);
}

myPlot.HideGrid();
myPlot.Axes.Top.IsVisible = false;
myPlot.Axes.Right.IsVisible = false;

if (Title != null)
{
myPlot.Title(Title);
myPlot.Axes.Title.Label.FontSize = TitleFontSize;
myPlot.Axes.Title.Label.ForeColor = GetDrawingColor(TitleFontColor);
myPlot.Axes.Title.Label.Bold = TitleFontBold;
myPlot.Axes.Title.Label.FontName = FontName;
}

myPlot.Axes.Margins(left: AxesMarginsLeft, right: AxesMarginsRight, bottom: AxesMarginsDown, top: AxesMarginsTop);

if (FigureBackgroundColor.HasValue)
{
myPlot.FigureBackground.Color = GetDrawingColor(FigureBackgroundColor.Value);
}

if (DataBackgroundColor.HasValue)
{
myPlot.DataBackground.Color = GetDrawingColor(DataBackgroundColor.Value);
}

if (EnableChartBorder)
{
myPlot.FigureBorder = new LineStyle()
{
Color = GetDrawingColor(ChartBorderColor),
Width = ChartBorderSize,
Pattern = ChartBorderStyleMap[ChartBorderStyle],
};
}

ApplyWatermark(myPlot);

string filepath = _outputFolderPath ?? Directory.GetCurrentDirectory();
return SaveInFormat(myPlot, width, height, filepath, filename, Format);
}
}
}
Loading