現在位置の近くにある宿を検索するサンプルプログラム
![](https://thinkit.co.jp/sites/default/files/styles/main_image_730x/public/main_images/4145_0_7.png?itok=0cJzeyOh)
場所が変更された時の処理
文字列型の新しいリストである、HotelUrlオブジェクトを作成します。
myCoreDispacher.RunAsyncメソッドで、イベントディスパッチャを実行し、ディスパッチされたイベントの結果を非同期に返します。ディスパッチャの優先順位は標準のNormalを指定しています。
緯度と経度のデータや都市の住所データを含めることができるGeoPositionクラス型の変数psoを宣言し、PositionChangedイベントに関連付けられた場所データを取得します。
Clearメソッドで、地図内を一度クリアしておきます。
新しいPushpinクラスのインスタンスmyPinオブジェクトを作成します。ピンの背景色をCrimsonに指定します。
地理的位置に関連付けされた緯度と経度で初期化された、新しいLocationクラスのインスタンスmyLocationオブジェクトを作成します。Locationクラスは、地図上の場所の標高と座標値を含むクラスです。
MapLayerクラスのSetPositionメソッドで、マップレイヤー内にピンの位置を設定します。myMapにAddメソッドでピンを追加します。
SetViewメソッドで、指定された中心部に位置、ズーム レベル、方位、およびピッチにマップビューを設定します。この場合myLocationの位置に16レベルでズームインします。
現在位置の住所を取得するWeb APIのURLを設定します。latに現在の緯度、lonには現在の経度、appidにはYahooのキーを指定し、変数myAddressUriに格納しておきます。
新しいHttpClientクラスのインスタンスmyAddressHttpClientオブジェクトを作成します。HttpClientクラスは、URIで識別されるリソースにHTTP要求を送信し、そのリソースからHTTP応答を受信するための基本クラスが含まれています。
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と文字列「辺り」を連結して指定します。
myStackPanelオブジェクトにAddメソッドで住所の設定された、myAddressTextBlockを追加します。
SetValueメソッドでmyAddressStackPanelのZIndexPropertyに20を指定します。
myAddressStackPaneが前面に表示されます。
MapLayerクラスのSetPositionメソッドで、マップレイヤー内にmyAddressStackPanelオブジェクトの位置を設定します。
MapLayerクラスは、地図上の要素の位置を保持しているマップレイヤーを表すクラスです。
myMapにAddメソッドでmyAddressStackPanelオブジェクトを追加します。
myAddressStackPanelは最初の状態では非表示になっているのでわかりませんが、ピンの位置にStackPanelオブジェクトが表示されることになります。
AddHandlerステートメントでピンをタップした時のイベントハンドラを追加します。イベントハンドラ内では、myAddressStackPanelオブジェクトを表示状態にします。
AddHandlerステートメントでmyAddressStackPanelをタップした時のイベントハンドラを追加します。
イベントハンドラ内では、myAddressStackPanelオブジェクトを非表示状態にします。
宿の情報を取得する「じゃらん」のWeb APIのURLを設定します。keyにはAPIキーをxとyには経度と緯度を指定します。pos.Coordinate.Longitude とpos.Coordinate.Latitudeで取得する経度と緯度はdegree形式です。
じゃらんのWeb APIに指定する経度と緯度はミリ秒である必要があるため、3600000を乗算して変換しています。Rangeには1を指定して、1Km範囲内としています。countには20を指定して、最大で20件までの情報を取得します。
これらの式を変数myUriに格納しておきます。
新しいHttpClientのインスタンスmyHttpClientを作成します。
GetStringAsyncメソッドで「じゃらん」の Web APIの式を格納しているmyUriの内容を読み取り、返ってくるXMLを変数resultAddressに格納します。
返ってきたXMLには、不要な名前空間が付いていますので、Replace関数で
XElement.ParseメソッドでresultAddressが格納しているXMLの内容を、文字列として読み込みます。
Descendantsメソッドで、すべての
変数noの値を1ずつ増加させます。この値は、これから作成する「Webで表示」ボタンの、どのボタンがクリックされたかの判定に使用します。
文字列型のリストであるHotelUri変数にAddメソッドで、
新しいPushPinクラスのインスタンスhotelPinオブジェクトを作成します。背景色にNavyを指定し、Textプロパティに1ずつ増加する変数noの値を指定します。ピンの表面に連番が表示されます。
地理的位置に関連付けされた緯度と経度で初期化された、新しいLocationクラスのインスタンスmyLocationオブジェクトを作成します。
取得される
MapLayerクラスのSetPositionメソッドで、マップレイヤー内に宿のピンの位置を設定します。myMapにAddメソッドで宿の位置にピンを追加します。
新しいStackPanelのインスタンスmyStackPanelオブジェクトを作成します。Marginに5、背景色にPinkを指定し、非表示としておきます。
新しいTextBlockのインスタンスmyTextBlockを作成します。
Widthに400、文字の回り込みは可、文字色はRed、文字サイズは24、パディングは5と指定します。
Textプロパティには、文字列「宿名」と
新しいTextBlockのインスタンスmyAddressTextBlockを作成します。
Widthに400、文字の回り込みは可、文字色はNavy、文字サイズは20、パディングは5と指定します。
Textプロパティには、文字列「住所」と
郵便番号付き住所が表示されます。
新しいTextBlockのインスタンスdescriptionTextBlockを作成します。
Widthに400、文字の回り込みは可、文字色はNavy、文字サイズは20、パディングは5と指定します。
Textプロパティには、文字列「概要」と
宿の概要が表示されます。
新しいTextBlockのインスタンスstationTextBlockを作成します。
Widthに400、文字の回り込みは可、文字色はNavy、文字サイズは20、パディングは5と指定します。
Textプロパティには、文字列「最寄り駅」と
宿のへの最寄り駅名が表示されます。
新しいButtonのインスタンスmyButtonを作成します。
Contentプロパティに「Webで表示」と指定します。枠線の色と文字色をBlackに指定します。
Tagプロパティに変数noの値を指定します。どのButtonがクリックされたかの判定に使用します。
SetValueメソッドで、myStackPanelのZIndexPropertyに変数noの値を指定します。myStackPanelがPushPinよりも前面に表示されるようになります。
myStackPanelオブジェクトにAddメソッドで、myTextBlock、addressTextBlock、descriptionTextBlock、stationTextBlock、myButtonを追加します。
MapLayerクラスのSetPositionメソッドで、マップレイヤー内にmyStackPanelの位置を設定します。
myMapにAddメソッドでmyStackPanelを追加します。
AddHandlerステートメントで、宿の位置を表すピンがタップされた時のイベントハンドラを追加します。myStackPanelオブジェクトが表示状態になり、宿の情報が表示されます。
AddHandlerステートメントで、宿の情報を表示した、myStackPanelがタップされた時のイベントハンドラを追加します。myStackPanelオブジェクトが非表示になります。
AddHandlerステートメントで、Buttonがクリックされた時のイベントハンドラを追加します。
myFrameを表示状態にし、クリックされたButtonのTagの値を変数myIndexに格納します。
宿のリスト情報を持っているHotelUrl内で変数myIndex-1に位置するホテルのURLを変数sendUriに格納します。
myIndexを-1しているのは、インデックスが0から始まるためです。sendUriを引数にWeBrowserPageに遷移します。
非同期処理で行われるためメソッドの先頭にAsyncを追加します。
Private Async Sub myGeolocator_PositionChanged(sender As Geolocator, args As PositionChangedEventArgs) Dim HotelUrl As New List(Of String) 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 myPin.Background = New SolidColorBrush(Colors.Crimson) Dim myLocation = New Location(CDbl(pos.Coordinate.Latitude), CDbl(pos.Coordinate.Longitude)) MapLayer.SetPosition(myPin, myLocation) myMap.Children.Add(myPin) myMap.SetView(myLocation, 16) Dim myAddressUri As String = String.Format("http://reverse.search.olp.yahooapis.jp/OpenLocalPlatform/V1/reverseGeoCoder?lat={0}&lon={1}&appid={2}", pos.Coordinate.Latitude, pos.Coordinate.Longitude, AppID) Dim myAddressHttpClient As New HttpClient Dim 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>") Dim xmldoc As XElement = 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) myAddressTextBlock.Text = myAddress & " 辺り" myAddressStackPanel.Children.Add(myAddressTextBlock) myAddressStackPanel.SetValue(Canvas.ZIndexProperty, 20) MapLayer.SetPosition(myAddressStackPanel, New Location(CDbl(pos.Coordinate.Latitude), CDbl(pos.Coordinate.Longitude))) 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 Dim myUri As String = String.Format("http://jws.jalan.net/APIAdvance/HotelSearch/V1/?key={0}&x={1}&y={2}&range=1.0&count=20", HotelAppID, CInt(CDbl(pos.Coordinate.Longitude * 3600000)), CInt(CDbl(pos.Coordinate.Latitude * 3600000))) Dim myHttpClient As New HttpClient Dim resultAddress = Await myHttpClient.GetStringAsync(myUri) resultAddress = resultAddress.Replace("<Results xmlns=" & ChrW(34) & "jws" & ChrW(34) & ">", "<Results>") Dim httpDoc As XElement = XElement.Parse(resultAddress) For Each result In From c In httpDoc.Descendants("Hotel") Select c no += 1 HotelUrl.Add(result.Element("HotelDetailURL").Value) Dim hotelPin As New Pushpin hotelPin.Background = New SolidColorBrush(Colors.Navy) hotelPin.Text = no.ToString myLatitude = result.Element("Y").Value myLongitude = result.Element("X").Value Dim hotelLocation = New Location(CDbl(CDbl(myLatitude) / 3600000), CDbl(CDbl(myLongitude) / 3600000)) MapLayer.SetPosition(hotelPin, hotelLocation) myMap.Children.Add(hotelPin) Dim myStackPanel As New StackPanel myStackPanel.Margin = New Thickness(5) myStackPanel.Background = New SolidColorBrush(Colors.Pink) myStackPanel.Visibility = Xaml.Visibility.Collapsed Dim myTextBlock As New TextBlock myTextBlock.Width = 400 myTextBlock.TextWrapping = TextWrapping.Wrap myTextBlock.Foreground = New SolidColorBrush(Colors.Red) myTextBlock.FontSize = 24 myTextBlock.Padding = New Thickness(5) myTextBlock.Text = "【宿名】=" & result.Element("HotelName").Value Dim addressTextBlock As New TextBlock addressTextBlock.Width = 400 addressTextBlock.TextWrapping = TextWrapping.Wrap addressTextBlock.Foreground = New SolidColorBrush(Colors.Navy) addressTextBlock.FontSize = 20 addressTextBlock.Padding = New Thickness(5) addressTextBlock.Text = "【住所】=" & result.Element("PostCode").Value & " " & result.Element("HotelAddress").Value Dim descriptionTextBlock As New TextBlock descriptionTextBlock.Width = 400 descriptionTextBlock.TextWrapping = TextWrapping.Wrap descriptionTextBlock.Foreground = New SolidColorBrush(Colors.Navy) descriptionTextBlock.FontSize = 20 descriptionTextBlock.Padding = New Thickness(5) descriptionTextBlock.Text = "【概要】=" & result.Element("HotelCaption").Value Dim stationTextBlock As New TextBlock stationTextBlock.Width = 400 stationTextBlock.TextWrapping = TextWrapping.Wrap stationTextBlock.Foreground = New SolidColorBrush(Colors.Navy) stationTextBlock.FontSize = 20 stationTextBlock.Padding = New Thickness(5) stationTextBlock.Text = "【最寄り駅】=" & result.Element("AccessInformation").Value Dim myButton As New Button myButton.Content = "Webで表示" myButton.BorderBrush = New SolidColorBrush(Colors.Black) myButton.Foreground = New SolidColorBrush(Colors.Black) myButton.Tag = no.ToString myStackPanel.SetValue(Canvas.ZIndexProperty, no) myStackPanel.Children.Add(myTextBlock) myStackPanel.Children.Add(addressTextBlock) myStackPanel.Children.Add(descriptionTextBlock) myStackPanel.Children.Add(stationTextBlock) myStackPanel.Children.Add(myButton) MapLayer.SetPosition(myStackPanel, New Location(CDbl(CDbl(myLatitude) / 3600000), CDbl(CDbl(myLongitude) / 3600000))) myMap.Children.Add(myStackPanel) AddHandler hotelPin.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) myFrame.Visibility = Xaml.Visibility.Visible Dim myIndex As Integer = CInt(DirectCast(mySender, Button).Tag.ToString) Dim sendUri = HotelUrl(myIndex - 1) myFrame.Navigate(GetType(WebBrowserPage), sendUri) End Sub Next End Sub) End Sub
Geolocatorの機能が変更された時に発生する処理
Geolocatorオブジェクトの更新後の状態を、変数positionStatusで参照します。
Select Case文で条件分岐を行います。場所データを提供するGeolocatorオブジェクトがDisabledであった場合、つまり、場所プロバイダーが無効であった場合は、RunAsyncメソッドでディスパッチされたイベントの結果を非同期に返します。ディスパッチャの優先順位は標準のNormalを指定しています。
この場合は、「位置情報を取得できません。GPSおよび位置情報取得可能なPCでお試しください。」というメッセージボックスを表示させ、処理を抜けます。
非同期で行われるためメソッドの先頭にAsyncを追加します。
Private Async Sub myGeolocator_StatusChanged(sender As Geolocator, args As StatusChangedEventArgs) Dim positionStatus = args.Status Select Case positionStatus Case Windows.Devices.Geolocation.PositionStatus.Disabled Await myCoreDispacher.RunAsync(CoreDispatcherPriority.Normal, Async Sub() TitleTextBlock.Text = "位置情報を取得できません。GPSおよび位置情報取得可能なPCでお試しください。" Exit Sub End Sub) End Select End Sub
戻る(←)アイコンがクリックされた時の処理
myFrameを非表示にします。
Private Sub backButton_Click(sender As Object, e As RoutedEventArgs) Handles backButton.Click myFrame.Visibility = Xaml.Visibility.Collapsed End Sub End Class
次に、ソリューションエクスプローラー内のWebBrowserPage.xamlを展開して表示される、WebBrowserPage.xaml.vbをダブルクリックしてリスト4のコードを記述します。
ロジックコードを記述する
リスト4 (WebBrowserPage.xaml.vb)
Option Strict On Public NotInheritable Class WebBrowserPage Inherits Page
ページがアクティブになった時の処理
MainPage.xamlから送られた値を、e.Parameterで受け取ります。これはObject型であるため、DirectCastで文字列にキャストして、変数myUriに格納しておきます。WebBrowserのSourceプロパティに、myUriで初期化された、新しいUriを指定します。これで、ブラウザ内に、宿の情報が表示されます。
Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs) Dim myUri = DirectCast(e.Parameter, String) WebBrowser1.Source = New Uri(myUri, UriKind.Absolute) End Sub End Class
解説では触れておりませんが、このプロジェクトにはAboutControl.xamlとAboutControl.xaml.vbが存在しています。これは「このアプリについて」をチャームから表示させるものです。このテンプレートはユーザーコントロールで作成しています。
このコードはMicrosoft様よりMVP向けに提供されたもので、私が書いたコードではありませんので、このなかでは触れていません。確認したい場合は、実行してチャームを表示させてみてください。「このアプリについて」の項目があります。これをタップするとAboutControl.xamlが表示されます。
また「プライバシーポリシー」を表示する項目もあります。このコードはApp.xaml.vb内に記述しています。これもMicrosoft様より提供されたもので、ここでは触れておりません。このサンプル以降もAboutControlやAppに関しては解説していませんので、興味のある方はソースコードを見て勉強してください。
今回の連載のサンプルは、実際にWindows ストアに申請して認定されたアプリのサンプルですので、上記のコードが含まれています。「プライバシーポリシー」に関しては、「これでリジェクトされない!Windowsストアアプリ申請のポイント」で触れています。
アイコンの作成
ソリューションエクスプローラーのAssetsフォルダ内には、4つのpngファイルが入っています(表1)。
表1:Assetsフォルダ内に入っているpngファイルの種類
ファイル名 | サイズ |
---|---|
Logo.png | 150×150 |
SmallLogo.png | 30×30 |
SplashScreen.png | 620×300 |
StoreLogo.png | 50×50 |
表1の画像はデフォルトでは、□に×の画像になっています。ストアの審査では、このままの画像では審査に受かりませんので、4種類のアイコンを作る必要があります。このサンプルは実際にストアで審査の通ったアプリですので、Assetsフォルダ内には筆者の作成したアイコンが収められています。見てみてください。
SplashScreen.pngはアプリを起動した際に、一瞬最初に表示される画像です。スタート画面にピン止めされる画像はデフォルトでは150×150のLogo.pngが使用されます。これを長方形の画像にしたい場合は、310×150サイズのpng画像を作成し、WideLogo.png(任意の名前でOK)としてAssetsフォルダに追加しておきます。
詳細については、前回の記事を参照してください。
今回はここまでです。ありがとうございました。
筆者からのお知らせ
筆者はWindowsストアでアプリを公開しています。チャームの検索からWindowsストアを選択して、検索欄に、kuniyasuまたはYakushijiKuniyasuと入力すると、公開されているアプリの一覧が表示されます。上記はどちらも私のアカウントですので、興味のある方は是非ダウンロードして使ってみてください。
現在位置の近くにある宿を検索するプログラム