連載:世界の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.exeSDLの組み合わせで行っていたが、ベータ2では新しいツールであるwsdl.exeWSDLで同等の作業を行う(これらは.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サービスをインターネット上で公開できる。ぜひとも、この次世代アプリケーション環境を自身で経験してもらいたい。 End of Article

関連記事(Insider.NET内)
Insider's Eye
Tech・Ed 2001:VS.NETベータ2など、プログラマ待望の最新版.NET開発環境がついに登場
連載
第2回 Microsoft TerraService-地図表示のためのWebサービス-
特集
Visual Studio.NETベータ2の新機能
 
 

 INDEX
  [連載]世界のWebサービス―― 究極のWebサービスを求めて ――
  第6回 ベータ2で加速するWebサービス
  1.ベータ2対応Webサービスを使ってみる
    
「世界のWebサービス」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間