Graphing with PowerShell done easy

PowerShell is nice for getting textual output, csv output, xml output, etc, but there are no built in charting tools. However, luckily PowerShell is based on .NET, which means all the libraries for .NET is available to us. In this article I will show you how to use the Microsoft Chart Controls together with PowerShell to create charts.

The first thing you need to do is to install the Microsoft Chart Controls from here. After this, you can verify with the following lines of code whether you are able to load the assemblies.


[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms.DataVisualization")

This should hopefully just return without output. If so, you are good to go.

To make things a bit easier than working directly with objects, I have made a simple module to create new charts, add datasets to them and display them. The following can either be just pasted into a PowerShell, or better, added to a separate “.psm1” file and loaded with Import-Module.

# Load assembly for Microsoft Chart Controls for Microsoft .NET Framework 3.5
Write-Verbose "Loading assemblies"
[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms.DataVisualization")


function New-Chart
{
    [CmdletBinding()]
    [OutputType([System.Windows.Forms.DataVisualization.Charting.ChartArea])]
    Param
    (
        # Dataset
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$false,
                   Position=0)]
        $Dataset,

        # Width
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$false)]
        [int]$Width = 500,

        # Height
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$false)]
        [int]$Height = 500,

        # X Interval
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$false)]
        [int]$XInterval,

        # Y Interval
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$false)]
        [int]$YInterval,

        # X Title
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$false)]
        [string]$XTitle,

        # Y Title
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$false)]
        [string]$YTitle,

        # Title
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$false)]
        [string]$Title


    )

    # Create chart
    Write-Verbose "Creating chart $Width x $Height"
    $Chart = New-object System.Windows.Forms.DataVisualization.Charting.Chart
    $Chart.Width = $Width
    $Chart.Height = $Height
    $Chart.Left = 0
    $Chart.Top = 0

    # Add chart area to chart
    $ChartArea = New-Object System.Windows.Forms.DataVisualization.Charting.ChartArea
    $Chart.ChartAreas.Add($ChartArea)

    # Set titles and lables
    if($Title) {
        Write-Verbose "Setting title: $Title"
        [void]$Chart.Titles.Add($Title)
    } else {
        Write-Verbose "No title provided"
    }

    if($YTitle) {
        Write-Verbose "Setting Ytitle: $YTitle"
        $ChartArea.AxisY.Title = $YTitle
    } else {
        Write-Verbose "No Ytitle provided"
    }

    if($XTitle) {
        Write-Verbose "Setting Xtitle: $XTitle"
        $ChartArea.AxisX.Title = $XTitle
    } else {
        Write-Verbose "No Xtitle provided"
    }

    if($YInterval) {
        Write-Verbose "Setting Y Interval to $YInterval"
        $ChartArea.AxisY.Interval = $YInterval
    }

    if($XInterval) {
        Write-Verbose "Setting X Interval to $XInterval"
        $ChartArea.AxisX.Interval = $XInterval
    }

    if($Dataset) {
        Write-Verbose "Dataset provided. Adding this as ""default dataset"" with chart type line."
        [void]$Chart.Series.Add("default dataset")
        $Chart.Series["default dataset"].Points.DataBindXY($Dataset.Keys, $Dataset.Values)
        $Chart.Series["default dataset"].ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Line
    }

    return $Chart
}




function Add-ChartDataset
{
    [CmdletBinding()]
    [OutputType([System.Windows.Forms.DataVisualization.Charting.ChartArea])]
    Param
    (
        # Chart
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true,
                   Position=0)]
        $Chart,

        # Dataset
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$false,
                   Position=0)]
        $Dataset,

        # DatasetName
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$false,
                   Position=1)]
        [string]$DatasetName = "Added dataset",

        # SeriesChartType = http://msdn.microsoft.com/en-us/library/system.windows.forms.datavisualization.charting.seriescharttype(v=vs.110).aspx
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$false,
                   Position=2)]
        [string]$SeriesChartType = "Line"
    )

    Write-Verbose "Adding series $Datasetname"
    [void]$Chart.Series.Add($DatasetName)

    Write-Verbose "Adding data binding"
    $Chart.Series[$DatasetName].Points.DataBindXY($Dataset.Keys, $Dataset.Values)

    Write-Verbose "Setting chart type to $SeriesChartType"
    $Chart.Series[$DatasetName].ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::$SeriesChartType

    return $Chart
}





function Show-Chart
{
    [CmdletBinding()]
    [OutputType([void])]
    Param
    (
        # Chart
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true,
                   Position=0)]
        $Chart
    )

    # display the chart on a form
    $Chart.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right -bor [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
    $Form = New-Object Windows.Forms.Form
    $Form.Text = "PowerShell Chart"
    $Form.Width = $chart.Width
    $Form.Height = $chart.Height + 50
    $Form.controls.add($Chart)
    $Form.Add_Shown({$Form.Activate()})
    $Form.ShowDialog() | Out-Null
}

The functions are documented in the module, and available with Get-Help.

Here is a demo script using the functions:

Import-Module .\GoodWorkaroundCharts-v0.1.psm1 -Force

# Create simple dataset
$simpleDataset = @{
    "Microsoft" = 800
    "Apple" = 250
    "Google" = 400
    "RIM" = 0
}

# Create chart and show it
New-Chart -Dataset $simpleDataset | Show-Chart



# Create ordered hashmap
$osloTemperature = [ordered]@{}

# Request weather data for Oslo, and put into dataset
[xml]$weather = (Invoke-WebRequest -Uri http://www.yr.no/place/Norway/Oslo/Oslo/Oslo/varsel.xml).Content
$weather.weatherdata.forecast.tabular.time | foreach {
    $osloTemperature[$_.from] = $_.temperature.value
}

# Create chart, add dataset and show
New-Chart -Title "Temperature in Oslo" -XInterval 4 -YInterval 2 -Width 1200 |
    Add-ChartDataset -Dataset $osloTemperature -DatasetName "Temperature" -SeriesChartType Spline -OutVariable tempChart |
    Show-Chart

# Save the chart as a PNG to the desktop
$tempChart.SaveImage($Env:USERPROFILE + "\Desktop\Chart.png", "PNG")

Hope that helps someone!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s