#
ChartImage Powershell Module
#
Static Website 👾
So I didn't think I would be making this many Powershell modules all in the month of July 2022. Guess it shows that if you are feeling productive you can be productive
As I think I mentioned it is really easy to put a decent looking documentation site together using Retype. It's quite old-skool 📼 but at the same time nicely presented. That got me thinking that the Retype template is ⭐ great looking out the box, and has enough easy-to-use components using simple markdown. I have also mentioned how much I miss Powershell Universal originally I got hooked on this program with the charts that it offered. They were just out of this world, and the fact you did it all in Powershell made it even better.
#
Image-Charts.com 💡
I wasn't looking for a new challenge or module to release, but when I stumbled on the C# library for image-charts.com I thought this would make an excellent binary module. Although essentially this is just API calling which could have been done with Powershell and Invoke-WebRequest or something similar. I just fancied trying to improve my non-existent C# skills. Before I got deep-diving into this project, I thought it would make sense to first have a butchers at the powershellgallery.com to make sure there wasn't already an image-charts module, and there wasn't. To my knowledge there isn't a lot apart from Powershell Universal that have lots of super cool charts (I know as I built a good number of charting modules for this product) but again I knew I couldn't have dynamic charts running on a static website. However I thought it would still make a page look even nicer if you had some data displayed on the page.
#
Building the project 👷
Originally I was thinking this was going to be super-easy to smash module. As I did a build and release and with the sample I found it just worked. Then the parameter list started to grow and grow 🌱 then me being me, I maybe had added these parameters without testing the build at each modification. So suddenly what I thought would work didn't quite work out as planned. Then all the parameters that I didn't specify as mandatory were expecting a value. I had to figure out how to pass a value to the variable for that parameter value should it be empty. I do not know C# so was happy when I got it all pretty much working to allow you as a potential end user to not be bombarded entering values for 13 different paramters. Instead I only made 3 mandatory paramters path
chartType
and dataPoints
to me these were the 3 fundamental things you needed to specify to produce a chart of your choice and the data in it, and finally the output path to store the image of the chart.
#
Help 📣
Not sure why I didn't think about this in previous binary modules. Maybe it's because I only got building them this month, but I was thinking I should add as much help as I could to the parameters via the help message for each parameter. I did this as it wasn't super obvious to me how I could layout the data to the way I wanted it, or how to choose different colours or labels to be displayed. There were just so many possibilities to each chart you could make. The more parameters the more possibilities. So although I have not created all the possible parameters, depending on how popular this module becomes, will be the deciding factor on time spent developing it more.
#
Two Commands 🧦 🧤
Maybe things are better in pairs, just like socks or maybe gloves. Originally I was thinking I just wanted to be able to save the image of the chart I was generating to a specific directory. I was really happy I had got it all working so I created a quick repository, got an icon, then posted it to the Powershell gallery. Then I was thinking 🤔 what if people just wanted the URL to be generated for there image? No problem I saw that this was one of the capabilities and as I had already done a namespace for the saving of the chart, it really was a case of copying and pasting that editing a few bits, then I suddenly had 2 commands to go with this module instead of just one. Allowing you to either save the image or to generate the automatic URL to view your image online.
#
Code Time 🥁
I thought I would post the final .CS code that made up this binary module. So without further hold-up here it is:-
using System;
using System.Management.Automation;
using ImageChartsLib;
namespace ChartImage
{
[Cmdlet(VerbsData.Save, "ChartImage")]
public class Program : PSCmdlet
{
[Parameter(Mandatory = true, HelpMessage = "Allows you to select the type of chart you want to generate. Please refer to documentation.image-charts.com for full reference. Or live dangerously and choose one.")]
[ValidateSetAttribute("bvs", "bvg", "bhs", "bhg", "bvo", "p", "p3", "pc", "pd", "ls", "lc", "lxy", "ls:nda", "lc:nda", "lxy:nda", "pa", "bb", "qr")]
public string? Chart { get; set; }
[Parameter(HelpMessage = "Allows you to decide which Axis you want to display on your chart")]
[ValidateSetAttribute("x", "y", "x,y", " ")]
public string? AxisValues { get; set; }
[Parameter(Mandatory = true, HelpMessage = "You need to specify the path you want to output the image chart to and specify the image format at the end")]
public string? Path { get; set; }
[Parameter(HelpMessage = "Allows you to set a title for the image chart you are generating.")]
public string? Title { get; set; }
[Parameter(HelpMessage = "Allows you to set the size of the image chart you want to save. If you do not specify a value this will default to 300x300. A valid input for this is WidthxHeight so 400x700 is valid for 400px width and 700px height output.")]
public string? Size { get; set; }
[Parameter(Mandatory = true, HelpMessage = "Defaulted to use the Awesome Text Format, all you need to do is apply comma seperated values, if you want stacked results then simple pipe the data '15,25,30|45,75,99' would show three stacked results")]
public string? DataPoints { get; set; }
[Parameter(HelpMessage = "Enter a number within speech marks to pass as the gradient to round corners. Normally about a number of 14 is more then enough roundness")]
public string? Rounded { get; set; }
[Parameter(HelpMessage = "Allows you to place values on the actual chart datapoint, again each value entry needs to be seperated using the pipe symbol 'profit|loss' then for each entry add another pipe to display it")]
public string? Labels { get; set; }
[Parameter(HelpMessage = "Selects the colour for each datapoint you define. You need to seperate the colours you select using the pipe symbol | and take full 6 digit colour code 'E08E45|F8F4A6' is a valid input for two sets of datapoints ")]
public string? Colors { get; set; }
[Parameter(HelpMessage = "This adds a legend to your chart and this will display the text in the legend. You need to seperate the text using the pipe symbol | so you could enter 'Jan|Feb' as a valid input")]
public string? LegendText { get; set; }
[Parameter(HelpMessage = "Again please refer to documentation.image-charts.com for full information, this allows you to set custom text along one of the Axis display, however you need to do it in either '0:|Jan|Feb|Mar' or '1:|Jan|Feb|Mar' depending on which Axis you want the custom text displayed.")]
//[ValidatePattern("[0:|*]|[1:|*]|' '")]
public string? AxisText { get; set; }
[Parameter(HelpMessage = "Allows you to position where you want the legend displayede left,right,top or bottom. This uses the first letter to refer to each of those")]
[ValidateSetAttribute("r", "t", "b", "l", " ")]
public string? LegendPosition { get; set; }
[Parameter(HelpMessage = "Please refer to documentation.image-charts.com/reference/grid-lines/ for full information this allows you to set gridlines, as well as having ability to make them dotted lines, or the colour of the gridlines. This defaults to '20,20,5,5' if not set")]
public string? GridLines { get; set; }
protected override void EndProcessing()
{
string? chartPath = Path;
string? chartType = Chart;
string? chartTitle = Title ?? " ";
string? chartSize = Size ?? "300x300";
string? chartData = DataPoints;
string? chartRounded = Rounded ?? "0";
string? chartLabels = Labels ?? " ";
string? chartAxisValues = AxisValues ?? "x,y";
string? chartAxisText = AxisText ?? "";
string? chartColors = Colors ?? "00A5C6";
string? chartLegendText = LegendText ?? " ";
string? chartLegendPosition = LegendPosition ?? "";
string? chartGrid = GridLines ?? "20,20,5,5";
new ImageCharts()
.cht(chartType)
.chtt(chartTitle)
.chl(chartLabels)
.chbr(chartRounded)
.chs(chartSize)
.chd("a:" + chartData)
.chco(chartColors)
.chxt(chartAxisValues)
.chxl(chartAxisText)
.chdl(chartLegendText)
.chdlp(chartLegendPosition)
.chg(chartGrid)
.toFile(chartPath);
WriteObject("Your chart has now been saved to this location:-" + " " + chartPath);
}
}
}
namespace ChartURL
{
[Cmdlet(VerbsCommon.Get, "ChartURL")]
public class Program : PSCmdlet
{
[Parameter(Mandatory = true, HelpMessage = "Allows you to select the type of chart you want to generate. Please refer to documentation.image-charts.com for full reference. Or live dangerously and choose one.")]
[ValidateSetAttribute("bvs", "bvg", "bhs", "bhg", "bvo", "p", "p3", "pc", "pd", "ls", "lc", "lxy", "ls:nda", "lc:nda", "lxy:nda", "pa", "bb", "qr")]
public string? Chart { get; set; }
[Parameter(HelpMessage = "Allows you to decide which Axis you want to display on your chart")]
[ValidateSetAttribute("x", "y", "x,y", " ")]
public string? AxisValues { get; set; }
[Parameter(HelpMessage = "Allows you to set a title for the image chart you are generating.")]
public string? Title { get; set; }
[Parameter(HelpMessage = "Allows you to set the size of the image chart you want to save. If you do not specify a value this will default to 300x300. A valid input for this is WidthxHeight so 400x700 is valid for 400px width and 700px height output.")]
public string? Size { get; set; }
[Parameter(Mandatory = true, HelpMessage = "Defaulted to use the Awesome Text Format, all you need to do is apply comma seperated values, if you want stacked results then simple pipe the data '15,25,30|45,75,99' would show three stacked results")]
public string? DataPoints { get; set; }
[Parameter(HelpMessage = "Enter a number within speech marks to pass as the gradient to round corners. Normally about a number of 14 is more then enough roundness")]
public string? Rounded { get; set; }
[Parameter(HelpMessage = "Allows you to place values on the actual chart datapoint, again each value entry needs to be seperated using the pipe symbol 'profit|loss' then for each entry add another pipe to display it")]
public string? Labels { get; set; }
[Parameter(HelpMessage = "Selects the colour for each datapoint you define. You need to seperate the colours you select using the pipe symbol | and take full 6 digit colour code 'E08E45|F8F4A6' is a valid input for two sets of datapoints ")]
public string? Colors { get; set; }
[Parameter(HelpMessage = "This adds a legend to your chart and this will display the text in the legend. You need to seperate the text using the pipe symbol | so you could enter 'Jan|Feb' as a valid input")]
public string? LegendText { get; set; }
[Parameter(HelpMessage = "Again please refer to documentation.image-charts.com for full information, this allows you to set custom text along one of the Axis display, however you need to do it in either '0:|Jan|Feb|Mar' or '1:|Jan|Feb|Mar' depending on which Axis you want the custom text displayed.")]
//[ValidatePattern("[0:|*]|[1:|*]|' '")]
public string? AxisText { get; set; }
[Parameter(HelpMessage = "Allows you to position where you want the legend displayede left,right,top or bottom. This uses the first letter to refer to each of those")]
[ValidateSetAttribute("r", "t", "b", "l", " ")]
public string? LegendPosition { get; set; }
[Parameter(HelpMessage = "Please refer to documentation.image-charts.com/reference/grid-lines/ for full information this allows you to set gridlines, as well as having ability to make them dotted lines, or the colour of the gridlines. This defaults to '20,20,5,5' if not set")]
public string? GridLines { get; set; }
protected override void EndProcessing()
{
string? chartType = Chart;
string? chartTitle = Title ?? " ";
string? chartSize = Size ?? "300x300";
string? chartData = DataPoints;
string? chartRounded = Rounded ?? "0";
string? chartLabels = Labels ?? " ";
string? chartAxisValues = AxisValues ?? "x,y";
string? chartAxisText = AxisText ?? "";
string? chartColors = Colors ?? "00A5C6";
string? chartLegendText = LegendText ?? " ";
string? chartLegendPosition = LegendPosition ?? "";
string? chartGrid = GridLines ?? "20,20,5,5";
string chartUrl = new ImageCharts()
.cht(chartType)
.chtt(chartTitle)
.chl(chartLabels)
.chbr(chartRounded)
.chs(chartSize)
.chd("a:" + chartData)
.chco(chartColors)
.chxt(chartAxisValues)
.chxl(chartAxisText)
.chdl(chartLegendText)
.chdlp(chartLegendPosition)
.chg(chartGrid)
.toURL();
WriteObject("The URL for your chart is:-" + " " + chartUrl);
}
}
}
Hopefully you can see why it ended up taking longer than expected, with all the parameters I kept adding, after thinking damn that's a cool parameter to have. As mentioned before there is a lot more parameters I could add should this module be a popular one.
#
Demo Time 📈
Okay I guess it's time to show you nice reader how the magic works. I cannot recommend enough that you take time to visit
To see what you can do with these charts, and how the parameters can affect the display of the chart.
I have not included the chartjs in this module
If I have not included enough documentation on this page or with the help in the parameters that I have included in the module this please check the documentation as well which is located here.
Install-Module -Name ChartImage
Import-Module ChartImage
Save-ChartImage -Path C:\out\age.png -DataPoints "13,12,7,5" -Chart bhg -Title "Age Chart" -Size 400x600 -AxisText "1:|Aurora|Zelda|Pixie|Laila-Belle" -LegendPosition b -Colors "93B7BE|554348|D4F5F5|747578" -Rounded 14
So the next time someone asks me how old all my daughters are... 😁
Save-ChartImage -Path C:\out\age2.png -DataPoints "5,7,12,13" -Chart bvg -Title "Age Chart" -Size 400x600 -AxisText "0:|Aurora|Zelda|Pixie|Laila-Belle" -LegendPosition b -Colors "93B7BE|554348|D4F5F5|747578" -Rounded 14
Save-ChartImage -Path C:\out\age3.png -DataPoints "5|7|12|13" -Chart bvs -Title "Age Chart" -Size 400x600 -Labels "Aurora|Zelda|Pixie|Laila-Belle" -LegendPosition b -Colors "93B7BE,554348,D4F5F5,747578" -Rounded 14