指定した目的地までの距離をキャラクターが教えてくれるアプリを作ろう
次に、ソリューション・エクスプローラー内のMainWindow.xamlを展開して表示される、MainWindow.xaml.vbをダブルクリックして、リスト7のコードを記述します。
ロジックコードを記述する
リスト7 (MainWindow.xaml.vb)
最新の HTTP アプリケーションのプログラミング インターフェイスを提供するクラスの含まれる、System.Net.Http 名前空間をインポートします。
Imports System.Net.Http
Bing Mapsの使用を可能にするクラスの含まれる、Bing Maps名前空間をインポートします。
Imports Bing.Maps Imports Windows.UI Imports Windows.UI.Popups
装飾的なレンダリング、またはコントロールの非対話形式部分の合成での使用を目的とした、基本的な図形を定義するクラスの含まれるWindows.UI.Xaml.Shapes名前空間をインポートします。
Imports Windows.UI.Xaml.Shapes Public NotInheritable Class MainPage Inherits Page
定数メンバー変数YahooIDをヤフーから取得したIDで初期化します。取得方法は後述します。
Const YahooAppID As String = "ヤフーより取得したID" Private myStartLatitude As String Private myStartLongitude As String Private myMiddleStartLatitude As String Private myMiddleStartLongitude As String Private myArriveLatitude As String Private myArriveLongitude As String
地図上にPoylineを描画するMapPolylineクラス型のメンバー変数myMapPolyline、myMapPolyline2、を宣言します。
Private myMapPolyline As MapPolyline Private myMapPolyline2 As MapPolyline
マップ上の位置の高度と座標値が含まれるLocationクラスのメンバー変数、firstLocation、middleLocation、endLocationを宣言します。
Private firstLocation As Location Private middleLocation As Location Private endLocation As Location
キャラクタに喋らせる内容を格納するメンバー変数readingTextを宣言します。
Private readingText As String
「出発点」の[決定]ボタンがタップされた時の処理
「出発点」にデータが入力されていない場合は、メッセージを表示して処理を抜けます。データが入力されていた場合は、以下の処理を行います。
GeocodingのWeb APIを使って、startTextBoxに入力された住所からレスポンスデータを受け取り、変数startMyUriに格納します。GeocodingのWeb APIについては、下記のURLを参照してください。
> Geocoding Web API
新しいHttpClientのインスタンスmyHttpClientオブジェクトを作成します。GetStringAsyncメソッドでstartMyUriから返される応答本体を文字列として受け取り、myResponse変数に格納します。
XElement.ParseメソッドでmyResponseデータを文字列として読み取り、xmldocで参照します。
メンバー変数myStartLatitudeに
メンバー変数myStartLongitudeに
PushPinクラスの新しいインスタンスmyPinオブジェクトを作成します。背景色をCrimsonとします。
新しいStackPanelのインスタンスmyStackPanelオブジェクトを作成します。Marginに「5」を指定して余白を設けます。背景色には「Pink」を指定します。
新しいTextBlockのインスタンス、startTextBlockオブジェクトを作成します。
文字サイズは「20」、パディングは「5」、TextプロパティにはstartTextBlock.Textに入力されたデータと文字列【出発点】を連結して表示します。文字色には「Red」を指定します。
myStackPanelオブジェクトにstartTextBlockを追加します。
新しいLocationオブジェクトを、メンバー変数myStartLatitudeとmyStartLongitudeで初期化し、メンバー変数firestLocationで参照します。
myStatStackPanelオブジェクトにAddメソッドでstartTextBlockの値を追加します。
MapLayer.SetPosition(myStartStackPanel, firstLocation)と指定すると、出発点の位置にピンが立ち、住所名が表示されます。
myMapにmyStackPanelオブジェクトを追加します。
MapLayer.SetPosition(myPin, firstLocation)と指定して、myMapにmyPinオブジェクトを指定します。
SetViewメソッドでfirstLocationの位置にズームインします。
「中継点」の[決定]ボタンの使用を可能にします。代わりに[出発点]のボタンの使用を不可とします。
非同期処理で行われるため、メソッドの先頭にAsyncを追加します。
Private Async Sub OkButton_Click(sender As Object, e As RoutedEventArgs) Handles OkButton.Click If startTextBox.Text = String.Empty Then Dim message As New MessageDialog("出発点を入力してください。") Await message.ShowAsync Exit Sub End If myStartLatitude = String.Empty myStartLongitude = String.Empty Dim startMyUri As String = String.Format("http://www.geocoding.jp/api/?v=1.1&q={0}", Uri.EscapeDataString(startTextBox.Text)) Dim myHttpClient As New HttpClient Dim myResponse = Await myHttpClient.GetStringAsync(New Uri(startMyUri, UriKind.Absolute)) Dim xmldoc As XElement = XElement.Parse(myResponse) myStartLatitude = xmldoc.Descendants("coordinate").Elements("lat").Value myStartLongitude = xmldoc.Descendants("coordinate").Elements("lng").Value Dim myPin As New Pushpin myPin.Background = New SolidColorBrush(Colors.Crimson) Dim myStartStackPanel As New StackPanel With myStartStackPanel .Margin = New Thickness(5) .Background = New SolidColorBrush(Colors.Pink) End With Dim startTextBlock As New TextBlock With startTextBlock .FontSize = 20 .Padding = New Thickness(5) .Text = "【出発点】=" & startTextBox.Text .Foreground = New SolidColorBrush(Colors.Red) End With firstLocation = New Location(CDbl(myStartLatitude), CDbl(myStartLongitude)) myStartStackPanel.Children.Add(startTextBlock) MapLayer.SetPosition(myStartStackPanel, firstLocation) myMap.Children.Add(myStartStackPanel) MapLayer.SetPosition(myPin, firstLocation) myMap.Children.Add(myPin) myMap.SetView(firstLocation, 6) OkButton2.IsEnabled = True OkButton.IsEnabled = False End Sub
「中継点」の[決定]ボタンがタップされた時の処理
中継点の位置にズームインするまでの処理は、「■「出発点」の[決定]ボタンがタップされた時の処理」と同じですので、そちらを参照してください。
「出発点」から「中継点」に直線を引く処理について解説します。
新しいMapPolylineのインスタンスmyMapPolylineオブジェクトを作成します。MapPolygon や MapPolyline などの図形を含むレイヤーを表すクラスであるMapShapeLayerクラスのインスタンス、myShapeLayerオブジェクトを作成します。
色はRedとします。myMapPlylineオブジェクトの図形を定義する場所を表すLocationsプロパティにAddメソッドで、firestLocationの値を指定します。同じくmiddleLocationの値を指定します。線の幅は4とします。
レイヤー内のシェイプのコレクションを取得する、MapShapeLayerのShapesプロパティにAddメソッドでmyMapPolylineオブジェクトを追加します。最後にmyMapの地図の、シェイプレイヤーのコレクションを取得するShapeLayersプロパティにAddメソッドでmyShapeLayerオブジェクトを追加します。これで、出発点から中継点に直線が引かれます。
「中継点」の[決定]ボタンの使用を不可とし、「到達点」の[決定]ボタンの使用を可能とします。
非同期処理で行われるため、メソッドの先頭にAsyncを追加します。
Private Async Sub OkButton2_Click(sender As Object, e As RoutedEventArgs) Handles OkButton2.Click Try If middleTextBox.Text = String.Empty Then Dim message As New MessageDialog("中継点を入力してください。") Await message.ShowAsync Exit Sub End If myMiddleStartLatitude = String.Empty myMiddleStartLongitude = String.Empty Dim middleMyUri As String = String.Format("http://www.geocoding.jp/api/?v=1.1&q={0}", Uri.EscapeDataString(middleTextBox.Text)) Dim myMiddleHttpClient As New HttpClient Dim myMiddleResponse = myMiddleHttpClient.GetStringAsync(New Uri(middleMyUri, UriKind.Absolute)) Dim myMiddleContent = myMiddleResponse.Result Dim middleXmldoc As XElement = XElement.Parse(myMiddleContent) myMiddleStartLatitude = middleXmldoc.Descendants("coordinate").Elements("lat").Value myMiddleStartLongitude = middleXmldoc.Descendants("coordinate").Elements("lng").Value Dim myMiddlePin As New Pushpin myMiddlePin.Background = New SolidColorBrush(Colors.Green) Dim myMiddleStartStackPanel As New StackPanel With myMiddleStartStackPanel .Margin = New Thickness(5) .Background = New SolidColorBrush(Colors.Pink) End With Dim middleTextBlock As New TextBlock With middleTextBlock .FontSize = 20 .Padding = New Thickness(5) .Text = "【中継点】=" & middleTextBox.Text .Foreground = New SolidColorBrush(Colors.Green) End With middleLocation = New Location(CDbl(myMiddleStartLatitude), CDbl(myMiddleStartLongitude)) myMiddleStartStackPanel.Children.Add(middleTextBlock) MapLayer.SetPosition(myMiddleStartStackPanel, middleLocation) myMap.Children.Add(myMiddleStartStackPanel) MapLayer.SetPosition(myMiddlePin, middleLocation) myMap.Children.Add(myMiddlePin) myMap.SetView(middleLocation, 6) myMapPolyline = New MapPolyline Dim myShapeLayer As New MapShapeLayer myMapPolyline.Color = Colors.Red myMapPolyline.Locations.Add(firstLocation) myMapPolyline.Locations.Add(middleLocation) myMapPolyline.Width = 4 myShapeLayer.Shapes.Add(myMapPolyline) myMap.ShapeLayers.Add(myShapeLayer) OkButton2.IsEnabled = False OkButton3.IsEnabled = True Catch Exit Sub End Try End Sub
「到達点」の[決定]ボタンがタップされた時の処理
「到達点」の位置にズームインして、「中継点」から直線を引くまでの処理は、「■「中継点」の[決定]ボタンがタップされた時の処理」と同じですので、そちらを参照してください。
ただし、ここでは、TotalCoordinate変数に、「出発点」、「中継点」、「到達点」の緯度と経度をカンマと空白で区切って格納しておきます。
「出発点」から「到達点」までの距離を求める処理について解説します。
Dim distanceUri = String.Format("http://distance.search.olp.yahooapis.jp/OpenLocalPlatform/V1/distance?coordinates={0}&appid={1}", TotalCoordinate, YahooAppID)
と指定して、Yahooの「2点間距離API」を使用しています。このAPIについては下記のURLを参照してください。
> 2点間距離API
引数のcoordinatesに変数TotalCoordinateの値を、appidにヤフーより取得したIDを指定しています。ヤフーのIDは下記のURLより取得してください。
> アプリケーションIDを登録する
新しいHttpClientのインスタンスmyHttpClient3オブジェクトを作成します。GetStringAsyncメソッドでdistanceUriから返される応答本体を文字列として受け取り、resultResponse変数に格納します。
返される結果XMLには不要な名前空間等が含まれていますので、Replace関数で手っ取り早く、名前空間を削除し、
XElement.ParseメソッドでresultResponseデータを文字列として読み取り、resultXmldocで参照します。
非同期処理で行われるため、メソッドの先頭にAsyncを追加します。
Private Async Sub OkButton3_Click(sender As Object, e As RoutedEventArgs) Handles OkButton3.Click If arriveTextBox.Text = String.Empty Then Dim message As New MessageDialog("到着点を入力してください。") Await message.ShowAsync Exit Sub End If myArriveLatitude = String.Empty myArriveLongitude = String.Empty Dim arrivedMyUri As String = String.Format("http://www.geocoding.jp/api/?v=1.1&q={0}", Uri.EscapeDataString(arriveTextBox.Text)) Dim myHttpClient2 As New HttpClient Dim myArriveResponse = Await myHttpClient2.GetStringAsync(New Uri(arrivedMyUri, UriKind.Absolute)) Dim arriveXmldoc As XElement = XElement.Parse(myArriveResponse) myArriveLatitude = arriveXmldoc.Descendants("coordinate").Elements("lat").Value myArriveLongitude = arriveXmldoc.Descendants("coordinate").Elements("lng").Value Dim TotalCoordinate = myStartLongitude & "," & myStartLatitude & " " & myMiddleStartLongitude & "," & myMiddleStartLatitude & " " & myArriveLongitude & "," & myArriveLatitude Dim myPin2 As New Pushpin myPin2.Background = New SolidColorBrush(Colors.Navy) Dim endLocation = New Location(CDbl(myArriveLatitude), CDbl(myArriveLongitude)) Dim myArriveStackPanel As New StackPanel With myArriveStackPanel .Margin = New Thickness(5) .Background = New SolidColorBrush(Colors.Pink) End With Dim arriveTextBlock As New TextBlock With arriveTextBlock .FontSize = 20 .Padding = New Thickness(5) .Text = "【到着点】=" & arriveTextBox.Text .Foreground = New SolidColorBrush(Colors.Navy) End With myArriveStackPanel.Children.Add(arriveTextBlock) MapLayer.SetPosition(myArriveStackPanel, endLocation) myMap.Children.Add(myArriveStackPanel) MapLayer.SetPosition(myPin2, endLocation) myMap.Children.Add(myPin2) myMap.SetView(endLocation, 6) myMapPolyline2 = New MapPolyline Dim myShapeLayer As New MapShapeLayer myMapPolyline2.Color = Colors.Red myMapPolyline2.Locations.Add(middleLocation) myMapPolyline2.Locations.Add(endLocation) myMapPolyline2.Width = 4 myShapeLayer.Shapes.Add(myMapPolyline2) myMap.ShapeLayers.Add(myShapeLayer) Dim distanceUri = String.Format("http://distance.search.olp.yahooapis.jp/OpenLocalPlatform/V1/distance?coordinates={0}&appid={1}", TotalCoordinate, YahooAppID) Dim myHttpClient3 As New HttpClient Dim resultResponse = Await myHttpClient3.GetStringAsync(New Uri(distanceUri, UriKind.Absolute)) resultResponse = resultResponse.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 resultXmldoc As XElement = XElement.Parse(resultResponse) Dim distance = resultXmldoc.Descendants("Distance").Value OkButton3.IsEnabled = False distanceTextBlock.Text = "出発点から到着点までは、直線的に約" & distance & "Kmあります。" readingText = distanceTextBlock.Text Await syokoVoice() End Sub
[クリア]アイコンがタップされた時の処理
引かれていた直線をクリアし、すべてを最初に読み込まれた状態にします。但し、各入力ボックスには、最後に入力された住所が残ったままになりますので、各自が調べたい住所を入力してください。
Private Sub deleteButton_Click(sender As Object, e As RoutedEventArgs) Handles deleteButton.Click If myMapPolyline Is Nothing = False Then myMapPolyline.Locations.Clear() If myMapPolyline2 Is Nothing = False Then myMapPolyline2.Locations.Clear() firstLocation = Nothing middleLocation = Nothing endLocation = Nothing myMap.Children.Clear() distanceTextBlock.Text = String.Empty OkButton.IsEnabled = True OkButton2.IsEnabled = False OkButton3.IsEnabled = False Exit Sub End Sub
結果をキャラクタが音声で喋る処理
MediaElement型のmyMedia変数を宣言し、MediaElement1で初期化しておきます。
音声機能へのアクセスを提供する、新しいSpeechSynthesizerのインスタンス、synthオブジェクトを作成します。
SynthesizeTextToStreamAsyncメソッドで、指定した文字列から、音声出力を非同期に生成します。
SetSourceメソッドで、指定されたストリームおよびMIME型を使用してSourceプロパティを設定します。Playメソッドで音声を再生します。
音声にどんな言語で、どのような声で喋らすかは、SpeechSynthesizerのVoiceプロパティで参照できます。下記のURLを参照してください。
> SpeechSynthesizer.Voice | voice property
上記URLを見るとJapanese JA は性別が「Female」で、名前は「Haruka」という女性が読み上げるようです。
非同期処理で行われるため、メソッドの先頭にAsyncを追加します。
Private Async Function syokoVoice() As Task Dim myMedia As MediaElement = Me.MediaElement1 Dim synth = New Windows.Media.SpeechSynthesis.SpeechSynthesizer Dim stream = Await synth.SynthesizeTextToStreamAsync(readingText) myMedia.SetSource(stream, stream.ContentType) myMedia.Play() End Function End Class
今回はここまでです。それでは、また次回の記事でお会いしましょう。
目的地までの距離を計算してキャラクターが音声で教えてくれるアプリ
『Windows 8.1+Visual Studio 2013によるWindows ストア・アプリ開発実例集』 第3回のサンプルプログラムです。