在Web页面中使用图表（chart）表现数据

---摘自互联网

    在 Web 编程中经常需要做的一件事情就是把从数据库中查出的数据（数字）
    使用图表（chart）的形式在页面中表现出来。下面我们简单总结几种常见的做法。

    1. 如果图表的样式只需要柱形图（bar）就可以的话，有一种非常简单，偷懒的
    方法，即使用某些 tag 的 width 属性来表现就可以。举例如下：

    <IMG HEIGHT=5 WIDTH=<%= 数值 %> SRC="小方块.gif">

    用这种思路，要是不嫌难看的话，你干脆用 for 循环控制 * 号的显示个数也未尝
    不可。;-)
    如果想比较美观的话，可以把 CSS 设计的好一些，再和 DHTML 结合。

    这种方法的一个完整的例子见：
    http://www.microsoft.com/workshop/database/datavis/datavis.asp

    2. 一些老兄喜欢直接把图片放在数据库中，那我们看看怎么把它们调出来。
    （如果这些图片正好是图表的话，我就不算离题。;-)）
    IIS 的在线帮助中有这么个例子：
    http://localhost/IIsSamples/SDK/asp/docs/CodeBrws.asp?source=/IIsSamples/SDK/asp
    /Database/Blob_VBScript.asp
    其核心代码：
    <%
          ' 声明回传的是 Gif 文件，不是平常的 HTML
          Response.Buffer = TRUE
          Response.ContentType = "image/gif"
          ' 连数据库
          Set oConn = Server.CreateObject("ADODB.Connection")
          oConn.Open "DSN=LocalServer;UID=sa;PWD=;DATABASE=pubs"
          ' 查出存好的图片
          Set oRs = oConn.Execute("SELECT logo FROM pub_info WHERE pub_id='0736'")
          ' 取值要显得专业些 ;-)
          PicSize = oRs("logo").ActualSize
          Pic = oRs("logo").GetChunk(PicSize)
          ' 再次强调回传的是 gif 图片，view source 是什么都看不到的
          Response.BinaryWrite Pic
          Response.End
    %>

    好，仔细看注释的老兄（我可没这好习惯;-)）会问：这支程序 mypic.asp 在浏览器中最后的效果
    相当于 http://host/foo/mypic.gif，我想要有文字怎么办？
    很简单，写个 web page 中间加上 <img src=mypic.asp> 不就完了。 ;-)

    3. 还有些老兄更甚，这些大侠的机器多半是 8 CPU 的 P III，他们使用 server-side 软件，
    比如 excel，现做一个 chart 图片，然后以 gif 格式传给浏览器。多见于 CGI 高手。;-)
    我们来看一个这样的 cool demo。
    核心代码：
    <%
        Set excel = GetObject("","Excel.Application")
        If Err.Number <> 0 Then
        Response.Write("Could not create Excel document. " + Err.Description+"")
        Err.Clear
        End If
        excel.DisplayAlerts = False
        Set workbooks = excel.Workbooks
        Set wb = workbooks.Add
        Set sheets = wb.Sheets
        Set wsTotal = sheets.Add( ,,,-4167)
        wsTotal.Name = "Total_Expenses"
        Set range = wsTotal.Range("B1")
        range.FormulaR1C1 = "1"
        Set range = wsTotal.Range("C1")
        range.FormulaR1C1 = "2"
        Set range = wsTotal.Range("D1")
        range.FormulaR1C1 = "3"

        wsTotal.Activate
        wsTotal.Select

        Set range = wsTotal.Range("B1:D1")
        excel.Charts.Add
        excel.ActiveChart.ChartType = 51
        excel.ActiveChart.SetSourceData range,2

        excel.ActiveChart.Export "d:\test\exceltest"+".gif","GIF"

        Response.Write "<img src=d:\test\exceltest.gif>"
    %>

    真正的懒人在写这段代码时还利用 excel 的 vba（:-P），绝对代码快枪手，
    可是运行效率----呸！;-)

    4. 好了，该看一看专业运动员的做法了----使用 chart control。
    哪种控件更好大家见仁见智，（比如有些老兄喜欢 Java Applets ;-) 还有些老兄
    喜欢自己用 C/C++ 开发）为简化起见，这里我推荐微软（;-)）的----
    Office 2000 Web Component。;-)

    在前面一文中我介绍过控件与数据结合的几种方式，我们来一一分析
    用 Excel 2000 的 chart control 如何实现。

    A. 逐行赋值法
    Excel 2000 chart control 有两种赋值方法：数组，字符串。
    数组法：
    代码示例：
    ----------------------------------
    <object id=ChartSpace1 classid=CLSID:0002E500-0000-0000-C000-000000000046
    style="width:100%;height:350"></object>

    <script language=vbs>
    Sub Window_OnLoad()
    Dim categories(3), values(3)
        ' 4 个分类
        categories(0) = "White"
        categories(1) = "Black"
        categories(2) = "Asian"
        categories(3) = "Latino"

        ' 准备活动 ;-)
        ChartSpace1.Clear
        ChartSpace1.Charts.Add
        Set c = ChartSpace1.Constants

        ' 添加三个系列的值
        ChartSpace1.Charts(0).SeriesCollection.Add
        ChartSpace1.Charts(0).SeriesCollection.Add
        ChartSpace1.Charts(0).SeriesCollection.Add

        ' 锦上添花 ;-)
        ChartSpace1.Charts(0).SeriesCollection(0).Caption = "Perot"

        ' 设置
        ChartSpace1.Charts(0).SeriesCollection(0).SetData c.chDimCategories,
        c.chDataLiteral, categories

        values(0) = 0.2 ' The White value.
        values(1) = 0.06 ' The Black value.
        values(2) = 0.17 ' The Asian value.
        values(3) = 0.13 ' The Latino value.

        ChartSpace1.Charts(0).SeriesCollection(0).Caption = "Perot"
        ChartSpace1.Charts(0).SeriesCollection(0).SetData c.chDimCategories,
        c.chDataLiteral, categories
        ChartSpace1.Charts(0).SeriesCollection(0).SetData c.chDimValues, c.chDataLiteral, values

        ' Series two contains election data for Clinton.
        ' Update the values array, then set the chart data.
        values(0) = 0.38 ' The White value.
        values(1) = 0.82 ' The Black value.
        values(2) = 0.28 ' The Asian value.
        values(3) = 0.62 ' The Latino value.

        ChartSpace1.Charts(0).SeriesCollection(1).Caption = "Clinton"
        ChartSpace1.Charts(0).SeriesCollection(1).SetData c.chDimCategories,
        c.chDataLiteral, categories
        ChartSpace1.Charts(0).SeriesCollection(1).SetData c.chDimValues,
        c.chDataLiteral, values

        ' Series two contains election data for Bush.
        ' Update the values array, and then set the chart data.
        values(0) = 0.42 ' The White value.
        values(1) = 0.12 ' The Black value.
        values(2) = 0.55 ' The Asian value.
        values(3) = 0.25 ' The Latino value.

        ChartSpace1.Charts(0).SeriesCollection(2).Caption = "Bush"
        ChartSpace1.Charts(0).SeriesCollection(2).SetData c.chDimCategories,
        c.chDataLiteral, categories
        ChartSpace1.Charts(0).SeriesCollection(2).SetData c.chDimValues,
        c.chDataLiteral, values

        ' Make the chart legend visible, format the left value axis as percentage,
        ' and specify that value gridlines are at 10% intervals.
        ChartSpace1.Charts(0).HasLegend = True
        ChartSpace1.Charts(0).Axes(c.chAxisPositionLeft).NumberFormat = "0%"
        ChartSpace1.Charts(0).Axes(c.chAxisPositionLeft).MajorUnit = 0.1
    End Sub
    </script>

    字符串法：
    代码示例：
    --------------------------
    <script language=vbs>
    Sub Window_OnLoad()
        Dim categories, values

        ' 原来的注释很无聊，被我删掉了 ;-)
        ChartSpace1.Clear
        ChartSpace1.Charts.Add
        Set c = ChartSpace1.Constants

        ' 以 tab 为分隔符的字符串拼凑
        categories = "White" & Chr(9) & "Black" & Chr(9) & "Asian" & Chr(9) & "Latino"

        ' Add three series to the chart.
        ChartSpace1.Charts(0).SeriesCollection.Add
        ChartSpace1.Charts(0).SeriesCollection.Add
        ChartSpace1.Charts(0).SeriesCollection.Add

        ' Series one contains election data for Perot.
        ' Set the series caption (the text that appears in the legend).
        ChartSpace1.Charts(0).SeriesCollection(0).Caption = "Perot"

        ' Set the categories for the first series (this collection is zero-based).
        ChartSpace1.Charts(0).SeriesCollection(0).SetData c.chDimCategories,
        c.chDataLiteral, categories

        ' 以 tab 为分隔符的字符串拼凑
        values = "0.2" & Chr(9) & "0.06" & Chr(9) & "0.17" & Chr(9) & "0.13"
        ChartSpace1.Charts(0).SeriesCollection(0).SetData c.chDimValues,
        c.chDataLiteral, values

        ' Series two contains election data for Clinton.
        ' Update the values string, and then set the chart data.
        values = "0.38" & Chr(9) & "0.82" & Chr(9) & "0.28" & Chr(9) & "0.62"
        ChartSpace1.Charts(0).SeriesCollection(1).Caption = "Clinton"
        ChartSpace1.Charts(0).SeriesCollection(1).SetData c.chDimCategories,
        c.chDataLiteral, categories
        ChartSpace1.Charts(0).SeriesCollection(1).SetData c.chDimValues,
        c.chDataLiteral, values

        ' Series two contains election data for Bush.
        ' Update the values string, and then set the chart data.
        values = "0.42" & Chr(9) & "0.12" & Chr(9) & "0.55" & Chr(9) & "0.25"
        ChartSpace1.Charts(0).SeriesCollection(2).Caption = "Bush"
        ChartSpace1.Charts(0).SeriesCollection(2).SetData c.chDimCategories,
        c.chDataLiteral, categories
        ChartSpace1.Charts(0).SeriesCollection(2).SetData c.chDimValues,
        c.chDataLiteral, values

        ' Make the chart legend visible, format the left value axis as percentage,
        ' and specify that value gridlines are at 10% intervals.
        ChartSpace1.Charts(0).HasLegend = True
        ChartSpace1.Charts(0).Axes(c.chAxisPositionLeft).NumberFormat = "0%"
        ChartSpace1.Charts(0).Axes(c.chAxisPositionLeft).MajorUnit = 0.1
    End Sub
    </script>
    -------------------------

    无论是数组法还是字符串法，你都可以在 client-side 逐行插入 <%=value%>。
    对于字符串法，你也可以在 server-side 就拼好一个字串，然后直接传过来。
    ChartSpace1.Charts(0).SeriesCollection(1).SetData c.chDimValues, c.chDataLiteral,
    <% =stringValues%>
    （好象要加引号哦。"<% =stringValues%>"，意思到了就中）

    B. client-side recordset 法
    Excel 2000 chart 这么 cool 的 control 当然支持直接的 recordset 绑定。
    代码示例：
    -------------------------
    <html>
    <body>

    第一步：创建 Chart 和 ADO Connection object
    <object id=ChartSpace1 classid=CLSID:0002E500-0000-0000-C000-000000000046
    style="width:100%;height:480"></object>
    <object id=ADOConnection1 classid=CLSID:00000514-0000-0010-8000-00AA006D2EA4></object>

    <script language=vbs>
    Sub Window_OnLoad()
         Dim rs, Categories, Values

         ' 找个英文 Access 97 里的 nwind.mdb 试试
         Categories = ""
         Values = ""

         ADOConnection1.Open "DRIVER={Microsoft Access Driver (*.mdb)};DBQ=c:\nwind.mdb"
         Set rs = ADOConnection1.Execute("SELECT * FROM [Category Sales for 1995]")
         rs.MoveFirst
         While Not rs.EOF
             Categories = Categories & rs.Fields(0).Value & Chr(9)
             Values = Values & rs.Fields(1).Value & Chr(9)
             rs.MoveNext
         Wend
         rs.Close
         ADOConnection1.Close

         ' 多拼了个 TAB，去了它
         Categories = Left(Categories, Len(Categories) - 1)
         Values = Left(Values, Len(Values) - 1)

         ' 很容易看懂吧
         ChartSpace1.Clear
         ChartSpace1.Charts.Add
         ChartSpace1.Charts(0).SeriesCollection.Add
         ChartSpace1.Charts(0).SeriesCollection(0).Caption = "Sales"
         ChartSpace1.Charts(0).SeriesCollection(0).SetData ChartSpace1.Constants.chDimCategories,

    ChartSpace1.Constants.chDataLiteral, Categories
         ChartSpace1.Charts(0).SeriesCollection(0).SetData ChartSpace1.Constants.chDimValues,

    ChartSpace1.Constants.chDataLiteral, Values

         '-- As a final step, we turn this into a bar chart (instead of a column chart), and
         '-- format the axis as US $.
         ChartSpace1.Charts(0).Type = ChartSpace1.Constants.chChartTypeBarClustered
         ChartSpace1.Charts(0).Axes(ChartSpace1.Constants.chAxisPositionBottom).NumberFormat
         = "$#,##0"
    End Sub
    </script>
    </body>
    </html>
    -----------------------------------
    C. 目前 Excel 2000 的 chart control 还没有用于 VI6 的 Design Time Control 版，恨恨！