現在位置近くの病院を素早く検索するサンプルプログラム
次に、ソリューションエクスプローラー内のMainWindow.xamlを展開して表示される、MainWindow.xaml.vbをダブルクリックしてリスト2のコードを記述します。
ロジックコードを記述する
リスト2 (MainWindow.xaml.vb)
Option Strict On
コンピュータの地理的位置にアクセスできるようにするクラスの含まれる、Windows.Devices.Geolocation名前空間をインポートします。
Imports Windows.Devices.Geolocation
Bing Mapsに関するクラスの含まれる、Bing.Maps名前空間をインポートします。
Imports Bing.Maps
UIに関するクラスの含まれるWindows.UI名前空間をインポートします。PushPinの色を設定する場合等に必要です。
Imports Windows.UI
アプリケーションウィンドウやウインドウ対話を作成し管理するサポートと、ウインドウ上の入力イベントを処理するクラスの含まれる、Windows.UI.Core名前空間をインポートします。
Imports Windows.UI.Core
最新のHTTPアプリケーション用のプログラミング インターフェイスを提供するクラスの含まれる、System.Net.Http名前空間をインポートします。
Imports System.Net.Http
コンテキストメニューおよびメッセージダイアログのサポートを提供するクラスの含まれる、Windows.UI.Popups名前空間をインポートします。
Imports Windows.UI.Popups Public NotInheritable Class MainPage Inherits Page
現在の地理的位置にアクセスするクラスである、Geolocatorクラスのメンバ変数myGeolocatorを宣言します。
Dim myGeolocator As Geolocator
Windowsランタイムコアイベントメッセージディスパッチャを提供するクラスである、CoreDispatcherクラスのメンバ変数、myCoreDispacherを宣言します。
Dim myCoreDispacher As CoreDispatcher
YahooのアプリケーションIDで初期化された、メンバ定数変数AppIDを宣言します。
Const AppID As String = "YahooのアプリケーションID"
GoogleのAPI Keyで初期化された、メンバ定数変数GoogleAppIDを宣言します。
Const GoogleAppID As String = "GoogleのAPI Key" Dim flag As Boolean = False Dim myCoodinatePosition As String = String.Empty Dim myAddressCoodinatePosition As String = String.Empty Dim myUri As String = String.Empty Dim myAddressLatitude As String Dim myAddressLongitude As String Dim myAddressUri As String
ページがアクティブになった時の処理
場所にアクセスし、場所が変更された時や、Geolocatorの機能が変更された時に発生する各イベントで、イベントハンドラを実行するDataShowプロシージャを実行します。
Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs) DataShow() End Sub
場所にアクセスし、場所が変更された時や、Geolocatorの機能が変更された時に発生する各イベントで、イベントハンドラを実行する処理。
myGeolocatorオブジェクトが作成されていない場合は、新しいGeolocatorクラスのインスタンスmyGeolocatorオブジェクトを作成します。
場所認識の精度レベルを表す、DesiredAccuracy プロパティにはDefaultを指定しておきます。
Default以外にHighがありますが、パフォーマンスが低下する恐れがありますので、Defaultを指定しています。
Window.Current.CoreWindow.Dispatcherで、現在アクティブになっている、ウインドウの内部コアオブジェクトのイベントディスパッチャを取得して、myCoreDispacherで参照します。
AddHandlerステートメントで、場所が更新された時のPositionChangedイベント時のイベントハンドラ、myGeolocator_PositionChangedを追加します。
同じく、AddHandlerステートメントで、更新された場所を提供するGeolocatorの機能が変更された時に発生する、StatusChanged時のイベントハンドラ、myGeolocator_StatusChangedを追加します。
Private Sub DataShow() If myGeolocator Is Nothing = True Then myGeolocator = New Geolocator myGeolocator.DesiredAccuracy = PositionAccuracy.Default myCoreDispacher = Window.Current.CoreWindow.Dispatcher End If AddHandler myGeolocator.PositionChanged, AddressOf myGeolocator_PositionChanged AddHandler myGeolocator.StatusChanged, AddressOf myGeolocator_StatusChanged End Sub
場所が変更された時の処理
myCoreDispacher.RunAsyncメソッドで、イベントディスパッチャを実行し、ディスパッチされたイベントの結果を非同期に返します。ディスパッチャの優先順位は標準のNormalを指定しています。
緯度と経度のデータや都市の住所データを含めることができるGeoPositionクラス型の変数psoを宣言し、PositionChangedイベントに関連付けられた場所データを取得します。
Clearメソッドで、地図内を一度クリアしておきます。
新しいPushpinクラスのインスタンスmyPinオブジェクトを作成します。ピンの背景色をNavyに指定します。
地理的位置に関連付けされた緯度と経度で初期化された、新しいLocationクラスのインスタンスmyLocationオブジェクトを作成します。Locationクラスは、地図上の場所の標高と座標値を含むクラスです。
ブール型のメンバ変数flagの値で条件分岐を行います。FlagがTrueであった場合、つまり、マウスを右クリックして表示される住所を入力するボックスに住所を入力して、「OK」ボタンがクリックされた場合、ということです。その場合は、Locationを、メンバ変数myAddressLatitudeに格納された緯度の値と、myAddressLongitudeに格納された経度の値で初期化します。
それ以外の場合は、現在位置の緯度と経度で初期化します。
MapLayerクラスのSetPositionメソッドで、マップレイヤー内にピンの位置を設定します。
myMapにAddメソッドでピンを追加します。
SetViewメソッドで、指定された中心部に位置、ズーム レベル、方位、およびピッチにマップビューを設定します。この場合myLocationの位置に16レベルでズームインします。
ブール型のメンバ変数flagの値で条件分岐を行います。FlagがTrueであった場合、つまり、マウスを右クリックして表示される住所を入力するボックスに住所を入力して、「OK」ボタンがクリックされた場合、ということです。
メンバ変数myAddressUriに「Yahoo!ローカルサーチAPI」のアドレスを格納します。
引数latにメンバ変数myAddressLatitudeが格納している緯度の値を指定して、lonにメンバ変数myAddressLongitudeが格納している経度の値を指定します。
AppidにはYahooのアプリケーションIDを指定します。
そうでない場合には、latとlonに現在の緯度と経度を指定します。
新しいHttpClientクラスのインスタンスmyAddressHttpClientオブジェクトを作成します。
HttpClientクラスは、URIで識別されるリソースにHTTP要求を送信し、そのリソースからHTTP応答を受信するための基本クラスが含まれています。
XML要素を表すXElementクラス型変数、xmldocを宣言します。
GetStringAsyncメソッドでmyAddressUriの結果を文字列として返し、変数myCurrentAddressに格納します。
GetStringAsyncメソッドは、指定URIにGET要求を送信し、非同期操作で応答本体を文字列として返すメソッドです。
返される文字列はXML形式になっています。このXMLのルート要素には、名前空間や不要な属性が定義されていて、内容を取得する際の邪魔(-_-;)になりますので、Replace関数で、ルート要素(
XElement.Parseメソッドで置換された結果のXMLが格納されている、myCurrentAddressを文字列として読み込みます。
読み込んだXMLから
要素の内容を取得して、変数myAddressに格納しておきます。新しいStackPanelのインスタンスmyAddressStackPanelオブジェクトを作成します。
Marginプロパティに5を指定して余白を設けます。
背景色をNavyに指定します。
このStackPanelオブジェクトのインスタンスを、非表示としておきます。
新しいTextBlockのインスタンスmyAddressTextBlockオブジェクトを作成します。文字色をRedに指定します。
文字サイズに24を指定し、パディングに5を指定します。
Textプロパティに住所を格納している変数myAddressと文字列「辺り」を連結して指定します。
ブール型のメンバ変数flagの値で条件分岐を行います。FlagがTrueであった場合、つまり、マウスを右クリックして表示される住所を入力するボックスに住所を入力して、「OK」ボタンがクリックされた場合、ということです。
myAddressTextBlockに変数myAddressと文字列「辺り」を連結して表示します。それ以外の場合は、WatermarkTextBoxに入力された値と文字列「辺り」を連結して表示します。
myStackPanelオブジェクトにAddメソッドで住所の設定された、myAddressTextBlockを追加します。
SetValueメソッドでmyAddressStackPanelのZIndexPropertyに20を指定します。myAddressStackPaneが前面に表示されます。
ブール型のメンバ変数flagの値で条件分岐を行います。FlagがTrueであった場合、つまり、マウスを右クリックして表示される住所を入力するボックスに住所を入力して、「OK」ボタンがクリックされた場合、ということです。
MapLayerクラスのSetPositionメソッドで、マップレイヤー内にmyAddressStackPanelオブジェクトの位置を設定します。
Locationをメンバ変数myAddressLatitudeが格納している緯度とmyAddressLongitudeが格納している経度で初期化します。
MapLayerクラスは、地図上の要素の位置を保持しているマップレイヤーを表すクラスです。それ以外の場合は、Locationを現在位置の緯度と経度で初期化します。
myMapにAddメソッドでmyAddressStackPanelオブジェクトを追加します。
myAddressStackPanelは最初の状態では非表示になっているのでわかりませんが、ピンの位置にStackPanelオブジェクトが表示されることになります。
AddHandlerステートメントでピンをタップした時のイベントハンドラを追加します。
イベントハンドラ内では、myAddressStackPanelオブジェクトを表示状態にします。
AddHandlerステートメントでmyAddressStackPanelをタップした時のイベントハンドラを追加します。イベントハンドラ内では、myAddressStackPanelオブジェクトを非表示状態にします。
ブール型のメンバ変数flagの値で条件分岐を行います。FlagがTrueであった場合、つまり、マウスを右クリックして表示される住所を入力するボックスに住所を入力して、「OK」ボタンがクリックされた場合、ということです。
病院の情報を取得するGoogleのWeb APIのURLを設定します。
locationには緯度と経度をカンマで区切って格納している、メンバ変数myAddressCoodinatePositionの値を指定します。
radiusには範囲(メートル単位)を指定します。ここでは1000を指定して1Kmとしています。
typesには、何を目的に検索するかを指定します。ここではHospitalを指定しています。
このtypesには|(パイプ)で区切って複数の値を指定できます。指定できる値の一覧は下記のURLを参照してください。
→ Google Places API/Supported Place Types
Sensorには場所の要求が位置センサー(GPS など)を使用してデバイスから来たかどうかを示す値を、trueまたはfalseで指定します。ここではtrueを指定しています。
languageにはjaをkeyにはGoogleより取得したAPIKeyを指定します。
「OK」ボタンがクリックされていない場合は、locationに現在位置の緯度と経度をカンマで区切った値を格納している、メンバ変数myCoodinatePositionの値を指定します。
検索結果は最大で20件までの情報を取得して表示します。これらの式を変数myUriに格納しておきます。TitleTextBlockにWatermarkTextBlockに入力された値、または現在位置の住所を格納しているmyAddress変数の値と、文字列「辺り」を連結して表示します。
新しいHttpClientのインスタンスmyHttpClientを作成します。
GetStringAsyncメソッドでGoogleの Web APIの式を格納しているmyUriの内容を読み取り、返ってくるXMLを変数resultHospitalに格納します。
XElement.ParseメソッドでresultHospitalが格納しているXMLの内容を、文字列として読み込みます。
Descendantsメソッドで、すべての
変数noの値を1ずつ増加させます。この値は、これから作成する「Webで表示」ボタンの、どのボタンがクリックされたかの判定に使用します。
新しいPushPinクラスのインスタンスhospitalPinオブジェクトを作成します。
背景色にCrimsonを指定し、Textプロパティに1ずつ増加する変数noの値を指定します。
ピンの表面に連番が表示されます。
地理的位置に関連付けされた緯度と経度で初期化された、新しいLocationクラスのインスタンスhospitalLocationオブジェクトを作成します。
MapLayerクラスのSetPositionメソッドで、マップレイヤー内に病院のピンの位置を設定します。myMapにAddメソッドで病院の位置にピンを追加します。
新しいStackPanelのインスタンスmyStackPanelオブジェクトを作成します。
Marginに5、背景色にNavyを指定し、非表示としておきます。
新しいTextBlockのインスタンスmyNameTextBlockを作成します。
文字色はRed、Widthに400、文字の回り込みは可、文字サイズは24、パディングは5と指定します。
Textプロパティには、文字列「病院名」と
新しいTextBlockのインスタンスaddressTextBlockを作成します。
文字色はBeige、Widthに400、文字の回り込みは可、文字サイズは20、パディングは5と指定します。
Textプロパティには、文字列「住所」と
新しいButtonのインスタンスmyButtonを作成します。
Contentプロパティに「Webで表示」と指定します。
Tagプロパティに変数noの値を指定します。どのButtonがクリックされたかの判定に使用します。
SetValueメソッドで、myStackPanelのZIndexPropertyに変数noの値を指定します。myStackPanelがPushPinよりも前面に表示されるようになります。
myStackPanelオブジェクトにAddメソッドで、myNameTextBlock、addressTextBlock、myButtonを追加します。
MapLayerクラスのSetPositionメソッドで、マップレイヤー内にmyStackPanelの位置を設定します。
myMapにAddメソッドでmyStackPanelを追加します。
AddHandlerステートメントで、病院の位置を表すピンがタップされた時のイベントハンドラを追加します。
myStackPanelオブジェクトが表示状態になり、病院の情報が表示されます。
AddHandlerステートメントで、病院の情報を表示した、myStackPanelがタップされた時のイベントハンドラを追加します。myStackPanelオブジェクトが非表示になります。
AddHandlerステートメントで、Buttonがクリックされた時のイベントハンドラを追加します。
バーを表す、クリックされたButtonのTagの値を変数indexに格納します。数値キャストしたindexの値をmyIndexに格納します。-1しているのは、ピンの表面に表示される連番は1~20まで表示されますが、XML要素のインデックスは0から始まるため-1しています。
変数mySendAddressにmyIndexに位置する
これらの値はUri.EscapeDataStringメソッドでエスケープ表現に変換しておきます。
Windows.System.Launcher.LaunchUriAsyncメソッドで、指定されたURIのURIスキーム名に関連付けられている既定のアプリケーション(この場合はIE10のブラウザ)を起動します。
先にも書きましたが、この記述方法では、ストアの審査には通りません。このようなやり方もある、という認識でお読みください。ストアの審査に通るにはWebBrowserコントロールを使って、その中に表示する方法を採るといいでしょう。
Try~Catch~End Tryで例外処理を行っています。例外が発生した場合はErrorShowプロシージャを実行します。
非同期処理で行われるためメソッドの先頭にAsyncを追加します。
Private Async Sub myGeolocator_PositionChanged(sender As Geolocator, args As PositionChangedEventArgs) Dim myLatitude As String = String.Empty Dim myLongitude As String = String.Empty Dim no As Integer = 0 Await myCoreDispacher.RunAsync(CoreDispatcherPriority.Normal, Async Sub() Dim pos As Geoposition = args.Position myMap.Children.Clear() Dim myPin As New Pushpin Dim myLocation As Location myPin.Background = New SolidColorBrush(Colors.Navy) If flag = True Then myLocation = New Location(CDbl(myAddressLatitude), CDbl(myAddressLongitude)) Else myLocation = New Location(CDbl(pos.Coordinate.Latitude), CDbl(pos.Coordinate.Longitude)) End If MapLayer.SetPosition(myPin, myLocation) myMap.Children.Add(myPin) myMap.SetView(myLocation, 16) If flag = True Then myAddressUri = String.Format("http://reverse.search.olp.yahooapis.jp/OpenLocalPlatform/V1/reverseGeoCoder?lat={0}&lon={1}&appid={2}", myAddressLatitude, myAddressLongitude, AppID) Else myAddressUri = String.Format("http://reverse.search.olp.yahooapis.jp/OpenLocalPlatform/V1/reverseGeoCoder?lat={0}&lon={1}&appid={2}", pos.Coordinate.Latitude, pos.Coordinate.Longitude, AppID) End If Dim myAddressHttpClient As New HttpClient Dim myCurrentAddress As String = String.Empty Dim xmldoc As XElement Try myCurrentAddress = Await myAddressHttpClient.GetStringAsync(myAddressUri) myCurrentAddress = myCurrentAddress.Replace("<YDF firstResultPosition=" & ChrW(34) & "1" & ChrW(34) & " totalResultsAvailable=" & ChrW(34) & "1" & ChrW(34) & " totalResultsReturned=" & ChrW(34) & "1" & ChrW(34) & " xmlns=" & ChrW(34) & "http://olp.yahooapis.jp/ydf/1.0" & ChrW(34) & ">", "<YDF>") xmldoc = XElement.Parse(myCurrentAddress) Dim myAddress = xmldoc.Descendants("Address").Value Dim myAddressStackPanel As New StackPanel myAddressStackPanel.Margin = New Thickness(5) myAddressStackPanel.Background = New SolidColorBrush(Colors.Navy) myAddressStackPanel.Visibility = Xaml.Visibility.Collapsed Dim myAddressTextBlock As New TextBlock myAddressTextBlock.Foreground = New SolidColorBrush(Colors.Red) myAddressTextBlock.FontSize = 24 myAddressTextBlock.Padding = New Thickness(5) If flag = False Then myAddressTextBlock.Text = myAddress & " 辺り" Else myAddressTextBlock.Text = WatermarkTextBox.Text & " 辺り" End If myAddressStackPanel.Children.Add(myAddressTextBlock) myAddressStackPanel.SetValue(Canvas.ZIndexProperty, 20) If flag = False Then MapLayer.SetPosition(myAddressStackPanel, New Location(CDbl(pos.Coordinate.Latitude), CDbl(pos.Coordinate.Longitude))) Else MapLayer.SetPosition(myAddressStackPanel, New Location(CDbl(myAddressLatitude), CDbl(myAddressLongitude))) End If myMap.Children.Add(myAddressStackPanel) AddHandler myPin.Tapped, Sub() myAddressStackPanel.Visibility = Xaml.Visibility.Visible End Sub AddHandler myAddressStackPanel.Tapped, Sub() myAddressStackPanel.Visibility = Xaml.Visibility.Collapsed End Sub If flag = True Then myUri = String.Format("https://maps.googleapis.com/maps/api/place/nearbysearch/xml?location={0}&radius=1000&types=hospital&sensor=true&language=ja&key={1}", myAddressCoodinatePosition, GoogleAppID) (B) TitleTextBlock.Text = WatermarkTextBox.Text & " 辺りの1Km範囲内の病院を表示" Else myCoodinatePosition = pos.Coordinate.Latitude & "," & pos.Coordinate.Longitude myUri = String.Format("https://maps.googleapis.com/maps/api/place/nearbysearch/xml?location={0}&radius=1000&types=hospital&sensor=true&language=ja&key={1}", myCoodinatePosition, GoogleAppID) TitleTextBlock.Text = myAddress & " 辺りの1Km範囲内の病院を表示" End If Dim myHttpClient As New HttpClient Dim resultHospital = Await myHttpClient.GetStringAsync(myUri) Dim httpDoc As XElement = XElement.Parse(resultHospital) For Each result In From c In httpDoc.Descendants("result") Select c no += 1 Dim hospitalPin As New Pushpin hospitalPin.Background = New SolidColorBrush(Colors.Crimson) hospitalPin.Text = no.ToString myLongitude = result.Element("geometry").Element("location").Element("lng").Value myLatitude = result.Element("geometry").Element("location").Element("lat").Value Dim hospitalLocation = New Location(CDbl(myLatitude), CDbl(myLongitude)) MapLayer.SetPosition(hospitalPin, hospitalLocation) myMap.Children.Add(hospitalPin) Dim myStackPanel As New StackPanel myStackPanel.Margin = New Thickness(5) myStackPanel.Background = New SolidColorBrush(Colors.Navy) myStackPanel.Visibility = Xaml.Visibility.Collapsed Dim myNameTextBlock As New TextBlock myNameTextBlock.Foreground = New SolidColorBrush(Colors.Red) myNameTextBlock.Width = 400 myNameTextBlock.TextWrapping = TextWrapping.Wrap myNameTextBlock.FontSize = 24 myNameTextBlock.Padding = New Thickness(5) myNameTextBlock.Text = "【病院名】=" & result.Element("name").Value Dim addressTextBlock As New TextBlock addressTextBlock.Foreground = New SolidColorBrush(Colors.Beige) addressTextBlock.Width = 400 addressTextBlock.TextWrapping = TextWrapping.Wrap addressTextBlock.FontSize = 20 addressTextBlock.Padding = New Thickness(5) addressTextBlock.Text = "【住所】=" & result.Element("vicinity").Value Dim myButton As New Button With myButton .Content = "Webで表示" .Tag = no.ToString End With myStackPanel.SetValue(Canvas.ZIndexProperty, no) myStackPanel.Children.Add(myNameTextBlock) myStackPanel.Children.Add(addressTextBlock) myStackPanel.Children.Add(myButton) MapLayer.SetPosition(myStackPanel, New Location(CDbl(myLatitude), CDbl(myLongitude))) myMap.Children.Add(myStackPanel) AddHandler hospitalPin.Tapped, Sub() myStackPanel.Visibility = Xaml.Visibility.Visible End Sub AddHandler myStackPanel.Tapped, Sub() myStackPanel.Visibility = Xaml.Visibility.Collapsed End Sub AddHandler myButton.Click, Sub(mySender As Object, myArgs As RoutedEventArgs) Dim index = DirectCast(mySender, Button).Tag Dim myIndex = CInt(index) - 1 Dim mySendAddress = "http://www.bing.com/search?q=" & Uri.EscapeDataString(httpDoc.Descendants("vicinity")(myIndex).Value & " " & httpDoc.Descendants("name")(myIndex).Value) Await Windows.System.Launcher.LaunchUriAsync(New Uri(mySendAddress)) End Sub Next Catch ErrorShow() End Try End Sub) End Sub
WatermarkTextBoxに住所を入力して「OK」ボタンをタップした際に、エラーが発生した時の処理
警告メッセージを表示して、現在位置の住所と病院の位置を表示します。
Private Async Sub ErrorShow() Dim message As New MessageDialog("住所が不正です。現在位置を表示します。") Await message.ShowAsync resetButton_Click(Nothing, Nothing) End Sub
現在位置近くの病院を検索するプログラム