はじめに
アプリケーション開発でよく必要となることの1つに、対話式のグラフ作成があります。たとえば、管理者のための売上高/生産量データ管理アプリケーションを開発するとします。このアプリケーションでは、データをSQLデータベースに格納し、ユーザーが新情報を追加したり既存情報を更新したりできるようにします。また、このような受注機能に加えて、データを円グラフ、棒グラフ、散布図(XYグラフ)などで視覚的に表現するための機能も付けてほしいという要求があるかもしれません。
Windowsデスクトップアプリケーションなら、これには何の問題もありません。無数のグラフライブラリやグラフ作成コンポーネントが存在します。しかし、Webアプリケーションでは、状況が異なります。Webアプリケーションにグラフ機能を持たせるには、次のどちらかの方法によらなければなりません。
- クライアントサイドの手法:ActiveXコンポーネントを使用すれば、Webブラウザ内部に「リッチ」なWindowsクライアント機能を実現できます。ただし、クライアントのセットアップが複雑になって、クライアントサイドソフトウェアの配布が必要になり、1クライアント当たりいくらというライセンス費用が発生します。さらに、MS Windows/Internet Explorer以外のクライアントでは機能しません。
- サーバーサイドの手法:Webサーバー上のサーバーサイドコードを使用し、グラフを動的に作成して、それをGIF画像やJPG画像としてWebクライアントにストリーミングします。この手法の利点は、クライアント上に標準的Webブラウザがあれば足りることです。一方、クライアントサイド技術と比べての欠点は、グラフィカル画像の対話性が薄れることです(Webサーバー宛にいちいち新しい要求を出さないと、拡大やスクロールすらできません)。Mapquest.comなどのマップサイトでは、この技術が多用されています。マップ画像がWebサーバーに格納されているわけではなく、ユーザーから要求があるたびに画像がマップデータベースから生成されています。
本稿では、サーバーサイドにグラフ作成を担当させてASP.NET Webページのグラフ機能を実現する方法を説明します。もっと具体的に言うと、MSSQLデータベースに格納されているデータから散布図(XYグラフ)を作成する方法を考えます。
グラフ作成エンジンとしてOWCを選択
ASP.NET Webアプリケーション用にグラフを作成するには、適切な「グラフ作成エンジン」が必要です。ASP.NETにも組み込み式のグラフィックライブラリ(System.Drawing名前空間のGDI+)があり、グラフィックプリミティブを使って簡単な円グラフ、棒グラフ、線グラフを描くことができます。たとえば、ASP.NET Reports Starter Kitには、GDI+を使ってサーバーサイドでグラフを作成するためのコードサンプルが含まれていますし、Scott Mitchellも、『Create Snazzy Web Charts and Graphics On the Fly with ASP.NET』という記事の中で、GDI+を使ってその場で円グラフを作成する方法を説明しています。ただ、こうした記事やサンプルアプリケーションはあるものの、私自身の感じでは、GDI+ライブラリは低水準すぎて、手軽に利用できるグラフ作成エンジンではありません。特に、複雑なグラフを扱おうとすると、その感が強くなります。
ASP.NET用のグラフライブラリもいくつか市販されています。Googleで検索したところ、すぐにいくつか見つかりました。
しかし、こうした製品の多くはきわめて高額であり、その使用感も、世界で最も多く使われているグラフ作成アプリケーションであるMicrosoft Excelに比べると多少「よそよそしさ」があります。MS Excelは強力なグラフ作成エンジンを搭載し、さまざまな種類のグラフと機能をサポートしていて、ユーザーの望むとおりにレイアウトを微調整してくれます。
すべてを考え合わせると、結局、本稿の標題にあるOWC(Office Web Components)に行き着きます。Microsoftによると、OWCは「Officeに似た機能をWebに持ち込むことを狙ったMicrosoft技術」です。これはExcelワークシートをWebページとして保存するときに使用されるクライアントサイド技術であり、このおかげで、対話式のスプレッドシートモデルやグラフをWebページ上に公表することが容易になりました。しかし、これはサーバーサイドグラフ作成エンジンとしても大きな威力を発揮します。
OWCのライセンス問題
OWCの旧バージョンを使ったことがある方なら、Microsoftのライセンス問題に混乱した経験がおありでしょう。Microsoftは、当初、「サーバーにも個々のクライアントPCにもOfficeライセンスが必要」という立場をとっていました。そのため、OWC使用の場は、クライアントライセンスを確認できるイントラネットに限られていたのが実状です。その後、Microsoftの態度が軟化し、現在では、サーバーには依然Officeライセンスが必要であるものの、クライアントには不要となっています(ただし、サーバーサイド同様、グラフを「対話式に使用しない」という条件が付きます)。実際には、サーバーサイドにもMS Excel 2002かFrontPage 2002があれば十分で、完全なMS Officeライセンスは必要ありません。つまり、サーバーサイドグラフ作成エンジンとしても、OWCはたいへん安上がりだと言えます。
「えっ、サーバーにMS Officeをインストールするの?」とお思いかもしれませんが、そうではありません。OWCは、ライセンス上はMS Officeの一部ですが、技術的には別個の製品です。したがって、WebサーバーにはMS Officeそのものでなく、OWCパッケージをインストールすることになります。
OWCはMS Office 2000で初登場し、OWCバージョン9と呼ばれていました。MS Office XPではOWCのプログラミングモデルが改変され、OWC XPとなりました。これはOWC10とも呼ばれますが、OWC9(使い方に古典的ASPの名残があります)とは完全互換ではありません。ASP.NET環境にはOWC10が必須なので、ASP.NETサーバーにはOWC10パッケージをインストールしてください。
次の疑問は、当然、「OWC10パッケージはどこにあるのか」でしょう。驚くべきことに、Microsoftから無料でダウンロードできます。もちろん、合法的に使用するためには、Webサーバーに何らかのMS Office 2002ライセンスが必要だということをお忘れなく。
OWCの働きの仕組み
OWCはCOM(ActiveX)コントロールの集まりであり、表計算、グラフ作成、PivotTableなど、さまざまな機能を含んでいます。ほとんどの場合、COMコントロールをPCクライアントにインストールし、クライアントサイド技術として使います。サーバーサイド技術として使うときの狙いは、主としてグラフ作成機能です。プログラム的に言うと、ASP.NET Webサーバーのメモリ内にグラフを作成し、そのグラフをGIF画像ファイルとしてWebクライアントにストリーミングすることになります。クライアント側から見えるのは通常の画像ファイルにすぎませんが、舞台裏では、サーバー上のASP.NETがHTTP要求に応じて画像を動的に生成しています。したがって、Webクライアント側には、GIF画像ファイルを表示できる機能以外、特に何も必要とされません。NetscapeでもOperaでも大丈夫です。
さて、こんな優秀なMicrosoft技術なのに、ASP.NET開発者コミュニティに広く採用されていないのはなぜでしょうか。Microsoftが架空のマーケティング問題やライセンス問題で開発者を震え上がらせたのも一因でしょうし、Microsoftがこの製品に未熟な部分を感じていたという可能性もあります。その関連で言えば、OWC11を搭載して登場するOffice 2003では、プログラミングモデルが多少変更されるかもしれません。しかし、OWC技術が広く行き渡ると、MS Officeの売上高が減少する……とMicrosoftが危惧したという理由も十分にありそうです。
また、現状ではプログラミングサンプルがほとんどありません。Microsoftのサポート技術情報には、クライアントサイドのサンプルと「古典的な」ASPサーバーサイドサンプルがいくつかありますが、ASP.NET環境でOWC10を使ったサーバーサイドサンプルは1つもありません(原稿執筆当時)。他のASP.NET開発者サイトも同様ですし、OWCニュースグループのmicrosoft.public.office.developer.web.componentsでも、主としてクライアントサイドのOWC使用が話し合われています。要するに、自力でやる部分が大きいということです。その意味で、本稿がこの技術を広める一助となれば幸いです。
WebサーバーへOWC10をインストール
ASP.NET WebサーバーでOWCグラフを作成するには、事前にいくつかのソフトウェアをインストールし、設定ファイルを変更しておかなければなりません。
まず、当然ながら、Webサーバー上にASP.NETランタイム環境が存在しなければなりません。具体的には.NET Framework Redistributableですが、他にOWCコントロール設定のためのGACUTILプログラム(.NET Framework SDKの一部)も必要なので、.NET Framework SDKユーティリティ群もインストールしてください。Webサーバー上でコマンドプロンプトを起動し、.NET Frameworkユーティリティ群にPATHを通します。.NET Framework 1.1 RedistributableとSDKが標準のディレクトリにインストールされているなら、PATHには次の各ディレクトリを指定します。
...;C:WINDOWSMicrosoft.NETFrameworkv1.1.4322;C:Program Files
Microsoft.NETSDKv1.1Bin;...
私としては、Webサーバー上のPATH環境変数を永続的に変更することをお勧めしますが、次のコードを含む「NETPATH.BAT」ファイルを作成し、これをコマンドプロンプトから実行して、PATHを一時的に変更することもできます。
PATH=C:WINDOWSMicrosoft.NETFrameworkv1.1.4322;C:Program Files
Microsoft.NETSDKv1.1Bin;%PATH%
次に、OWC10パッケージをWebサーバーにインストールします。パッケージは、Microsoftからダウンロードできます。インストール時の選択肢は既定のままでかまいません。
OWC10はCOM技術なので、続いてOffice XP PIA(Primary Interop Assembly)をWebサーバーにインストールし、OWC10コンポーネントを.NETコードから使用できるようにしなければなりません。MicrosoftからOXPPIA.EXEファイルをダウンロードしてください。いくつかのZIPファイルになっているので、Webサーバー上のどこかの永続的ディレクトリ(「C:oxppia」をお勧めします)で解凍します。次にコマンドプロンプトを起動し(PATHはすでに正しく設定されているはずです)、「C:oxppia」ディレクトリに移動して、「REGISTER.BAT」を実行します。このコマンドファイルは、Office XP PIAをGAC(Global Assembly Cache)にインポートし、レジストリ設定を変更します。「REGISTER.BAT」からの出力を調べ、GACUTILコマンドを実行できることを確認してください。PATHが誤っていると、PIAはインポートされません。READMEでは、「Visual Studio .NETコマンドプロンプトを使う」とされていますが、おそらく、WebサーバーにはVS.NETがインストールされていないでしょうから、PATH変更は自力でやってください(結果は同じです)。
最後に、Webサーバーの「machine.config」ファイルを開き、<assemblies>セクションの下に次の1行を追加します(.NET Framework 1.1の「machine.config」は、「C:WINDOWSMicrosoft.NETFrameworkv1.1.4322CONFIG」ディレクトリにあるはずです)。
<add assembly="Microsoft.Office.Interop.Owc, Version=10.0.4504.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
以降では、OWCプログラミングモデルと、ASP.NET Webページからグラフを作成するためのソースコードをいくつか見ていきます。1つの例として、OWCを使った散布図を作成してみます。
OWCプログラミングモデル
ASP.NET WebページにOWCグラフを表示するため、グラフ表示だけを行うASP.NET Webページを1つ作成し、その名前を「getchart.aspx」とします。Webページでのグラフ表示には、標準のHTML<img>タグを次のとおり使用します。
<img src="getchart.aspx" />
「getchart.aspx」ページは、サーバのメモリー中にOWCグラフを作成し、それをバイナリGIF画像としてクライアントにストリーミングします。したがって、クライアント側からは「getchart.aspx」が.GIFファイルに見えますが、舞台裏ではWebサーバが動き、.GIFファイルを動的に生成するコードを実行しています(この手法は、『Displaying a Scaled List of Images』など、他の4Guys記事でも使用されています)。
「getchart.aspx」内のコードに入力パラメータを渡すには、通常のHTTPクエリ文字列とASP.NETセッション変数のどちらでも使えます。
「getchart.aspx」ファイルをもう少し詳しく見てみましょう。ASP.NET分離コードモデルを使用すれば、「getchart.aspx」ファイルの中身は次の2つのASP.NETディレクティブにすぎません。
<%@ Page Language="vb" AutoEventWireup="false"
Codebehind="getchart.aspx.vb" Inherits="getchart"%>
<%@ OutputCache Duration="5" VaryByParam="none" %>
「getchart.aspx」ファイルの働きは、クライアントに.GIFファイル(バイナリデータストリーム)を返すことなので、このファイルにHTMLを含めることはできません。「getchart.aspx」は分離コードファイルへの単なる参照にすぎず、参照された分離コードファイルがバイナリデータストリームを生成します。2行目は、キャッシュの寿命を指定しています(この例では5秒)。データベースに更新頻度の高い情報(気象データや株価など)が含まれているときは、キャッシュ寿命を短くし、再表示ボタンが押されるたびに更新済みのグラフが表示されるようにしなければなりません。
では、分離コードファイルを見てみましょう。すべての出来事はここで起こります。
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports Microsoft.Office.Interop
Public Class getchart
Inherits System.Web.UI.Page
Protected WithEvents ChartSpace1 As Owc.ChartSpace
Private Sub Page_Load(ByVal Sender As System.Object, _
ByVal E As System.EventArgs) Handles MyBase.Load
Response.Buffer = TRUE
Response.ContentType = "image/gif"
’SQL Server connection string:
Dim ConnectionString As String = "connection string"
’SQL command to find number of datapoints:
Dim CountText As String = "SELECT COUNT(*) From OWCDATA"
’SQL command to get the datapoints:
Dim CommandText As String = "SELECT X, Y From OWCDATA ORDER BY X"
’Define the database connection object:
Dim myConnection As New SqlConnection(ConnectionString)
’Define the command object to find number of datapoints:
Dim myCount As New SqlCommand(CountText, myConnection)
’Define the command object to get the datapoints:
Dim myCommand As New SqlCommand(CommandText, myConnection)
’Define Data Reader object:
Dim DataReader1 As SqlDataReader
’i = index variable (used to populate array)
’NumPoints =
’ integer representing number of datapoints from database
’aX = array containing X values
’aY = array containing Y values
Dim i, NumPoints, aX, aY
’Define chart and series objects, as used in OWC chart model:
Dim Chart1, Chart1_Series1
’Open the database connection
myConnection.Open()
’Step 1: Find number of datapoints and return value
’in "NumPoints" variable:
NumPoints = myCount.ExecuteScalar()
’Debug
’Response.Write(NumPoints & "#")
’Redimension arrays according to number of datapoints
ReDim aX(NumPoints - 1)
ReDim aY(NumPoints - 1)
’Step 2: Get the datapoints and return X and Y values
’in aX and aY arrays:
DataReader1 = myCommand.ExecuteReader()
i = 0
While DataReader1.Read
aX(i) = DataReader1.GetValue(0)
aY(i) = DataReader1.GetValue(1)
i = i + 1
End While
DataReader1.Close()
’Debug
’For i = 0 to NumPoints - 1
’ Response.Write(aX(i) & "|" & aY(i) & "#")
’Next i
’Close the database connection
myConnection.Close()
’Create a new chartspace:
ChartSpace1 = new Owc.ChartSpace()
’Create a new chart within ChartSpace1:
Chart1 = Chartspace1.Charts.Add(0)
’Add a new dataseries within Chart1:
Chart1_Series1 = Chart1.SeriesCollection.Add(0)
’Define Chart1_Series1 as "scatter" (XY) diagram,
’with lines and markers:
Chart1_Series1.Type = _
Chartspace1.Constants.chChartTypeScatterLineMarkers
’Name the dataseries (name appears in Legend):
Chart1_Series1.SetData (OWC.ChartDimensionsEnum.chDimSeriesNames, _
OWC.ChartSpecialDataSourcesEnum.chDataLiteral, "Chart1_Series1")
’Populate the X and Y values from array:
Chart1_Series1.SetData (OWC.ChartDimensionsEnum.chDimXValues, _
OWC.ChartSpecialDataSourcesEnum.chDataLiteral, aX)
Chart1_Series1.SetData (OWC.ChartDimensionsEnum.chDimYValues, _
OWC.ChartSpecialDataSourcesEnum.chDataLiteral, aY)
’Format the chartspace elements.
With ChartSpace1
’.Border.Color = Chartspace1.Constants.chColorNone
End With
’Format the chart elements.
With Chart1
’.SeriesCollection(0).Interior.Color = "Rosybrown"
’.PlotArea.Interior.Color = "Wheat"
.HasLegend = true
.Legend.Position = _
OWC.ChartLegendPositionEnum.chLegendPositionBottom
.HasTitle = true
.Title.Caption = "Chart1"
.Axes(0).HasTitle = true
.Axes(0).Title.Caption = "Y axis"
.Axes(1).HasTitle = true
.Axes(1).Title.Caption = "X axis"
End With
’Return the new chart in GIF format.
Response.BinaryWrite(Chartspace1.GetPicture ("gif", 500, 400))
Response.End
End Sub
End Class
次のスクリーンショットは、あるサンプルデータをグラフにして、ブラウザに表示したところです。
Microsoft.Office.Interop名前空間は、Office XP PIAを指しますから、これをサーバにインストールしておいてください。ソースコードのコンパイル時には、必ずOffice XP PIA OWC DLLを参照しなければなりません。Visual Studio .NETを使用しているときは、Microsoft.Office.Interop.Owc.dllファイル(Office XP PIAファイルを解凍したディレクトリにあります)への参照を追加するだけです。コマンドライン経由でコンパイルするときは、/r:スイッチを次のように使用してください。
vbc /t:library /out:bingetchart.dll /r:System.dll /r:System.Web.dll
/r:System.Data.dll /r:C:oxppiaMicrosoft.Office.Interop.Owc.dll
getchart.aspx.vb
上記コードには、いくつか興味深い点があります。まず、データソースがOWCDEMOというMSSQLデータベースにあることです。ここにはOWCDATAという表があり、その表にはXとYという2つの数値データフィールドが含まれています。「getchart.aspx」の目的は、データベース内のこのデータを散布図(XYグラフ)にプロットしていくことです。本稿のダウンロードサンプルに、このMS-SQLデータベースの全内容が収録されています。
ASP.NET DataSetからOWCグラフにデータ点を直接プロットする方法はありません。したがって、まずデータベースからリテラル配列にデータを読み込み、その配列からOWCグラフにデータ点をプロットしていくことになります。今後、抽象的なデータソース(DataSet、XMLファイル、配列など)からXYグラフを直接プロットできるようなASP.NETサーバコントロールができれば、グラフ作成には強力な武器となるでしょう。
DataSetよりDataReaderを使ったほうが速く効率的です。そのためには、返されてくるレコードの数を前もって知り、配列のサイズを決めなければなりません。まず、SQLのSELECT COUNT(*)クエリでレコードの数を求め、次いで配列のサイズを決め、最後にもう1つのSQL SELECTクエリでレコードを取得します。また、レコードのソートもグラフ作成と無関係ではありません。点と点を線でつないだ折れ線グラフがほしいときは、レコードをX軸に沿ってソートしなければなりません。それにはSQLのORDER BY句を使います。
OWCグラフモデルは、「グラフ空間」という概念に基づいています。グラフ空間は1つ以上のグラフを含むことができ、そこに含まれるどのグラフも1つ以上の「データシリーズ」を含むことができます。OWCグラフのプログラミングでは、まずグラフ空間を作成し、そのグラフ空間にグラフを追加し、グラフタイプを選択し、データシリーズを追加し、最後にデータシリーズにデータを埋めていくという手順をとります。必要なら、レイアウトオプションを指定することもできます。配色、軸タイトル、見出し、凡例などが代表的なオプションですが、OWCには何百というレイアウトオプションがあり、グラフをどのようにでも微調整できます。グラフモデルは、グラフタイプごとに少しずつ違うことを心得ておいてください。たとえば、円グラフと散布図では、必要なパラメータが異なります。OWCグラフモデルの詳細については、OWC10インストールパッケージに含まれるOWCヘルプファイルを参照してください。
最後に、Response.BinaryWriteパラメータを使用して、グラフィックファイルのフォーマット(GIF)と、幅と高さ(単位はピクセル)を指定しています。こうして、OWCグラフ画像を好みに合わせて伸縮させることができます。
まとめ
本稿で使用した例のソースコードは、ダウンロードサンプルに収録されています。また、OWCで扱える各種のグラフタイプを紹介している、もっと完全なデモもあります。そのソースコードを見れば、グラフタイプごとのプログラミングの違いもわかりますから、グラフ作成の「ハウツーガイド」としてもご使用いただけるでしょう。もっと高度なグラフオプションのデモとして、傾向線やエラーバーを使ったグラフサンプルも含めておきました。OWC10についてさらに学びたい方は、次のリソースを参照してください。
では、ハッピープログラミング!