Here's a C# wrapper ..

64 views
Skip to first unread message

Sichbo

unread,
Dec 11, 2007, 12:12:23 PM12/11/07
to Google Chart API
Threw this together last night while evaluating google's chart api -
go nuts.

/*
* Author: Sichbo Interactive (www.sichbo.ca)
* License: Free use for all, even commercial, no warranties
* herein.
*
* Notes: Largely untested but someone else might find it
* useful. You may have to tweak it if you try any
* of the fancier graph types.
*/
using System;
using System.Collections.Generic;
using System.Text;

/// <summary>
/// Describes a chart
/// </summary>
public class GoogleChart
{
private int _Width = 200;
/// <summary>
/// Chart width. Default is 200.
/// </summary>
public int Width
{
get { return _Width; }
set {
if (value > 1000)
throw new ArgumentException("Max Width/Height allowed
is 1,000px");
if (value * _Height > 300000)
throw new ArgumentException("Max Area allowed is
300,000px");
_Width = value; }
}

private int _Height = 90;
/// <summary>
/// Chart height. Default is 90.
/// </summary>
public int Height
{
get { return _Height; }
set {
if (value > 1000)
throw new ArgumentException("Max Width/Height allowed
is 1,000px");
if (value * _Width>300000)
throw new ArgumentException("Max Area allowed is
300,000px");
_Height = value; }
}

private string _Title;
/// <summary>
/// chtt
/// Specify a space with a plus sign (+). Use a pipe character (|)
to force a line break.
/// Optionally, you can also set the title's color and size with
&lt;color&gt;,&lt;fontsize&gt;
/// The chart is clipped (only partially visible) if the size
specified (with chs) is too small.
/// </summary>
public string Title
{
get { return _Title; }
set { _Title = value; }
}

private string _TitleColour;
/// <summary>
/// eg CC0000 or with opacity FFCC0000
/// </summary>
public string TitleColour
{
get { return _TitleColour; }
set { _TitleColour = value; }
}

private int _TitleSizePx;
/// <summary>
/// Font size in pixels, default is 12
/// </summary>
public int TitleSizePx
{
get { return _TitleSizePx; }
set { _TitleSizePx = value; }
}


private GoogleChartType _Type;
/// <summary>
/// The type of Chart you want
/// </summary>
public GoogleChartType Type
{
get { return _Type; }
set { _Type = value; }
}

private int _BarSize;
/// <summary>
/// Bar chart has different widths by default. When specified,
this
/// will make them even.
/// </summary>
/// <remarks>
/// chbh=10
/// </remarks>
public int BarSize
{
get { return _BarSize; }
set { _BarSize = value; }
}


private List<string> _LegendLabels = new List<string>();
/// <summary>
/// Use chdl together with line colors as described in section
Colors.
/// </summary>
/// <remarks>
/// chdl=first data set label|n data set label
/// </remarks>
public List<string> LegendLabels
{
get { return _LegendLabels; }
set { _LegendLabels = value; }
}

private List<string> _PieLabels = new List<string>();
/// <summary>
/// Specify a missing value with two consecutive pipe characters
(||).
/// </summary>
/// <remarks>
/// chl=label 1 value|...label n value;
/// </remarks>
public List<string> PieLabels
{
get { return _PieLabels; }
set { _PieLabels = value; }
}

private List<GoogleChartAxis> _Axes = new List<GoogleChartAxis>();
/// <summary>
/// Specifies the Axes to be used on the graph
/// </summary>
/// <remarks>
/// chxt=x,y,r,x
/// chxl=
/// 0:|Jan|July|Jan|July|Jan|
/// 1:|0|100|
/// 2:|A|B|C|
/// 3:|2005|2006|2007
/// chxp=1,10,35,75|2,0,1,2,4
/// chxr=0,100,500|1,0,200|2,1000,0
/// </remarks>
public List<GoogleChartAxis> Axes
{
get { return _Axes; }
set { _Axes = value; }
}

private GoogleChartFill _BgFill;
/// <summary>
/// Specifies the fill for the Background
/// </summary>
public GoogleChartFill BgFill
{
get { return _BgFill; }
set { _BgFill = value; }
}

private GoogleChartFill _ChartFill;
/// <summary>
/// Specifies the Fill for the Chart area
/// </summary>
public GoogleChartFill ChartFill
{
get { return _ChartFill; }
set { _ChartFill = value; }
}

private List<GoogleChartData> _Data = new List<GoogleChartData>();
/// <summary>
/// Defines Data Sets within the graph
/// </summary>
public List<GoogleChartData> Data
{
get { return _Data; }
set { _Data = value; }
}

/// <summary>
/// Gets the Google Chart API URL for the graph
/// </summary>
public string Url
{
get { return GetUrl(); }
}

/// <summary>
/// Scale all of the values between 1 - 100
/// </summary>
void CalculatePercents()
{
for (int i = 0; i < _Data.Count; i++)
{
GoogleChartData d = _Data[i];
d._Percentiles = new double[d.Values.Count];
double total = 0;

for (int x = 0; x < d.Values.Count; x++)
total += d.Values[x];

for (int x = 0; x < d.Values.Count; x++)
d._Percentiles[x] = d.Values[x] / total * 100.0;
}
}


public string Render(string cssStyle)
{
StringBuilder s = new StringBuilder();
s.Append("<img width=\"" + _Width + "\" height=\"" + _Height +
"\" style=\"" + cssStyle + "\" alt=\"" + HtmlEncode(_Title) + "\" src=
\"" + Url + "\" />");
return s.ToString();
}

/// <summary>
/// Google Chart API meat & potatoes. Mmmm meat.
/// </summary>
public string GetUrl()
{
CalculatePercents();
if (_Width == 0)
throw new ArgumentException("Chart Width cannot be zero");
if (_Height == 0)
throw new ArgumentException("Chart Height cannot be
zero");
StringBuilder s = new StringBuilder();
s.Append("http://chart.apis.google.com/chart?");
s.Append("chs=" + _Width + "x" + _Height);
s.Append("&cht=");
switch (_Type)
{
case GoogleChartType.BarGroupedH:
s.Append("bhg");
break;
case GoogleChartType.BarGroupedV:
s.Append("bvg");
break;
case GoogleChartType.BarStackedH:
s.Append("bhs");
break;
case GoogleChartType.BarStackedV:
s.Append("bvs");
break;
case GoogleChartType.LineX:
s.Append("lc");
break;
case GoogleChartType.LineXY:
s.Append("lxy");
break;
case GoogleChartType.Pie2D:
s.Append("p");
break;
case GoogleChartType.Pie3D:
s.Append("p3");
break;
case GoogleChartType.Scatter:
s.Append("s");
break;
case GoogleChartType.Venn:
s.Append("v");
break;
}
s.Append("&chd=t:");

bool hasColours = false;
bool hasShapes = false;
for (int i = 0; i < _Data.Count; i++)
{
if (!String.IsNullOrEmpty(_Data[i].ColourHtml))
hasColours = true;

if (_Data[i].Markers != null && _Data[i].Markers.Count >
0)
hasShapes = true;

if (i > 0)
s.Append("|");
GoogleChartData d = _Data[i];
for (int x = 0; x < d._Percentiles.Length; x++)
{
if (x > 0)
s.Append(",");
s.Append(d._Percentiles[x].ToString("0.0"));
}
}
if (hasColours)
{
s.Append("&chco=");
for (int i = 0; i < _Data.Count; i++)
{
if (i > 0)
s.Append(",");
s.Append(_Data[i].ColourHtml != null ?
_Data[i].ColourHtml.Replace("#", "").Trim() : "");
}
}

if (_BgFill != null && _BgFill.Type!=
GoogleChartFillType.None)
{
s.Append("&chf=bg,");
RenderFill(s, _BgFill);
}

if (_ChartFill != null && _ChartFill.Type !=
GoogleChartFillType.None)
{
s.Append("&chf=c,");
RenderFill(s, _ChartFill);
}
s.Append("&chtt=");
s.Append(UrlEncode(_Title));
s.Append("&chts=");
s.Append(_TitleColour!=null?
_TitleColour.Replace("#","").Trim():"000000");
s.Append(",");
s.Append(_TitleSizePx > 0 ? _TitleSizePx : 12);

if (_Type == GoogleChartType.Pie3D
|| _Type == GoogleChartType.Pie2D)
{
if (_Data.Count > 0 && _Data[0].PieLabels.Count>0)
{
if (_Data[0].PieLabels.Count != _Data[0].Values.Count)
throw new ArgumentException("The number of Pie
Labels (" + _Data[0].PieLabels.Count + ") must match the number of
Values (" + _Data[0].Values.Count + ")");
s.Append("&chl=");
for (int i = 0; i < _Data[0].PieLabels.Count; i++)
{
if (i > 0)
s.Append("|");
s.Append(UrlEncode(_Data[0].PieLabels[i]));
}

}
}
else
{
s.Append("&chdl=");
for (int i = 0; i < _Data.Count; i++)
{
if (i > 0)
s.Append("|");
s.Append(UrlEncode(_Data[i].Label));
}
}


if (_Axes.Count > 0)
{
// Types
s.Append("&chxt=");
for (int i = 0; i < _Axes.Count; i++)
{
if (i > 0)
s.Append(",");
s.Append(_Axes[i].Type.ToString().ToLower());
}
// Labels
s.Append("&chxl=");
for (int i = 0; i < _Axes.Count; i++)
{
if (i > 0)
s.Append("|");
if (_Axes[i].Labels == null)
continue;
s.Append(i + ":");
for (int x = 0; x < _Axes[i].Labels.Count;x++)
{
s.Append("|");
s.Append(UrlEncode(_Axes[i].Labels[x]!=null?
_Axes[i].Labels[x].Replace(",",""):""));
}
}
// Positions
s.Append("&chxp=");
for (int i = 0; i < _Axes.Count; i++)
{
if (i > 0)
s.Append("|");
if (_Axes[i].Positions == null)
continue;
s.Append(i + ":");
for (int x = 0; x < _Axes[i].Positions.Count; x++)
{
if (x > 0)
s.Append(",");
s.Append(_Axes[i].Positions[x]);
}
}
// Ranges
s.Append("&chxr=");
for (int i = 0; i < _Axes.Count; i++)
{
if (i > 0)
s.Append("|");
if (_Axes[i].RangeFrom == 0 && _Axes[i].RangeTo==0)
continue;

s.Append(i + ":");
s.Append(_Axes[i].RangeFrom);
s.Append(",");
s.Append(_Axes[i].RangeTo);
}
// Label Styles
s.Append("&chxs=");
for (int i = 0; i < _Axes.Count; i++)
{
if (i > 0)
s.Append("|");
s.Append(i + ":");
s.Append(_Axes[i].ColourHtml!=null?
_Axes[i].ColourHtml.Replace("#","").Trim():"000000");
s.Append(",");
s.Append(_Axes[i].FontSizePx > 0 ?
_Axes[i].FontSizePx : 12);
s.Append(",");
s.Append((int)_Axes[i].Align);

}
}

if (hasShapes)
{
s.Append("&chm=");
for (int i = 0; i < _Data.Count; i++)
{
s.Append("|");
if (_Data[i].Markers == null)
continue;
for (int x = 0; x < _Data.Count; x++)
{
GoogleChartMarker m = _Data[i].Markers[x];
switch (m.Type)
{
case GoogleChartMarkerType.Arrow:
s.Append("a");
break;
case GoogleChartMarkerType.Circle:
s.Append("o");
break;
case GoogleChartMarkerType.Cross:
s.Append("c");
break;
case GoogleChartMarkerType.Diamond:
s.Append("d");
break;
case GoogleChartMarkerType.LineH:
s.Append("h");
break;
case GoogleChartMarkerType.LineVB:
s.Append("v");
break;
case GoogleChartMarkerType.LineVT:
s.Append("V");
break;
case GoogleChartMarkerType.Square:
s.Append("s");
break;
case GoogleChartMarkerType.X:
s.Append("x");
break;
}
s.Append(",");
s.Append(m.ColourHtml!=null?
m.ColourHtml.Replace("#","").Trim():"000000");
s.Append(",");
s.Append(i);
s.Append(",");
s.Append(m.ValueIndex);
s.Append(",");
s.Append(m.SizePx);
}
}
}

return s.ToString();
}

static void RenderFill(StringBuilder s, GoogleChartFill _BgFill)
{
switch (_BgFill.Type)
{
case GoogleChartFillType.Linear:
s.Append("lg,");
s.Append(_BgFill.Angle);
if (_BgFill.GradOffsets == null ||
_BgFill.GradOffsets.Length == 0)
throw new ArgumentException("No linear background
offsets were specified.");
if (_BgFill.Colours == null || _BgFill.Colours.Length
== 0)
throw new ArgumentException("No linear background
colours were specified.");
if (_BgFill.Colours.Length !=
_BgFill.GradOffsets.Length)
throw new ArgumentException("The number of colours
and gradient offsets do not match. Each colour must have a
corresponding gradient offset.");
for (int i = 0; i < _BgFill.GradOffsets.Length; i++)
{
s.Append(",");
s.Append(_BgFill.Colours[i]);
s.Append(",");
s.Append(((double)_BgFill.GradOffsets[i]/
100.0).ToString("0.0"));
}
break;
case GoogleChartFillType.Solid:
s.Append("s,");
if (_BgFill.Colours == null || _BgFill.Colours.Length
== 0)
throw new ArgumentException("No solid background
fill colour was specified.");
s.Append(_BgFill.Colours[0]);
break;
case GoogleChartFillType.Stripped:
s.Append("l,");
s.Append(_BgFill.Angle);
if (_BgFill.StripeWidths == null ||
_BgFill.StripeWidths.Length == 0)
throw new ArgumentException("No stripe widths were
specified.");
if (_BgFill.Colours == null || _BgFill.Colours.Length
== 0)
throw new ArgumentException("No stripe background
colours were specified.");
if (_BgFill.Colours.Length !=
_BgFill.StripeWidths.Length)
throw new ArgumentException("The number of colours
and stripe widths do not match. Each colour must have a corresponding
stripe width.");
for (int i = 0; i < _BgFill.GradOffsets.Length; i++)
{
s.Append(",");

s.Append(_BgFill.Colours[i]);
s.Append(",");
s.Append(((double)_BgFill.StripeWidths[i] /
100.0).ToString("0.0"));
}
break;
}
}

/// <summary>
/// Encodes string in HEX and handles null gracefully
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
static string UrlEncode(string text)
{
if (text == null)
return "";

StringBuilder s = new StringBuilder();
for (int i = 0; i < text.Length; i++)
{
if (!Char.IsLetterOrDigit(text[i]))
s.Append("%" + ((int)text[i]).ToString("X"));
else
s.Append(text[i]);
}
return s.ToString();
}
/// <summary>
/// Html encodes and handles null gracefully
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
static string HtmlEncode(string text)
{
if (text == null)
return "";
return System.Web.HttpUtility.HtmlEncode(text);
}
}

[Flags]
public enum GoogleAxisType
{
X, T, Y, R
}

public enum GoogleAxisAlignment : int
{
Left = -1,
Centre = 0,
Right = 1,
}

public enum GoogleChartType
{
/// <summary>
/// </summary>
/// <value>
/// lc
/// </value>
LineX,
/// <summary>
/// Provide a pair of data sets for each line you wish to draw,
the first data set of each pair specifies the x-axis coordinates, the
second the y-axis coordinates.
/// Provide a single undefined value to evenly space the data
points along the x-axis.
/// </summary>
/// <value>lxy</value>
LineXY,
/// <summary>
/// </summary>
/// <value>bhs</value>
BarStackedH,
/// <remarks>
/// </remarks>
/// <value>bvs</value>
BarStackedV,
/// <remarks>
/// </remarks>
/// <value>bhg</value>
BarGroupedH,
/// <remarks>
/// </remarks>
/// <value>bvg</value>
BarGroupedV,
/// <remarks>
/// </remarks>
/// <value>p</value>
Pie2D,
/// <summary>
/// 3D Pie
/// </summary>
/// <value>
/// p3
/// </value>
Pie3D,
/// <summary>
/// Supply two data sets, the first data set specifies x
coordinates, the second set specifies y coordinates.
/// </summary>
/// <value>
/// s
/// </value>
Scatter,
/// <summary>
/// Supply one data set where:
/// * the first three values specify the relative sizes of three
circles, A, B, and C
/// * the fourth value specifies the area of A intersecting B
/// * the fifth value specifies the area of B intersecting C
/// * the sixth value specifies the area of C intersecting A
/// * the seventh value specifies the area of A intersecting B
intersecting C
/// </summary>
/// <value>
/// v
/// </value>
Venn
}

public class GoogleChartAxis
{
public GoogleChartAxis() { }
public GoogleChartAxis(GoogleAxisType type)
{
_Type = type;
}
public GoogleChartAxis(GoogleAxisType type, string[] labels):
this(type)
{
if (labels != null && labels.Length > 0)
_Labels.AddRange(labels);
}
private GoogleAxisType _Type;

public GoogleAxisType Type
{
get { return _Type; }
set { _Type = value; }
}

private List<string> _Labels = new List<string>();

public List<string> Labels
{
get { return _Labels; }
set { _Labels = value; }
}

private string _ColourHtml;

public string ColourHtml
{
get { return _ColourHtml; }
set { _ColourHtml = value; }
}

private List<int> _Positions = new List<int>();
/// <summary>
/// Specify locations between 1 - 100
/// </summary>
public List<int> Positions
{
get { return _Positions; }
set { _Positions = value; }
}

private double _RangeFrom;

public double RangeFrom
{
get { return _RangeFrom; }
set { _RangeFrom = value; }
}

private double _RangeTo;

public double RangeTo
{
get { return _RangeTo; }
set { _RangeTo = value; }
}

private GoogleAxisAlignment _Align;

public GoogleAxisAlignment Align
{
get { return _Align; }
set { _Align = value; }
}

private string _HtmlColour;

public string HtmlColour
{
get { return _HtmlColour; }
set { _HtmlColour = value; }
}

private int _FontSizePx;

public int FontSizePx
{
get { return _FontSizePx; }
set { _FontSizePx = value; }
}

}

public enum GoogleChartMarkerType
{
/// <summary>
/// a
/// </summary>
Arrow,
/// <summary>
/// c
/// </summary>
Cross,
/// <summary>
/// d
/// </summary>
Diamond,
/// <summary>
/// o
/// </summary>
Circle,
/// <summary>
/// s
/// </summary>
Square,
/// <summary>
/// v - represents a vertical line from the x-axis to the data
point.
/// </summary>
LineVB,
/// <summary>
/// V - represents a vertical line to the top of the chart.
/// </summary>
LineVT,
/// <summary>
/// h
/// </summary>
LineH,
/// <summary>
/// x
/// </summary>
X
}

/// <summary>
/// Describes a Marker
/// </summary>
public class GoogleChartMarker
{
private int _Index;

public int ValueIndex
{
get { return _Index; }
set { _Index = value; }
}

private string _ColourHtml;

public string ColourHtml
{
get { return _ColourHtml; }
set { _ColourHtml = value; }
}

private GoogleChartMarkerType _type;

public GoogleChartMarkerType Type
{
get { return _type; }
set { _type = value; }
}

private int _SizePx;

public int SizePx
{
get { return _SizePx; }
set { _SizePx = value; }
}

}

public class GoogleChartData
{
private List<GoogleChartMarker> _Markers;
/// <remarks>
/// chm=marker type,color,data set index,data point,size|
/// </remarks>
public List<GoogleChartMarker> Markers
{
get { return _Markers; }
set { _Markers = value; }
}

private List<double> _Values = new List<double>();

public List<double> Values
{
get { return _Values; }
set { _Values = value; }
}

private List<string> _PieLabels = new List<string>();

public List<string> PieLabels
{
get { return _PieLabels; }
set { _PieLabels = value; }
}


private string _ColourHtml;
/// <remarks>
/// chco
/// </remarks>
public string ColourHtml
{
get { return _ColourHtml; }
set { _ColourHtml = value; }
}

private string _Label;
/// <remarks>
/// chdl=first data set label|n data set label
/// </remarks>
public string Label
{
get { return _Label; }
set { _Label = value; }
}

internal double[] _Percentiles;
}


public enum GoogleChartFillType
{
None,
/// <remarks>
/// chf=bg or c,s,#######
/// </remarks>
Solid,
/// <remarks>
/// chf=bg or c,lg,angle,color 1,offset 1,color n,offset n
/// </remarks>
Linear,
/// <summary>
/// chf=bg or c,l,angle,color 1,width 1,color n,width n
/// </remarks>
Stripped,
}


public class GoogleChartFill
{
public GoogleChartFill()
{ }

public GoogleChartFill(string colour)
{
_Type = GoogleChartFillType.Solid;
_Colours = new string[] { colour };
}

public GoogleChartFill(double angle, string[] colours, int[]
offsets)
{
_Type = GoogleChartFillType.Linear;
_Angle = angle;
_Colours = colours;
_GradOffsets = offsets;
}

public GoogleChartFill(bool vert, string[] colours, int[]
stripeWidths)
{
_Type = GoogleChartFillType.Stripped;
_Angle = vert ? 90 : 0;
_Colours = colours;
_StripeWidths = stripeWidths;
}

private GoogleChartFillType _Type;

public GoogleChartFillType Type
{
get { return _Type; }
set { _Type = value; }
}

private double _Angle;
/// <summary>
/// Gradient or Stripe angle
/// </summary>
public double Angle
{
get { return _Angle; }
set { _Angle = value; }
}

private string[] _Colours;
/// <summary>
/// eg CC0000 or with opacity FFCC0000
/// </summary>
public string[] Colours
{
get { return _Colours; }
set { _Colours = value; }
}

private int[] _GradOffsets;
/// <summary>
/// Specify as a percent (1 - 100)
/// </summary>
public int[] GradOffsets
{
get { return _GradOffsets; }
set { _GradOffsets = value; }
}

private int[] _StripeWidths;
/// <summary>
/// Specify as a percent (1 - 100)
/// </summary>
public int[] StripeWidths
{
get { return _StripeWidths; }
set { _StripeWidths = value; }
}
}

Sichbo

unread,
Dec 11, 2007, 12:15:09 PM12/11/07
to Google Chart API
Silly me, should have checked before writing and posting this - some
clever people have already written one;
http://groups.google.com/group/google-chart-api/browse_thread/thread/5bb0a859c13b7dcd

Reply all
Reply to author
Forward
0 new messages