はじめに
都道府県の白地図上に、地域別になんらかのデータ(人口や売上など)を表示したい、と考える人は多いのではないでしょうか。
なぜならデータを地図上に表現することは、直感的でわかりやすいものだからです。実際、都道府県の白地図にグラフなどでデータの量を表現した図は、日常の様々なところで見かけると思います。
NetAdvantage for Silverlight Data Visualizationのコントロールの1つであるXamMapを使うと、そのような地図アプリを簡単に構築することができます。
本稿では、XamMapの利用方法を、都道府県の白地図に人口のデータを表示するサンプルのアプリケーションを開発しながら解説します。
対象読者
Visual Basic 2010またはVisual C# 2010でSilverlightアプリケーションを開発した経験がある方。
必要環境
Visual Basic 2010、Visual C# 2010以降の開発環境。今回のサンプルは原稿執筆時の最新バージョンであるVisual Studio 2010 Ultimate、.NET Framework 4にて作成しています。また、Windows 7 Ultimate(64ビット)において動作を検証しています。
SQL Server 2008 R2またはSQL Server 2008 R2 Express Edition(with Management Tools)。
またXamMapを利用するため、NetAdvantage for Silverlight Data Visualization:http://jp.infragistics.com/dotnet/netadvantage/silverlight/data-visualization.aspxが必要となります。こちらは有償の製品ですが、20日間すべての機能を使用できるトライアル版としてインストール可能です。
XamMapの概要
XamMapは、NetAdvantage for Silverlight Data Visualizationで提供されるコントロールの1つです。
XamMapを利用すると、Silverlight上に地理データを表示し、さらにパン、ズーム、マウスオーバー、ツールチップなどリッチな操作ができる地図を提供できます。
サンプルアプリで利用しているXamMapの機能
本稿のサンプルは、都道府県の白地図に、都道府県別に人口の多さを円で表示するアプリケーションです。
このサンプルで利用しているXamMapの機能は以下です。
機能 | 説明 |
データバインディング | SQLサーバーに格納した都道府県の白地図の地理データを表示する |
カスタマイズしたシンボル | SQLサーバーに格納した都道府県別の人口数に応じたシンボル(円)として表示する |
ツールチップ | シンボル上に都道府県名と人口を表示するツールチップを表示する |
XamMapを利用した地図アプリ開発
それでは、XamMapを利用した地図アプリ開発方法を解説します。はじめに開発手順の全体像を示します。
開発手順の全体像
- サンプル用のデータベースを取り込む
- Silverlightアプリケーションを作成する
- Silverlight対応WCFサービスをWebサイトに追加する。
- WCFサービスに、データベースから情報を取得するメソッドを定義する
- Silverlightアプリケーションに、XamMapを追加する
- XamMapにWCFサービスから取得した都道府県境データをレイヤーとして表示する
- XamMapにWCFサービスから取得した人口データをシンボルとして表示する
それではさっそく開発していきましょう。
サンプル用のデータベースを取り込む
まずは、サンプル用のデータベースをSQLサーバーに取り込みます。
このデータベースには、都道府県境の地理情報と都道府県別の人口が格納されています。次の手順でSQLサーバーに取り込みましょう。
データベースのアタッチ
- サンプルのデータベース、XamMapDB.zipをダウンロードする。
- zipファイルを解凍し次の2ファイルを確認する。
- XamMapDB.mdf
- XamMapDB_log.ldf
- SQLServer ManagementStudioを起動し、ローカルのデータベースに接続する(※使用しているOSが、Vistaや7の場合は管理者権限で実行してください)
- データベース上で右クリックし、アタッチを選択し、データベースのアタッチ画面を表示します。
- データベースのアタッチ画面で追加ボタンを押し、XamMapDB.mdfを選択しOKボタンで完了します。
- XamMapDBが、SQLサーバーにアタッチされます。
データベースの確認
アタッチしたデータベースの内容を確認します。
都道府県境の地理データ
XamMapDBデータベースのPrefectureテーブル上で右クリックし、上位1000行の選択を実行し、Prefectureデータを取得します。すると空間結果タブで、日本の都道府境の地理データを地図の形で確認することができます。
都道府県別の人口データ
XamMapDBデータベースのPopulationテーブルには、都道府県別の人口が格納されているので、確認してください。PopulationテーブルのPrefectureId列とPrefectureテーブルのId列で関連しています。
都道府県別の地理データと人口データを取得するストアド プロシージャ
XamMapDBデータベースのストアド プロシージャには、GetPrefecturePopulationという名前のストアド プロシージャが定義されています。GetPrefecturePopulationは、PrefectureテーブルとPopulationテーブルを結合し、都道府県別の地理データと人口を取得するSelect文を実行した結果を返します。
テーブルを分割した理由
ところで、このサンプルだけでの利用に限っては、Prefectureテーブル、Populationテーブルと分割する必要性はありません。しかし、カスタマイズしやすさを考え、このような構成としました。
たとえば、人口ではなく、売上データを表示したい場合。このデータベースに都道府県別の売上データを持つテーブルを作成しデータを入れることで、簡単にカスタマイズすることができるのです。
以上で、サンプル用のデータベースを取り込むことができました。次に、取り込んだデータをXamMapに表示するSilverlightアプリケーションを作成します。
Silverlightアプリケーションを作成する
XamMapを利用したSilverlightアプリケーションを作成します。
Silverlightアプリケーションプロジェクトの作成
- Visual Studioを起動する
- メニュー→ファイル→新規作成→プロジェクトを選択し、新しいプロジェクト画面を表示する
- インストールされたテンプレートリストから、Silverlightを選択する
- Siverlightアプリケーション プロジェクトを選択し、任意の名前(本稿ではXamMapSample)をつけてOKボタンをクリックする
- 新しいSilverlightアプリケーション画面が表示される
- Silverlightアプリケーションを新しいWebサイトでホストする、のチェックをONにし、OKボタンをクリックする
ソリューションの構成の確認
作成したSilverlightアプリケーションプロジェクトのソリューションの構成を確認します。ソリューション内には、Silverlightアプリケーションプロジェクト(=XamMapSample)とそのアプリケーションをホストするWebサイトプロジェクト(=XamMapSample.Web)の2つのプロジェクトが作成されています。
Silverlight対応WCFサービスをWebサイトに追加する
Silverlightアプリケーションは、原則として、直接データベースにアクセスすることができません。ブラウザに読み込まれ実行されるクライアントサイドの技術だからです。
このサンプルでは、Silverlightアプリケーションからデータベースにアクセスするために、次の手順でSilverlightをホストするWebサイトプロジェクト(=XamMapSample.Web)にWCFサービスを追加し、そのサービス経由でアクセスします。
WCFサービスの追加手順
- Webサイト(=XamMapSample.Web)プロジェクトで右クリックし、追加→新しい項目を選択する
- 新しい項目の追加画面のインストールされたテンプレートリストから、Silverlightを選択する
- Silverlight対応WCFサービス を選択する
- 任意のサービスの名前(本稿ではXamMapService)を入力し、追加ボタンをクリックする
WCFサービスに、データベースから情報を取得するメソッドを定義する
追加したWCFサービスに、データベースから情報を取得するメソッドを定義します。先ほど作成したWCFサービス(=XamMapService.svc)を、次のように編集します。
//追加 using System.Configuration; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; namespace XamMapSample.Web { [ServiceContract(Namespace = "")] [SilverlightFaultBehavior] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class XamMapService { [OperationContract] public IEnumerable<Dictionary<string, string>> GetPrefecturePopulation() { using (var con = new SqlConnection()) { con.ConnectionString = ConfigurationManager .ConnectionStrings["XamMapDB"] .ConnectionString; var cmd = new SqlCommand() { Connection = con, CommandType = CommandType.StoredProcedure, CommandText = "GetPrefecturePopulation" }; con.Open(); var reader = cmd.ExecuteReader(); var rows = new List<Dictionary<string, string>>(); if (reader != null) { while (reader.Read()) { var row = new Dictionary<string, string>(); for (int i = 0; i < reader.FieldCount; i++) row.Add(reader.GetName(i), reader.GetValue(i).ToString()); rows.Add(row); } } con.Close(); return rows; } } } }
このコードの目的は、データベースに定義したGetPrefecturePopulationストアド プロシージャを実行し、その実行結果(=都道府県境および都道府県別の人口)を取得することです。その処理を、WCFサービスのメソッドとしてGetPrefecturePopulationという名前で定義しています。データベースにアクセスする処理などのコード詳細については、本稿の範囲ではないので解説を避けます。
続いて、データベースへの接続文字列を、Webサイトプロジェクト(=XamMapSample.Web)のWeb.configファイルを編集し、次のように定義します。
<configuration> <connectionStrings> <add name="XamMapDB" connectionString="Data Source=(local);Initial Catalog=XamMapDB;Integrated Security=True" /> </connectionStrings> <!-- 省略 --> </configuration>
以上で、Silverlight対応WCFサービスを追加し、そのサービスからデータベースにアクセスする処理を定義することができました。
SilverlightアプリケーションにXamMapを追加する
ここまで、都道府県境および都道府県別の人口を格納したサンプル用のデータベースを取込み、そのデータベースにSiverlightアプリケーションからアクセスするためのWCFサービスを作成し、準備が整いました。
それではいよいよ、SilverlightアプリケーションにXamMapを追加し、地図アプリケーションを開発していきます。次の手順で、XamMapをMainPage.xamlへ追加します。
XamMapをMainPage.xamlへ追加する手順
- Silverlightアプリケーションプロジェクト(=XamMapSample)のMainPage.xamlを開く
- ツールボックスからXamMapをMainPage.xamlのデザイナへドラッグ&ドロップする
以上で、XamMapを追加することができました。
XamMapにWCFサービスから取得した都道府県境データをレイヤーとして表示する
MainPage.xamlに追加したXamMapを修正し、都道府県境データをレイヤーとしてを表示させます。
サービス参照の追加
まずは、Silverlightアプリケーションプロジェクト(=XamMapSample)に、先ほど作成したWCFサービス(=XamMapService.svc)をサービス参照として追加します。
- Silverlightアプリケーションプロジェクト(=XamMapSample)の参照で右クリックしサービス参照の追加を選択する
- サービス参照の追加画面で探索ボタンをクリックし先ほど作成したWCFサービス(=XamMapService.svc)を表示する
- 名前空間に任意の名前(本稿ではXamMapServiceReference)を入力し、OKボタンをクリックする
以上でサービス参照の追加が完了しました。
画面側(XAML)の修正
都道府県境データを表示するレイヤーを定義するために、MainPage.xamlのXamMapを次のように修正します。
<UserControl x:Class="XamMapSample.MainPage" (略)> <Grid x:Name="LayoutRoot" Background="White"> <ig:XamMap Name="xamMap"> <ig:XamMap.Layers> <ig:MapLayer Name="Japan"> <ig:MapLayer.Reader> <ig:SqlShapeReader DataMapping="Data=Geom"/> </ig:MapLayer.Reader> </ig:MapLayer> </ig:XamMap.Layers> <ig:MapNavigationPane x:Name="navi" Margin="10" VerticalAlignment="Top" ig:XamDock.Edge="InsideRight" /> </ig:XamMap> </Grid> </UserControl>
コードビハインド側の修正
都道府県境データを取得するWCFサービスを呼び出し、先ほど作成したレイヤーにバインドするため、MainPage.xaml.csを次のように修正します。
//追加 using Infragistics.Controls.Maps; using XamMapSample.XamMapServiceReference; namespace XamMapSample { public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); var client = new XamMapServiceClient(); client.GetPrefecturePopulationCompleted += (sender, e) => { var jLayer = xamMap.Layers["Japan"]; if (jLayer.Reader != null) { (jLayer.Reader as SqlShapeReader).DataSource = e.Result; jLayer.ImportAsync(); } }; client.GetPrefecturePopulationAsync(); } } }
以上でXamMapにWCFサービスから取得した都道府県境データをレイヤーとして表示できるようになりました。さっそく実行してみましょう。次の画像のように都道府県の地図が表示されれば成功です。
また、画面の右側には、マップのナビゲーションが表示されているはずです。これを利用して地図の拡大縮小、パンなどの操作することもできます。
XamMapにWCFサービスから取得した人口データをシンボルとして表示する
XamMapにWCFサービスから取得した人口データをシンボルとして表示させます。さきほど都道府県境データをレイヤーとして表示させましたが、そのレイヤー上に、都道府県別に人口の多さに応じた円をシンボルとして表示させます。
画面側(XAML)の修正
都道府県別に人口の多さに応じた円をシンボルとして表示させるために、MainPage.xamlのXamMapを
次のように修正します。
<UserControl x:Class="XamMapSample.MainPage" (略)> <Grid x:Name="LayoutRoot" Background="White"> <ig:XamMap Name="xamMap"> <ig:XamMap.Layers> <!-- 追加:Importedイベントのハンドラ --> <ig:MapLayer Name="Japan" Imported="Japan_Imported"> <ig:MapLayer.Reader> <!-- 追加:Value=Population; ToolTip=Name --> <ig:SqlShapeReader DataMapping="Data=Geom;Value=Population; ToolTip=Name"/> </ig:MapLayer.Reader> <!-- 追加:表示するシンボルのテンプレート --> <ig:MapLayer.ValueTemplate> <DataTemplate> <Grid Width="{Binding Path=Value}" Height="{Binding Path=Value}"> <ToolTipService.ToolTip> <StackPanel> <TextBlock Text="{Binding Path=ToolTip, StringFormat='県名:{0}'}" /> <TextBlock Text="{Binding Path=Value, StringFormat='人口:{0:#,##0}(十万人)'}" /> </StackPanel> </ToolTipService.ToolTip> <Ellipse Fill="Aqua" Opacity="0.7"/> </Grid> </DataTemplate> </ig:MapLayer.ValueTemplate> </ig:MapLayer> </ig:XamMap.Layers> <ig:MapNavigationPane x:Name="navi" Margin="10" VerticalAlignment="Top" ig:XamDock.Edge="InsideRight" /> </ig:XamMap> </Grid> </UserControl>
このコードのポイントは3つあります。第1のポイントは、MapLayer要素にImportedイベントのハンドラを追加している点です。このイベントは、対象のレイヤーがインポートされたときに発生し、その際に地図上に表示するシンボルを設定することができます。
第2のポイントは、SqlShapeReader要素のDataMappingプロパティに、「Value=Population; ToolTip=Name」を追加している点です。Value=Populationでは、シンボルに表示する際に利用する人口数を設定し、またToolTip=Nameではツールチップに表示する都道府県名を設定しています。
第3のポイントは、表示するシンボルのテンプレートであるMapLayer.ValueTemplate要素を追加している点です。テンプレートには、都道府県名と人口を表示するためのツールチップ用のStackPanelと、人口数を円の大きさであらわすためのEllipseが定義されています。
また、このStackPanelとEllipseは、Grid内に定義されています。そのGridのWidth(幅)とHeight(高さ)は、{Binding Path=Value}と人口数がバインドされています。これによって、人口数に応じたシンボルを表示することができるわけです。
コードビハインド側の修正
続いて、画面側(XAML)のテンプレートに定義したシンボルに値を設定するために、MainPage.xaml.csを次のように修正します。
(略) namespace XamMapSample { public partial class MainPage : UserControl { public MainPage() { (略) } //人口の単位(十万人) const double PoplationUnit = 100000; private void Japan_Imported(object sender, MapLayerImportEventArgs e) { var layer = sender as MapLayer; foreach (MapElement ele in layer.Elements.Where(t => t!=null)) { //対象エリア(都道府県)の最大面積のポリゴンの //中心点を表示するシンボルの起点して設定する var plines = (ele as SurfaceElement).Polylines; var ordered = plines.OrderByDescending(c => c.GetPolygonArea()); var poly = ordered.ElementAt(0); ele.SymbolOrigin = poly.GetCentroid(); //人口の値を単位で割った値にする ele.Value = ele.Value / PoplationUnit; } } } }
さきほど、画面側(XAML)で定義した、レイヤーのImportedイベントのハンドラのJapan_Importedを実装しています。ポイントは2つあります。
第1に、シンボルの中心点の求める方法です。レイヤー上のMapElement要素から、都道府県境をあらわすポリゴンを各都道府県毎に取得します。その際、北海道や鹿児島のように、複数のポリゴンからなる都道府県があり得るため、その中で最大の面積を持つポリゴンを取得します。そのポリゴンの中心点を、シンボルの中心点として設定しています。
第2に、シンボルの値に人口を設定する方法です。画面側(XAML)では、人口の値が、そのままシンボルの幅と高さに設定されるようにバインドされていました。その場合、たとえば東京の1300万という値が、そのままシンボルに幅と高さに設定されると、画面上表示しきれない大きさになってしまいます。そこで、人口の値を画面上の程よい大きさで表示するため、人口を十万人単位で割った値として設定しています。
ポリゴン計算ユーティリティを追加
最後に、Silverlightアプリケーションプロジェクトに、ポリゴン計算のためのメソッドを定義した、ユーティリティを追加します。
using System; using System.Windows; using System.Collections.Generic; namespace XamMapSample { public static class Util { //ポリゴンの符号付きの面積を取得する public static double GetPolygonSignedArea(this IList<Point> c) { int n = c.Count; double a = 0.0; if (n > 2) { for (int k = n - 2, j = n - 1, i = 0; i < n; k = j, j = i, ++i) { a += c[j].X * (c[i].Y - c[k].Y); } } return a; } //ポリゴンの符号の面積を取得する public static double GetPolygonArea(this IList<Point> c) { return Math.Abs(0.5 * GetPolygonSignedArea(c)); } //ポリゴンの中心点を取得する public static Point GetCentroid(this IEnumerable<Point> points) { double x = 0.0; double y = 0.0; double c = 0.0; foreach (Point point in points) { x += point.X; y += point.Y; c += 1.0; } return new Point(x / c, y / c); } } }
このユーティリティには、大きく次の2つのメソッドが定義されています。
- ポリゴンの面積を取得する
- ポリゴンの中心点を取得する
それぞれのメソッドで行っている計算方法については、本稿の範囲を超えますので解説を避けます。ここでは、ポリゴンから値を計算するメソッドが拡張メソッドとして定義されていて、それをさきほどのレイヤーのImportedイベントのハンドラで利用している、という点だけ理解いただければ大丈夫です。
以上で、XamMapにWCFサービスから取得した人口データをシンボルとして表示できるようになりました。さっそく実行してみましょう。次の画像のように都道府県の地図上に人口が円のシンボルとして表示されれば成功です。またシンボルにマウスオーバーすると、都道府県名と人口数がツールチップとして表示されます。
まとめ
本稿では、XamMapの利用方法を、都道府県の白地図に人口のデータを表示するサンプルのアプリケーションを開発しながら解説しました。XamMapを利用すると、地図を利用したアプリケーションが簡単に開発できることがご理解いただけたのではないでしょうか。本稿が読者の皆様のアプリケーション開発にお役にたてば筆者として、それ以上うれしいことはありません。