Bing Maps上に地震の震源地を表示するプログラムを作る

2013年2月20日(水)
薬師寺 国安

<※前ページからの続きです。>

[現在の情報保存]ボタンがクリックされた時の処理

現在の、何年何月何日何時何分何秒.xmlという文字列を作成し、変数myFileに格納しておきます。

ピクチャライブラリ内のEarthQuakeDataというサブフォルダにアクセスします。CreateAsyncFolderメソッドにCreationCollisionOption.OpenIfExistsを指定してフォルダを作成すると、既に同名のフォルダがある場合は、そのフォルダ名を返し、無い場合はフォルダを新規に作成してくれます。

CreateFileAsyncメソッドでサブフォルダ内に変数myFileが格納しているファイルを作成し、変数saveFileで参照しておきます。

FileIO.WriteTextAsyncメソッドで、指定したファイルにmyResponse(結果XMLが入っている)の内容を書き込みます。「保存しました。」のメッセージを表示し、dataShowButtonを使用可能にします。
これで、現在の地震の情報を記録したXMLファイルが保存されます。非同期処理で行われるため、メソッドの先頭にAsyncを追加します。

  Private Async Sub dataSaveButton_Click(sender As Object, e As RoutedEventArgs) Handles dataSaveButton.Click
    Dim myFile As String = DateTime.Now.ToString("yyyy年MM月dd日HH時mm分ss秒") & ".xml"
    Dim myStorageFolder As StorageFolder = Windows.Storage.KnownFoldersPicturesLibrary
    Dim mySubFolder = Await myStorageFolder.CreateFolderAsync("EarthQuakeData", CreationCollisionOption.OpenIfExists)
    Dim saveFile As StorageFile = Await mySubFolder.CreateFileAsync(myFile)
    Await FileIO.WriteTextAsync(saveFile, myResponse)
   Dim myMessage As New MessageDialog("保存しました")
    Await myMessage.ShowAsync
    dataShowButton.IsEnabled = True
  End Sub

[過去のデータ一覧]ボタンをクリックした時の処理

Button表面の文字で条件分岐を行います。

Button表面の文字が「過去のデータ一覧」の場合の処理です。

非表示だったListBoxを表示します。ピクチャライブラリのEarthQuakeDataサブフォルダにアクセスします。
GetFileAsyncメソッドでEarhtQuakeDataサブフォルダ内のファイルを取得しコレクション変数DataFileに格納します。
文字列型の新しいリストであるmyFileオブジェクトを作成します。

ファイルを格納しているコレクション変数DataFile内のファイルを変数resultで取得しながら、反復処理を行います。
リストのオブジェクトmyFileにAddメソッドで、Path.GetFileNameWithoutExtension(result.Path)と指定して、取得したファイル名を、パスや拡張子を除いて追加していきます。
同じくリストであるメンバ変数myFile2にはパス付拡張子付のファイル名を追加していきます。

Button表面の文字が「現在のデータ表示」の場合は、ListBoxを非表示にし、Buttonの文字を「過去のデータ一覧」としてDataShowプロシージャを実行します。

非同期処理で行われるため、メソッドの先頭にAsyncを追加します。Asyncが追加されていると、その処理が非同期で行われることを意味します。

  Private Async Sub dataShowButton_Click(sender As Object, e As RoutedEventArgs) Handles dataShowButton.Click
    Select Case dataShowButton.Content.ToString
      Case "過去のデータ一覧"
        ListBox1.Visibility = Xaml.Visibility.Visible
        Dim myStorageFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary
        Dim mySubFolder = Await myStorageFolder.CreateFolderAsync("EarthQuakeData", CreationCollisionOption.OpenIfExists)
        Dim DataFile As IReadOnlyList(Of StorageFile) = Await mySubFolder.GetFilesAsync()
        Dim myFile As New List(Of String)
 
        For Each result In DataFile
          myFile.Add(System.IO.Path.GetFileNameWithoutExtension(result.Path))
          myFile2.Add(result.Path)
        Next
        ListBox1.ItemsSource = myFile
        dataShowButton.Content = "現在のデータ表示"
      Case "現在のデータ表示"
        ListBox1.Visibility = Xaml.Visibility.Collapsed
        dataShowButton.Content = "過去のデータ一覧"
        DataShow()
      End Select
  
  End Sub

ListBoxから表示したい日付が選択された時の処理

Mapを一度初期化しておきます。この処理を怠ると円が重複して表示されますので、注意してください。
ピクチャライブラリのEarthQuakeDataサブフォルダにアクセスします。GetFileAsyncメソッドでEarthQuakeDataサブフォルダ内のファイルを取得し、コレクション変数readXmlFileに格納しておきます。
コレクション変数readXmlFileでListBoxから選択されたインデックスに該当するファイルを取得し、変数myTextに格納します。ドキュメントライブラリのEarthQuakeDataフォルダには図18のような形でXMLファイルが保存されています。

図18:ドキュメントライブラリのEarthQuakeDataサブフォルダに保存されているXMLファイル(クリックで拡大)

XElement.ParseメソッドでテキストとしてのXMLファイル(myText)を読み込みます。

地図の表示形式を「道路表示」とします。各変数をString.Emptyで初期化しておきます。

ListBoxから選択されたファイル名を、SubStringメソッドを使って、年、月、日の値を抽出します。/(スラッシュ)で連結して変数nenTukiHiに格納しておきます。

DescenDatnstメソッドで全ての子孫要素に対して、その値を変数resultに格納しながら、反復処理を行います。メンバ変数zIndexNo2の値を1ずつ加算します。

要素の各子要素の値を各変数に格納していきます。結果XMLの構造はリスト3を参照してください。

リスト3の結果XML内の、要素の子要素要素の子要素での子要素要素の子要素での子要素要素の値を使用します。

MapのCenterプロパティにDouble型に変換したmyLatitude(要素の値を格納)とmyLongitude(要素の値を格納)の値で初期化された、新しいLocationを指定します。

新しいEllipseのインスタンスmyEllipseオブジェクトを作成します。枠線の色をNavyに指定します。WidthとHeightは20とします。直径20pixelの円が描かれます。

新しいStackPanelのインスタンスmyStackpanelオブジェクトを作成します。SetValueメソッドのZindexPropertyに1ずつ加算されるzIndexNo2の値を指定します。StackPanelが円よりも前面に表示されます。myStackPanelオブジェクトの背景色にGainsBoroを指定します。初期状態ではmyStackPanelオブジェクトを非表示としておきます。

次に、TextBlockの新しいインスタンスmyTextBlockオブジェクトを作成します。パディングに5を指定し、Textプロパティに改行を含めた、「発生日時」、「震源地」、「マグニチュード」、「震度」を指定します。文字色はNavyで、文字サイズは20とします。
myStackPanelオブジェクトにmyTextBlockオブジェクトを追加します。

MapLayerクラスのSetPositionメソッドで、マップレイヤー内に要素の位置を設定します。この場合、myLatitudeとmyLongitudeの位置にmyStackPaneオブジェクトをセットします。MapLayerクラスは、地図上の要素の位置を保持しているマップレイヤーを表すクラスです。
MapにAddメソッドでmyStackPanelオブジェクトを追加します。

「発生日時が」の中に変数nenTukiHI(現在の日付)が含まれていた場合は、円をRedで塗りつぶします。それ以外はYellowで塗りつぶします。

MapLayerクラスのSetPositionメソッドで、マップレイヤー内に要素の位置を設定します。この場合、myLatitudeとmyLongitudeの位置にmyEllipse(円)オブジェクトをセットします。
MapにAddメソッドでmyEllipseオブジェクトを追加します。

円がタップされた時はmyStackPanelオブジェクトを表示します。「発生日時」、「震源地」、「マグニチュード」、「震度」の書かれたTextBlockが表示されます。
myStackPanelオブジェクトがタップされた時は、myStackPanelオブジェクトを非表示にします。「発生日時」、「震源地」、「マグニチュード」、「震度」の書かれたTextBlockも非表示になります。
非同期処理で行われるため、メソッドの先頭にAsyncを追加します。Asyncが追加されていると、その処理が非同期で行われることを意味します。

  Private Async Sub ListBox1_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles ListBox1.SelectionChanged
    Try
      myMap.Children.Clear()
      Dim myStorageFolder As StorageFolder = Windows.Storage.KnownFoldersPicturesLibrary
      Dim mySubFolder = Await myStorageFolder.CreateFolderAsync("EarthQuakeData", CreationCollisionOption.OpenIfExists)
      Dim readXmlFile As IReadOnlyList(Of StorageFile) = Await mySubFolder.GetFilesAsync()
      Dim myText As String = Await FileIO.ReadTextAsync(readXmlFile(ListBox1.SelectedIndex))
      Dim doc As XElement = XElement.Parse(myText)
 
      myMap.MapType = MapType.Road
 
      Dim detectionDate As String = String.Empty
      Dim myLocation As String = String.Empty
      Dim myLatitude As String = String.Empty
      Dim myLongitude As String = String.Empty
      Dim myMagnitude As String = String.Empty
      Dim myIntensity As String = String.Empty
 
      Dim myDateTimeStr = ListBox1.SelectedItem.ToString
      Dim _year As String = myDateTimeStr.Substring(0, 4)
      Dim _month As String = myDateTimeStr.Substring(5, 2)
      Dim _day As String = myDateTimeStr.Substring(8, 2)
      Dim nenTukiHi As String = _year & "/" & _month & "/" & _day
 
      For Each result In From c In doc.Elements("item") Select c
        zIndexNo2 = zIndexNo2 + 1
        myLocation = result.Element("epicenter_location_name").Value
        detectionDate = result.Element("detection_date").Value
        myLatitude = result.Element("epicenter_coor_lat").Value
        myLongitude = result.Element("epicenter_coor_long").Value
        myMagnitude = result.Element("magnitude").Value
        myIntensity = result.Element("intensity").Value
        myMap.Center = New Location(CDbl(myLatitude), CDbl(myLongitude))
        
        Dim myEllipse As New Ellipse
        myEllipse.Width = 20
        myEllipse.Height = 20
        myEllipse.Stroke = New SolidColorBrush(Colors.Blue)
 
        Dim myStackPanel As New StackPanel
        myStackPanel.SetValue(Canvas.ZIndexProperty, zIndexNo2)
        myStackPanel.Background = New SolidColorBrush(Colors.Gainsboro)
        myStackPanel.Visibility = Xaml.Visibility.Collapsed
  
        Dim myTextBlock As New TextBlock
        myTextBlock.Padding = New Thickness(5)
        myTextBlock.Text = "発生日時=" & detectionDate & vbCrLf & "震源地=" & myLocation & vbCrLf & "マグニチュード=" & myMagnitude & vbCrLf & "震度=" & myIntensity
        myTextBlock.Foreground = New SolidColorBrush(Colors.Navy)
        myTextBlock.FontSize = 20
        myStackPanel.Children.Add(myTextBlock)
 
        MapLayer.SetPosition(myStackPanel, New Location(CDbl(myLatitude), CDbl(myLongitude)))
        myMap.Children.Add(myStackPanel)
  
        Dim _myDate As DateTime = Date.Now
        If detectionDate.Contains(nenTukiHi) = True Then
          myEllipse.Fill = New SolidColorBrush(Colors.Red)
        Else
          myEllipse.Fill = New SolidColorBrush(Colors.Yellow)
        End If
        MapLayer.SetPosition(myEllipse, New Location(CDbl(myLatitude), CDbl(myLongitude)))
        myMap.Children.Add(myEllipse)
        AddHandler myEllipse.Tapped, Sub()
                               myStackPanel.Visibility = Xaml.Visibility.Visible
                             End Sub
 
        AddHandler myStackPanel.Tapped, Sub()
                             myStackPanel.Visibility = Xaml.Visibility.Collapsed
                          End Sub
      Next
    Catch
      Exit Sub
    End Try
  End Sub
End Class

地図の拡大縮小、表示モードの変更等はBing Mapsで自動的に処理されるため、コードを書く必要はありません。

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

  • Bing Maps上に地震の震源地を表示するWindowsアプリ

薬師寺国安事務所

薬師寺国安事務所代表。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メルマガ会員のサービス内容を見る

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