引言
Google图表API是一个免费的、非常强劲的图表生成工具,您可以仅凭一个Url地址,轻松生成一张具有专业水准的图表。
更为可贵的是,图表图形的计算和渲染完全由Google服务器处理,生成的图片也从Google服务器下载,你的服务器就可以大幅节约运算及网络流量资源。
故此,将Google图表API作为网站的图表解决方案,是一个绝佳的选择。
这是Google官方提供的一个范例:
它的Url为:
http://chart.apis.google.com/chart?cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World
你可以尝试修改Url中的各个参数,图表对应的图像就会发生变化。
这些参数在Google官方说明文档中都有详尽的说明,但即便如此,它们看起来也还是像天书一般,并且非常杂乱。
上面那个还仅仅是一个简单之至的图表,来看看文章末尾提供的范例所对应的Url:
http://chart.apis.google.com/chart?cht=lc&chs=1000x300&chtt=30天内日志记录量统计图&chco=BD6CE9FF,1865BBFF,0AA413FF,FF9E1FFF,FF565EFF&chm=B,BD6CE932,0,0,0|B,1865BB32,1,0,0|B,0AA41332,2,0,0|B,FF9E1F32,3,0,0|B,FF565E32,4,0,0&chdl=管理员|异常|系统|用户&chdlp=b&chd=t:0,12,0,180,103,0,71,0,0,2,0,0,0,0,0,47,0,0,0,127,0,11,0,0,0,0,0,0,0,118,41|0,0,111,22,2,0,3,0,27,2,23,19,9,18,30,18,0,6,16,3,0,8,0,0,4,0,1,0,0,21,3|0,4,77,80,26,0,7,0,50,18,49,42,33,74,77,56,0,32,83,38,0,18,0,0,46,8,24,0,4,97,48|0,1,4,9,1,0,3,0,4,1,1,4,2,3,12,9,0,7,7,2,0,1,0,0,5,8,23,0,1,3,2&chds=0,180&chxt=x,r,y&chxr=1,0,180|2,0,180&chxp=1,22.9677419354839,11.1612903225806,31.9677419354839,3.64516129032258&chxl=0:|6.14|6.15|6.16|6.17|6.18|6.19|6.20|6.21|6.22|6.23|6.24|6.25|6.26|6.27|6.28|6.29|6.30|7.1|7.2|7.3|7.4|7.5|7.6|7.7|7.8|7.9|7.10|7.11|7.12|7.13|7.14|1:|管理员均值|异常均值|系统均值|用户均值&chf=bg,s,FAFAFAFF|c,lg,90,EDFDFDFF,0,EDFDFD00,1
啊哈,是不是已经喷血了?
显然这样的图表不适合人工创建,而即使通过程序来创建,也非常麻烦:数据统计的逻辑本来就有够复杂了,再加上频繁地查看文档、测试……
有什么办法能让图表的创建变得轻松、简单吗?
高度封装的图表创建类
为实现这一目标,我决定将图表的创建操作封装起来,有了封装后的类库,就可以这样轻松的创建一个图表Url:
创建一个图表对象 > 为之添加各种所需参数 > 生成代码
以上面那个Google的三维饼图范例为例,我们通过下面的代码生成一个同样的图表:
[TestMethod()]
public void 生成代码Test2()
{
var 图表 = new 三维饼图();
图表.添加参数(new 图表尺寸 { 高度 = 100, 宽度 = 250 });
var label = new 饼图和指数标签();
label.标签列表.Add("Hello");
label.标签列表.Add("World");
图表.添加参数(label);
var data = new 文本编码数据();
data.数据列表.Add(new System.Collections.Generic.List<double>());
data.数据列表[0].Add(60);
data.数据列表[0].Add(40);
图表.添加参数(data);
var expected = "http://chart.apis.google.com/chart?cht=p3&chs=250x100&chl=Hello|World&chd=t:60,40";
var actual = 图表.生成代码();
Assert.AreEqual(expected, actual);
}
感觉如何?这样基本上就把整个创建流程语义化了,创建图表的时候再也不用频繁查看文档了吧!
此类库使用中文是出于三个原因:
其一就是我个人的写程序习惯,我觉得中文最便于自己理解,中文和英文放在一起非常显眼,并且中文在VisualStudio的智能感知列表中始终处于最下方,这样自定义的东西一下就能找到;
其二就是中文毕竟是我们的母语,大多数人一眼就能理解,省去翻译的麻烦;
其三就是其中的大部分命名直接引用或参考了Google的中文文档中的命名,这样大家在使用时能够更方便地对照文档。
类库解析
此类库中有两大基类:参数、图表
图表类就是各种类型的图表的基类,如饼图、三维饼图、折线图、条形图等。
参数即为图表的参数,如尺寸、数据、颜色、标签、标题等均继承于此类。
因为各种图表所能够支持的参数类型不尽相同,所以除了参数本身的封装类型之外,还定义了参数的支持接口,图表只有继承了这些支持接口,才能通过扩展方法“添加参数()”添加相应类型的参数:
扩展方法定义:
上图中的“唯一参数标识”属性是用于避免参数重复添加的属性。
此外,参数还定义了一种依赖机制:
在生成代码时,如果仅有此参数,而没有其所需的某种依赖参数,则报告异常。
如:图表图例参数需依赖于图表颜色参数
生成图表范例
功能点:饼图 四色 标签 标题 条纹背景 整体不透明度控制
功能点:折线图 三色 图例 渐变背景 标题 轴标签定制
功能点:条形图 三色 纵向分布 图例 渐变背景 标题 轴标签定制
Asp.Net中,结合Entity Framework使用的应用范例
在页面中添加一个Image控件,然后在Page_Load中书写代码实现日志信息统计:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
var 图表 = new 折线图();
图表.类型 = 折线图类型.均布折线图;
图表.添加参数(new 图表尺寸 { 宽度 = 1000, 高度 = 300 });
图表.添加参数(new 图表标题 { 标题 = "30天内日志记录量统计图" });
图表.添加参数(new 实体填充 { 填充类型 = 实体填充类型.整体背景填充, 颜色 = Color.FromArgb(250, 250, 250) });
var xt = new 线性渐变填充();
图表.添加参数(xt);
xt.角度 = 90;
xt.填充类型 = 线性渐变填充类型.图表区域背景填充;
xt.色标列表.Add(new 色标 { 位置 = 0, 颜色 = Color.FromArgb(237, 253, 253) });
xt.色标列表.Add(new 色标 { 位置 = 1, 颜色 = Color.FromArgb(0, 237, 253, 253) });
var color = new 图表颜色();
图表.添加参数(color);
color.颜色列表.AddRange(new Color[] { Color.FromArgb(189, 108, 233), Color.FromArgb(24, 101, 187), Color.FromArgb(10, 164, 19), Color.FromArgb(255, 158, 31), Color.FromArgb(255, 86, 94), });
var colorfill = new 区域填充();
图表.添加参数(colorfill);
colorfill.填充列表.Add(new 数据实体填充 { 颜色 = Color.FromArgb(50, 189, 108, 233), 目标数据组索引 = 0 });
colorfill.填充列表.Add(new 数据实体填充 { 颜色 = Color.FromArgb(50, 24, 101, 187), 目标数据组索引 = 1 });
colorfill.填充列表.Add(new 数据实体填充 { 颜色 = Color.FromArgb(50, 10, 164, 19), 目标数据组索引 = 2 });
colorfill.填充列表.Add(new 数据实体填充 { 颜色 = Color.FromArgb(50, 255, 158, 31), 目标数据组索引 = 3 });
colorfill.填充列表.Add(new 数据实体填充 { 颜色 = Color.FromArgb(50, 255, 86, 94), 目标数据组索引 = 4 });
var tl = new 图表图例();
图表.添加参数(tl);
tl.位置 = 图表图例位置.下;
var td = new 带有数据换算的文本编码数据();
图表.添加参数(td);
td.换算标准.Add(new 范围限定());
td.换算标准[0].最大值 = double.MinValue;
td.换算标准[0].最小值 = double.MaxValue;
var mtg = new 多轴标签();
图表.添加参数(mtg);
var dz = new 轴标签 { 类型 = 轴类型.底部 };
var a = new 轴标签 { 类型 = 轴类型.右侧 };
mtg.轴标签列表.Add(dz);
mtg.轴标签列表.Add(a);
using (var c = new DatabaseEntities())
{
var d = DateTime.Now.AddDays(-30);
var s = c.日志.Select(f => new { f.触发时间, f.所属日志类型.名称 }).Where(f => f.触发时间 >= d).ToList().GroupBy(f => f.名称);
bool first = true;
foreach (var f in s)
{
tl.图例名称列表.Add(f.Key);
var dl = new List<double>();
for (int i = -30; i <= 0; i++)
{
var od = DateTime.Today.AddDays(i);
if (first)
{
dz.轴标签列表.Add(od.Month + "." + od.Day);
}
var cd = f.Count(v => v.触发时间.Date == od);
dl.Add(cd);
td.换算标准[0].最大值 = Math.Max(td.换算标准[0].最大值, cd);
td.换算标准[0].最小值 = Math.Min(td.换算标准[0].最小值, cd);
}
a.轴标签列表.Add(f.Key + "均值");
a.轴标签位置列表.Add(dl.Average());
td.数据列表.Add(dl);
first = false;
}
}
mtg.轴标签列表.Add(new 轴标签 { 类型 = 轴类型.左侧, 轴范围 = new 轴范围 { 起始值 = (int)td.换算标准[0].最小值, 终止值 = (int)td.换算标准[0].最大值 } });
a.轴范围 = new 轴范围 { 起始值 = (int)td.换算标准[0].最小值, 终止值 = (int)td.换算标准[0].最大值 };
Image1.ImageUrl = 图表.生成代码();
}
}
生成的图表:
当前进度
目前还没有将所有图表及参数全部封装,但现有的功能已足以应付多数常规应用。
源代码下载
另外,我也将提供类库的源代码,供大家自行扩展需要的功能:
下载类库源代码:
结语
我的设计还很不成熟,所以难免在此类库中存有一些设计缺陷,极力欢迎各位朋友指导、指正。
还有什么需求或想法,也欢迎在此留言述说。
打个小广告:
我妈最近强迫我找工作-。-,有哪位朋友公司在北京,准备招人的请通过我的博客评估一下我吧,觉得不错的话欢迎联系我:13693536899
在此先行拜谢^^
转载请遵循此协议:署名 - 非商业用途 - 保持一致
并保留此链接:http://skyd.cnblogs.com/