Yahoo!ローカルサーチAPIを使って地図上にランドマークを表示させるプログラムを作る

2013年9月20日(金)
薬師寺 国安

ページがアクティブになった時の処理

XElement.LoadメソッドでXML文書ファイル(Castle.xml)を読み込みます。

Descendantsメソッドで、全ての子孫要素の内容を、変数resultに格納しながら、以下の処理を繰り返します。

Imageの新しいインスタンスmyImageオブジェクトを作成します。Widthに28、Heightに36と指定し、SourceプロパティにソリューションエクスプローラーのImageフォルダ内にあるお城の画像を指定します。

変数myLatitudeに(緯度)要素の値を、変数myLongitudeに(経度)の値を格納します。

地理的位置に関連付けされた緯度と経度で初期化された、新しいLocationクラスのインスタンスmyLocationオブジェクトを作成します。Locationクラスは、地図上の場所の標高と座標値を含むクラスです。

MapLayerクラスのSetPositionメソッドで、マップレイヤー内にmyImageオブジェクトの位置を設定します。
myMapにAddメソッドでmyImageオブジェクトを追加します。
SetViewメソッドで、指定された中心部に位置、ズーム レベル、方位、およびピッチにマップビューを設定します。この場合myLocationの位置に8レベルでズームインします。

現在位置の住所を取得するWeb APIのURLを設定します。
latに変数myLatitudeの値、lonには変数myLongitudeの値、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のインスタンスmyStackPanelオブジェクトを作成します。背景色にNavy、Marginに5を指定して余白を設けます。最初の状態では非表示としておきます。
SetValueメソッドにZIndexPropertyを指定し、値に1ずつ加算されるメンバ変数noを指定します。お城の画像アイコンよりもStackPanelが前面に表示されます。

新しいTextBlockのインスタンスmyAddressTextBlockオブジェクトを作成します。
文字サイズに22、文字色にCrimson、Widthに400、文字の回り込みを可、Textプロパティには変数myAddressの値と文字列「辺り」を連結して指定します。パディングには5を指定します。

新しいTextBlocのインスタンスmyCastleNameTextBlockオブジェクトを作成します。
文字サイズに24、文字色にGold、Textプロパティに要素の値を指定します。パディングには5を指定します。

新しいButtonのインスタンスmyButtonオブジェクトを作成します。
Contentプロパティに「Webで検索」と指定し、Tagプロパティにメンバ変数noの値を文字列にキャストして指定します。どのボタンがクリックされたかの判定に使用します。

MapLayerクラスのSetPositionメソッドで、マップレイヤー内にmyStackPanelオブジェクトの位置を設定します。
myMapにAddメソッドでmyStackPanelオブジェクトを追加します。メンバ変数noを1ずつ加算します。
AddHandlerステートメントで、myImageがタップされた時のイベントハンドラを追加します。イベントハンドラ内では、以下の処理を行います。

myStackPanelオブジェクトを表示状態にします。

AddHandlerステートメントで、myStackPanelがタップされた時のイベントハンドラを追加します。イベントハンドラ内では、以下の処理を行います。

myStackPanelオブジェクトを非表示状態にします。

AddHandlerステートメントで、myButtonがタップされた時のイベントハンドラを追加します。イベントハンドラ内では、以下の処理を行います。

myFrameを表示状態にします。

Buttonの情報を保持しているmySenderオブジェクトをDirectCastでButtonにキャストして、そのTagプロパティの値を取得し、変数Indexに格納します。Indexの値を数値に変換してmyIndexに格納します。

全ての子孫要素のmyIndexに位置する要素の値を取得して、変数castleNameに格納します。

Navigateメソッドで、変数castleNameの値を引数にWebBrowserPageに遷移します。

  Protected Overrides Async Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
    xmldoc = XElement.Load("Castle.xml")
    For Each result In From c In xmldoc.Descendants("Info") Select c
      Dim myImage As New Image
      With myImage
        .Width = 28
        .Height = 36
        .Source = New BitmapImage(New Uri("ms-appx:///Image/城.png"))
      End With
      Dim myLatitude = result.Element("Latitude").Value
      Dim myLongitude = result.Element("Longitude").Value
      Dim myLocation = New Location(CDbl(myLatitude), CDbl(myLongitude))
      MapLayer.SetPosition(myImage, myLocation)
      myMap.Children.Add(myImage)
      myMap.SetView(myLocation, 8)
 
      Dim myAddressUri As String = String.Format("http://reverse.search.olp.yahooapis.jp/OpenLocalPlatform/V1/reverseGeoCoder?lat={0}&lon={1}&appid={2}", CDbl(myLatitude), CDbl(myLongitude), 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 doc As XElement = XElement.Parse(myCurrentAddress)
      Dim myAddress = doc.Descendants("Address").Value
 
      Dim myStackPanel As New StackPanel
      With myStackPanel
        .Background = New SolidColorBrush(Colors.Navy)
        .Margin = New Thickness(5)
        .Visibility = Xaml.Visibility.Collapsed
        .SetValue(Canvas.ZIndexProperty, no)
      End With
 
      Dim myAddressTextBlock As New TextBlock
      
      With myAddressTextBlock
        .FontSize = 22
        .Foreground = New SolidColorBrush(Colors.Crimson)
        .Width = 400
        .TextWrapping = TextWrapping.Wrap
        .Text = myAddress& "辺り"
        .Padding = New Thickness(5)
      End With
 
      Dim myCastleNameTextBlock As New TextBlock
      
      With myCastleNameTextBlock
        .FontSize = 24
        .Foreground = New SolidColorBrush(Colors.Gold)
        .Text = result.Element("Name").Value
        .Padding = New Thickness(5)
      End With
 
      Dim myButton As New Button
      With myButton
        .Content = "Webで検索"
        .Tag = no.ToString
      End With

      myStackPanel.Children.Add(myAddressTextBlock)
      myStackPanel.Children.Add(myCastleNameTextBlock)
      myStackPanel.Children.Add(myButton)

      MapLayer.SetPosition(myStackPanel, New Location(CDbl(myLatitude), CDbl(myLongitude)))
      myMap.Children.Add(myStackPanel)
      no += 1
      AddHandlermyImage.Tapped, Sub()

                                   myStackPanel.Visibility = Xaml.Visibility.Visible
                                End Sub
      
      AddHandlermyStackPanel.Tapped, Sub()
                                   myStackPanel.Visibility = Xaml.Visibility.Collapsed
                                End Sub

      AddHandlermyButton.Click, Sub(mySender As Object, myArgs As RoutedEventArgs)
                                    myFrame.Visibility = Xaml.Visibility.Visible
                                    Dim Index = DirectCast(mySender, Button).Tag
                                    Dim myIndex = CInt(Index)
                                    Dim castleName = xmldoc.Descendants("Info")(myIndex).Element("Name").Value
                                    myFrame.Navigate(GetType(WebBrowserPage), castleName)
                                End Sub
    Next
  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をダブルクリックしてリスト5のコードを記述します。

ロジックコードを記述する

リスト5 (WebBrowser.xaml.vb)

Public NotInheritable Class WebBrowserPage
  Inherits Page

ページがアクティブになった時の処理

MainPage.xamlから送られた値は、e.Parameterで取得できます。これはObject型であるため、DirectCastで文字列にキャストして変数castleNameに格納します。
変数myUriに、ウィキペディアのUriに、引数castleNameをエスケープ表現に変換して指定し、格納しておきます。

WebBrowserのSourceプロパティに、myUriで初期化された新しいUriのインスタンスを指定します。これで、指定したお城に関するウィキペディアの情報が表示されます。

  Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
    Dim castleName = DirectCast(e.Parameter, String)
    Dim myUri = String.Format("http://ja.wikipedia.org/wiki/{0}", Uri.EscapeDataString(castleName))
    WebBrowser1.Source = New Uri(myUri, UriKind.Absolute)
  End Sub
End Class

アイコンの作成

詳細については、「自分の現在位置を取得して表示するサンプルプログラム」の記事を参照してください。

今回はここまでです。ありがとうございました。

  • 地図上にランドマークを表示させるプログラム

薬師寺国安事務所

薬師寺国安事務所代表。Visual Basic プログラミングと、マイクロソフト系の技術をテーマとした、書籍や記事の執筆を行う。
1950年生まれ。事務系のサラリーマンだった40歳から趣味でプログラミングを始め、1996年より独学でActiveXに取り組む。1997年に薬師寺聖とコラボレーション・ユニット PROJECT KySS を結成。2003年よりフリーになり、PROJECT KySS の活動に本格的に参加、.NETやRIAに関する書籍や記事を多数執筆する傍ら、受託案件のプログラミングも手掛ける。Windows Phoneアプリ開発を経て、現在はWindows ストア アプリを多数公開中

Microsoft MVP for Development Platforms - Client App Dev (Oct 2003-Sep 2012)。Microsoft MVP for Development Platforms - Windows Phone Development(Oct 2012-Sep 2013)。Microsoft MVP for Development Platforms - Client Development(Oct 2013-Sep 2014)。Microsoft MVP for Development Platforms-Windows Platform Development (Oct 2014-Sep 2015)。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています