http://chart.apis.google.com/chart?cht=bhg&chs=180x120&chtt=Google+Chart&chd=t:15,20,30,10,80&chxt=y,x&chxr=0,1,5|1,0,100&chbh=10

引言

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的中文文档中的命名,这样大家在使用时能够更方便地对照文档。

类库解析

此类库中有两大基类:参数、图表

图表类就是各种类型的图表的基类,如饼图、三维饼图、折线图、条形图等。

参数即为图表的参数,如尺寸、数据、颜色、标签、标题等均继承于此类。

因为各种图表所能够支持的参数类型不尽相同,所以除了参数本身的封装类型之外,还定义了参数的支持接口,图表只有继承了这些支持接口,才能通过扩展方法“添加参数()”添加相应类型的参数:

image

扩展方法定义:

 image

上图中的“唯一参数标识”属性是用于避免参数重复添加的属性。

此外,参数还定义了一种依赖机制:

image

在生成代码时,如果仅有此参数,而没有其所需的某种依赖参数,则报告异常。

如:图表图例参数需依赖于图表颜色参数

生成图表范例

http://chart.apis.google.com/chart?chs=320x240&cht=p&chd=t:32.5,82.5,31,155&chco=FFD622,9B722C,9B3686%3E%A3%AC%CA%B5%BC%CA%CE%AA:%20%3Chttp://chart.apis.google.com/chart?cht=p&chd=t:32.5,82.5,31,155&chds=-200,200&chco=FFD622FF,9B722CFF,9B3686FF&chs=360x300&chl=AA|BB|CC|DD&chtt=Test|Test&chf=a,s,23247B82|bg,ls,40,FFFFFFFF,0.2,E1E17DFF,0.2,C8C864FF,0.1,B9B932FF,0.2

功能点:饼图 四色 标签 标题 条纹背景 整体不透明度控制

http://chart.apis.google.com/chart?cht=lc&chs=500x350&chd=t:35,11,321.3,55|15,111,4,55|315,211,-74,-55&chds=-321,500&chco=01FFFFFF,FF01FFFF,FFFF01FF&chdl=Freya|Isis|Athena&chdlp=b&chtt=Line+Test&chts=FF32C8,14&chxt=x,r,x,y&chxr=0,50,300|1,500,-1300|3,0,100&chxp=3,10,20,100&chxl=2:|test|test2|test3|3:|test|test2|test3&chxs=2,FF0000,16,-1|3,FF0000,12,-1&chf=c,lg,90,FFFFFFFF,1,E1E17DFF,0.5001,C8C864FF,0.5,B9B932FF,0

功能点:折线图 三色 图例 渐变背景 标题 轴标签定制

http://chart.apis.google.com/chart?cht=bvg&chs=500x350&chd=t:35,11,321.3,55|15,111,4,55|315,211,-74,-55&chds=-100,355&chco=DDFFFFFF,FFDDFFFF,FFFFDDFF&chdl=Freya|Isis|Athena&chdlp=b&chtt=Line+Test&chts=FF32C8,14&chxt=x,r,x,y&chxr=0,50,300|1,-74,322|3,0,100&chxp=3,10,80,100&chxl=2:|A|B|C|3:|X|Y|Z&chxs=2,FF0000,16,-1|3,FF0000,12,-1&chf=c,lg,90,FFFFFFFF,1,E1E1DCFF,0.5001,FFEBDEFF,0.5,E9D7D4FF,0

功能点:条形图 三色 纵向分布 图例 渐变背景 标题 轴标签定制

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 

在此先行拜谢^^

 

  

posted on 2009-07-15 01:53  斯克迪亚  阅读(8988)  评论(74编辑  收藏  举报