連載:世界のWebサービス
第6回 ベータ2で加速するWebサービス
1.ベータ2対応Webサービスを使ってみる
田口 景介
2001/07/11 |
|
ベータ2のリリースからまだ日が浅いにもかかわらず、すでにベータ2への移行は着々と進みつつある。本連載第2回「Microsoft TerraService」で紹介したTerraServer(http://terraserver.microsoft.net/)やIBuySpy(http://www.ibuyspy.com/)といった、Microsoftによるフィールド・テストサイトでは、すでにベータ2へと移行してサービスを始めている(TerraServerのベータ2版はhttp://terraservice.net/)。そこで、以前TerraServer用クライアント・アプリケーションとして作成したTerraClientをベータ2対応に移植して、その違いを確認することにしよう。
|
TerraClient |
TerraServerでホスティングされるTerraServiceを呼び出して、指定した緯度経度付近の航空写真を表示するプログラム。ベータ1バージョンと比べて、見た目に変化はない。TerraServerについて詳しくは本連載第2回を参照してほしい。
|
■WSDL(Webサービス記述言語)
まず、TerraServerでホスティングされているTerraServiceを利用するために、プロキシ・コードを生成する。ベータ1ではこの作業を.NET Framework SDKに付属するツールであるWebServiceUtil.exe+SDLの組み合わせで行っていたが、ベータ2では新しいツールであるwsdl.exe+WSDLで同等の作業を行う(これらは.NET Framework SDKを使用してプログラミングを行う場合の話である。Visual Studio.NETを使用する場合には、これらのツールを明示的に使用する必要はない)。以下のコマンドを実行すると、カレント・ディレクトリにTerraService.cs(C#のソース・ファイル)が作成されるはずだ。なお、ベータ2ベースのTerraServerがまだ安定していないのか、筆者が試したときにはなかなか正常にプロキシ・コードが生成されず、wsdl.exeを実行しても「no classes were generated」が表示されるばかりであった。もしこのメッセージが表示されるようならば、しばらくおいてから再度試してもらいたい。
wsdl /n:TerraClient http://terraservice.net/TerraService.asmx?WSDL
ところで、前回紹介したように、Microsoft以外のサーバでホスティングされるWebサービスでも、WSDLを提供して同様にWebサービスを呼び出せるようになっている。ところが、筆者が試した範囲では、他社製のサーバが提供するWSDLを参照したときには、ベータ2のwsdl.exeでプロキシ・コードを生成できるWebサービスは皆無だった。WSDLの互換性が確立されるのは、まだこれからのようだ。
WSDL本来の主旨どおり、将来はプラットフォームに依存することなく、WSDLでプロキシ・コードを生成できるようになってほしい。しかし現在の状況には、悲観的な将来を予感させるものもある。.NET Framework SDKに付属するwsdl.exeは、当然のことながら、.NET Framework用のソース・コードを生成するツールだ。そして、他社製のクラス・ライブラリには、やはり専用のWSDLツールがセットで提供される。つまり、クラス・ライブラリごとにいくつものwsdl.exeに相当するツールが存在するわけだ。となると、各社とも自社製のサーバおよびクラス・ライブラリとの互換性を最優先してツールを設計し、他社製品との互換性が重要視されない可能性は否定できない。これが杞憂に済むことを期待したい。
■ソースの修正は規則的
TerraClientのメイン・ソース・ファイルを以下に示す。
1: using System;
2: using System.Drawing;
3: using System.Drawing.Imaging;
4: using System.Collections;
5: using System.ComponentModel;
6: using System.Windows.Forms;
7: using System.Data;
8: using System.IO;
9:
10: namespace TerraClient
11: {
12: public class Form1 : System.Windows.Forms.Form
13: {
14: private System.Windows.Forms.PictureBox pictureBox1;
15: private System.Windows.Forms.Label label1;
16: private System.Windows.Forms.Label label2;
17: private System.Windows.Forms.TextBox textLon;
18: private System.Windows.Forms.TextBox textLat;
19: private System.Windows.Forms.Button button1;
20: private System.Windows.Forms.StatusBar statusBar;
21: private System.ComponentModel.Container components = null;
22:
23: public Form1()
24: {
25: InitializeComponent();
26: }
27:
28: protected override void Dispose( bool disposing )
29: {
30: if( disposing )
31: {
32: if (components != null)
33: {
34: components.Dispose();
35: }
36: }
37: base.Dispose( disposing );
38: }
39:
40: private void InitializeComponent()
41: {
42: this.button1 = new System.Windows.Forms.Button();
43: this.statusBar = new System.Windows.Forms.StatusBar();
44: this.pictureBox1 = new System.Windows.Forms.PictureBox();
45: this.textLat = new System.Windows.Forms.TextBox();
46: this.textLon = new System.Windows.Forms.TextBox();
47: this.label1 = new System.Windows.Forms.Label();
48: this.label2 = new System.Windows.Forms.Label();
49: this.SuspendLayout();
50: //
51: // button1
52: //
53: this.button1.Location = new System.Drawing.Point(440, 320);
54: this.button1.Name = "button1";
55: this.button1.TabIndex = 5;
56: this.button1.Text = "実行";
57: this.button1.Click += new System.EventHandler(this.button1_Click);
58: //
59: // statusBar
60: //
61: this.statusBar.Location = new System.Drawing.Point(0, 377);
62: this.statusBar.Name = "statusBar1";
63: this.statusBar.Size = new System.Drawing.Size(536, 20);
64: this.statusBar.TabIndex = 0;
65: this.statusBar.Text = "座標を入力してください";
66: //
67: // pictureBox1
68: //
69: this.pictureBox1.Location = new System.Drawing.Point(16, 8);
70: this.pictureBox1.Name = "pictureBox1";
71: this.pictureBox1.Size = new System.Drawing.Size(500, 300);
72: this.pictureBox1.TabIndex = 0;
73: this.pictureBox1.TabStop = false;
74: //
75: // textLat
76: //
77: this.textLat.Location = new System.Drawing.Point(128, 352);
78: this.textLat.Name = "textLat";
79: this.textLat.TabIndex = 4;
80: this.textLat.Text = "37.8";
81: //
82: // textLon
83: //
84: this.textLon.Location = new System.Drawing.Point(128, 320);
85: this.textLon.Name = "textLon";
86: this.textLon.TabIndex = 3;
87: this.textLon.Text = "-122.4";
88: //
89: // label1
90: //
91: this.label1.Location = new System.Drawing.Point(16, 320);
92: this.label1.Name = "label1";
93: this.label1.TabIndex = 1;
94: this.label1.Text = "緯度";
95: //
96: // label2
97: //
98: this.label2.Location = new System.Drawing.Point(16, 352);
99: this.label2.Name = "label2";
100: this.label2.TabIndex = 2;
101: this.label2.Text = "経度";
102: //
103: // Form1
104: //
105: this.AutoScaleBaseSize = new System.Drawing.Size(5, 12);
106: this.ClientSize = new System.Drawing.Size(536, 397);
107: this.Controls.AddRange(new System.Windows.Forms.Control[] {
108: this.statusBar,
109: this.button1,
110: this.textLat,
111: this.textLon,
112: this.label2,
113: this.label1,
114: this.pictureBox1});
115: this.Name = "Form1";
116: this.Text = "Form1";
117: this.ResumeLayout(false);
118:
119: }
120:
121: [STAThread]
122: static void Main()
123: {
124: Application.Run(new Form1());
125: }
126:
127: private void button1_Click(object sender, System.EventArgs e)
128: {
129: LonLatPt center = new LonLatPt();
130: Theme theme = new Theme();
131: Scale scale = new Scale();
132: Int32 mapWidth;
133: Int32 mapHeight;
134:
135: button1.Text = "読み込み中";
136: statusBar.Text = "読み込み中";
137:
138: try
139: {
140: // テキストボックスに入力された座標を取得する
141: center.Lon = Double.Parse(textLon.Text);
142: center.Lat = Double.Parse(textLat.Text);
143: }
144: catch
145: {
146: // 数値以外が入力されると例外が発生
147: statusBar.Text = "座標は実数で入力してください";
148: button1.Text = "実行";
149: return;
150: }
151:
152: // 画像の種類を指定する
153: // Theme.Photo 航空写真
154: // Theme.Topo 絵地図
155: // Theme.Relief レリーフ
156: theme = Theme.Photo;
157: // 縮尺を指定する。ここでは32m/ドットに固定
158: // WinForms.Scaleクラスとの競合を避けるため、
159: // パッケージ名を含めて指定する
160: scale = TerraClient.Scale.Scale32m;
161:
162: // ピクチャボックスのサイズで地図を取得する
163: mapWidth = pictureBox1.Size.Width;
164: mapHeight = pictureBox1.Size.Height;
165:
166: TerraService ts;
167: AreaBoundingBox abb;
168: try
169: {
170: // Webサービスオブジェクトを作成し、タイルを取得する
171: ts = new TerraService();
172: abb = ts.GetAreaFromPt(center, theme, scale, mapWidth, mapHeight);
173: }
174: catch
175: {
176: statusBar.Text = "その座標に画像はありません";
177: button1.Text = "実行";
178: return;
179: }
180:
181: // 地図画像を読み込み、ピクチャボックスに設定する
182: // Imageオブジェクトを作成する
183: PixelFormat pf = PixelFormat.Format32bppRgb;
184: Image compositeImage = new Bitmap(mapWidth, mapHeight, pf);
185: Graphics compositeGraphics = Graphics.FromImage(compositeImage);
186:
187: // 取得したタイルを元にサーバから地図画像を読み込み、
188: // Imageオブジェクトに描画する
189: Int32 xStart = abb.NorthWest.TileMeta.Id.X;
190: Int32 yStart = abb.NorthWest.TileMeta.Id.Y;
191: for (Int32 x = xStart; x <= abb.NorthEast.TileMeta.Id.X; x++)
192: {
193: for (Int32 y = yStart; y >= abb.SouthWest.TileMeta.Id.Y; y--)
194: {
195: TileId tid = abb.NorthWest.TileMeta.Id;
196: tid.X = x;
197: tid.Y = y;
198: Image tileImage = Image.FromStream(new MemoryStream(ts.GetTile(tid)));
199: compositeGraphics.DrawImage(tileImage,
200: (x - xStart) * tileImage.Width - (Int32) abb.NorthWest.Offset.XOffset,
201: (yStart - y) * tileImage.Height - (Int32) abb.NorthWest.Offset.YOffset,
202: tileImage.Width, tileImage.Height);
203: tileImage.Dispose();
204: }
205: }
206:
207: // 地図画像を描画したImageオブジェクトを
208: // ピクチャボックスに設定する
209: pictureBox1.Image = compositeImage;
210:
211: statusBar.Text = "画像が表示されました";
212: button1.Text = "実行";
213: }
214: }
215: } |
|
TerraClient.cs(TerraClientのメイン・ソース・ファイル) |
|
|
※このプログラムのコンパイルおよび実行には.NET Framework SDKベータ2あるいはVisual Studio.NETベータ2が必要です。 |
TerraClientは比較的シンプルなサンプル・プログラムだったこともあって、ベータ2に対応させるにあたり、ソース・コードに加えた修正はそれほど多くはない。また、いずれの修正個所もプログラムの構造にまで及ぶものではなく、規則的な修正で済んでいる。もっとも、これは運よく(?)TerraClientに複雑な修正を要するコードが含まれていなかっただけで、常に規則的な修正で済むわけではない。
以下にTerraClient.csの修正点を挙げる。
- System.WinFormsネームスペースがSystem.Windows.Formsネームスペースに変更された(6行目)
前述したように、ネームスペースの構造が変わっているため、これを修正している。また、以前はコントロールを1つ1つフォームへレイアウトしなければならなかったが、ベータ2ではControlCollection.AddRangeメソッドで一括してレイアウトできるようになっている(107行目)。
- PixelFormat.Format32bppRGBがPixelFormat.Format32bppRgbへ変更された(183行目)
ImageFormat.JPEGがImageFormat.Jpegに変更されるなど、命名規則が変更され、単語の切れ目以外はすべて小文字とすることで統一されたようだ。
- String.ToDoubleメソッドが削除され、Double.Parseメソッドで同等の処理を行うようになった(141〜142行目)
このほかにも、TerraServiceで定義されているTileMeta.IDプロパティがTileMeta.Idプロパティへと名称変更されたため、該当個所を修正している(189〜195行目)。
■ビルド
TerraClientをビルドするには、wsdl.exeで生成したTerraService.csと上のリストに示したTerraClient.csをカレント・ディレクトリに置いて、以下のコマンドを実行する。ベータ1では「/r オプション」で明示的にリンクするライブラリを指定する必要があったが、ベータ2からは不要になり、かなり簡単にコマンドラインからコンパイル可能になった。
csc /t:winexe /out:TerraClient.exe TerraService.cs TerraClient.cs
以上のコマンドを実行すると、カレント・ディレクトリにTerraClient.exeが生成される。従来ならば、このTerraClient.exeは.NET Framework SDKがインストールされている環境でしか動作させることができなかったが、ベータ2からはよりコンパクトな.NET Framework再配布パッケージがインストールされている環境でも実行可能となっている。再配布パッケージは、.NET Framework SDKからランタイム・ライブラリなどの.NETアプリケーションの実行環境だけを抜き出したもので、.NET Framework SDKの123MBytesに対して、わずか18MBytesのパッケージとなっているため、手軽に.NETアプリケーションを配布できる(これについても「Tech・Ed 2001:VS.NETベータ2など、プログラマ待望の最新版.NET開発環境がついに登場」で解説している)。
■
出荷バージョンへと直接つながる.NET Frameworkベータ2がリリースされたことによって、各所でWebサービスの開発に弾みがつくことだろう。これまでWebサービスを開発したところで、それを公開する手段がなかったために、広く使ってもらうことができずにいたプログラマも「Visual Studio.NETベータ2の新機能」で解説したWebホスティング・サービスを利用すれば、誰もが自作のWebサービスをインターネット上で公開できる。ぜひとも、この次世代アプリケーション環境を自身で経験してもらいたい。