使用センサー
今回利用するセンサーは手のひらや指の位置を検出できる「Leap Motion」を使用します。Leap MotionはWindowsストアアプリからも利用可能で、簡単に対応アプリが作れるテンプレートも提供されています。
Leap Motionからの手のひらのデータはX,Y,Zの三次元データとして取得できます。
Leapの上で手を左右にふればX値が変わり、上下に動かせばY値、前後に動かせばZ値が変わります。単位はLeapの中心位置からの距離でmm(ミリメートル)単位で取得できます。
Chartを追加する
Windowsストアアプリのプロジェクトを新規作成し、MainPage.xamlを表示してツールボックスから「C1Chart」をxaml上にドラッグ&ドロップします。Page属性としてあらたに「xmlns:Xaml="using: C1.Xaml.Chart"」が追加され、ページ定義中にも「<Xaml:C1Chart />」が追加されます。
<Chart:C1Chart ChartType="Column" HorizontalAlignment="Left" Height="150" Margin="946,431,0,0" Palette="Office" VerticalAlignment="Top" Width="200"> <Chart:C1Chart.Data> <Chart:ChartData> <Chart:ChartData.Children> <Chart:DataSeries Label="s1" Opacity="1" Values="20 22 19 24 25"/> <Chart:DataSeries Label="s2" Opacity="1" Values="8 12 10 12 15"/> </Chart:ChartData.Children> </Chart:ChartData> </Chart:C1Chart.Data> <Chart:C1ChartLegend Position="Right" VerticalContentAlignment="Center"/> </Chart:C1Chart>
初期設定値は2種類の値が表示されている棒グラフになります。この定義のポイントは次のようなところです。
XAML定義のポイント
(1)ChartTypeプロパティ
C1Chart定義の一番ベースとなる部分にChartTypeプロパティとして「Column」が指定されています。これはこのグラフが棒グラフであることを示しています。位置情報を可視化するときは棒グラフではなく折れ線グラフの方が適切なので、このプロパティ値を変更すれば目的の折れ線グラフに変更できます。
(2) DataSeries
2種類のデータは2つのDataSeriesとして定義しています。今回はX,Y,Zの3種類の値を表示したいのでDataSeriesを3つ用意することになります。
初期表示からの変更ポイント
ツールボックスから設定した直後の状態から目的のデザインに変更するためには次のような作業項目が必要になります。
- グラフサイズを全画面サイズに変更
- グラフの色合いを変更
- 折れ線グラフに変更
- 3種類の値を表示するように変更
チャートを加工する
設置直後の定義から次のようなデザインに変更します。
全画面サイズに変更
初期設定では縦横の大きさや画面上での位置が明示的に指定されているため、画面全体を使ってグラフを表示できていません。そこで明示的なサイズ指定を削除して全画面表示に切り替えます。ただしこのままではタイトルにかかってしまうので、Grid.Rowも明示的に指定します。
<Chart:C1Chart ChartType="Column" Grid.Row="1" Palette="Office">
これで目指すデザインと同じサイズで棒グラフが表示できます。
色合いを変更
初期状態の色合いも黒ベースで見やすいのですが色合いをExcelライクに近づけてみます。そのためにはThemeプロパティとPaletteプロパティを利用します。
<Chart:C1Chart ChartType="Column" Grid.Row="1" Theme="Office2007Blue" Palette="Metro">
これで白系バックのグラフに変更できました。この他にもさまざまな組込済テーマがありますので、好みに合ったテーマをさがしてみるとよいでしょう。
折れ線グラフに変更
それではグラフを折れ線グラフに変更してみましょう。
折れ線グラフへの変更はChartTypeをLineにすることで実現できます。
<Chart:C1Chart ChartType="Line" Grid.Row="1" Theme="Office2007Blue" Palette="Metro">
これで2種類の値が同時に表示されている折れ線グラフを表示させるところまでたどり着きました。
3種類の値を表示するように変更
2種類の折れ線を3種類の折れ線に変更するためには、DataSeriesを3つに増やします。
これまでの変更点も含め、C1ChartのXAML定義全体を再確認してみましょう。
<Chart:C1Chart ChartType="Line" Grid.Row="1" Theme="Office2007Blue" Palette="Metro"> <Chart:C1Chart.Data> <Chart:ChartData> <Chart:ChartData.Children> <Chart:DataSeries Label="X" Opacity="1" Values="20 22 19 24 25"/> <Chart:DataSeries Label="Y" Opacity="1" Values="8 12 10 12 15"/> <Chart:DataSeries Label="Z" Opacity="1" Values="10 12 15 8 12 "/> </Chart:ChartData.Children> </Chart:ChartData> </Chart:C1Chart.Data> <Chart:C1ChartLegend Position="Right" VerticalContentAlignment="Center"/> </Chart:C1Chart>
さて、ここまで画面デザインができたところで大きな疑問がわいてくると思います。ここが今回のサンプルの最大の難関で、ヘルプをざっと眺めてみてみただけでは解決する方法がみつからなかったポイントになります。
それは「どうやって計測値をC1Chartに渡すのか」ということです。現時点ではXAMLに表示する値を直接記載していますので、これではリアルタイムに変化させることができません。
サンプルデータを表示
ここでいきなりLeap Motionから値をとってきて、データ設定方法を確認しながらデータ連携方法を確認していくのでは非効率的なので、サンプルデータを生成するクラスを用意しましょう。
XAMLなのできちんとしたコントロールであればBindingによりデータ表示できるはずだと予想して、次のようなサンプルデータクラスを定義します。
表示データ形式の定義
3種類のデータを表示するのでグラフとある時点(X軸のある時点)での値はX,Y,Zの3つの値を持ちます。そこで表示用データクラスとして次のような定義を行います。
Public Class TValue Public Property ValueX As Integer Public Property ValueY As Integer Public Property ValueZ As Integer End Class
データ連携用ViewModelクラスの用意
このデータについて折れ線グラフのX軸方向に配列のようにデータが時系列で並べればよいので、ObservableCollectionを使って「ObservableCollection(Of Models.TValue)」を公開するViewModelクラスを用意します。
Public Class MainViewModel Implements INotifyPropertyChanged Public Property HandData As ObservableCollection(Of Models.TValue) Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) _ Implements INotifyPropertyChanged.PropertyChanged Protected Sub OnPropertyChanged(<CallerMemberName> Optional propertyName As String = Nothing) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub End Class
もちろんBindingできるようにINotifyProperyChangedインターフェースを実装します。
サンプルデータクラスの用意
データ連携用ViewModelクラスを継承してサンプルデータクラスを作り、サンプルデータを設定します。
Public Class MainSample Inherits ViewModels.MainViewModel Public Sub New() Me.HandData.Add(New Models.TValue With {.ValueX = 80, .ValueY = 400, .ValueZ = 20}) Me.HandData.Add(New Models.TValue With {.ValueX = 400, .ValueY = 20, .ValueZ = 60}) Me.HandData.Add(New Models.TValue With {.ValueX = 20, .ValueY = 60, .ValueZ = 150}) Me.HandData.Add(New Models.TValue With {.ValueX = 60, .ValueY = 150, .ValueZ = 300}) Me.HandData.Add(New Models.TValue With {.ValueX = 150, .ValueY = 300, .ValueZ = 130}) Me.HandData.Add(New Models.TValue With {.ValueX = 300, .ValueY = 130, .ValueZ = 500}) Me.HandData.Add(New Models.TValue With {.ValueX = 130, .ValueY = 500, .ValueZ = 80}) Me.HandData.Add(New Models.TValue With {.ValueX = 500, .ValueY = 80, .ValueZ = 400}) End Sub End Class
サンプルデータ表示
サンプルデータクラスが用意できたのでMainPage.xamlでサンプルデータクラスを参照してグラフの3つのDataSeriesにデータをBindingしてみましょう。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:CZ1404IoT" xmlns:common="using:CZ1404IoT.Common" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Chart="using:C1.Xaml.Chart" xmlns:data="using:CZ1404IoT.Data" x:Name="pageRoot" x:Class="CZ1404IoT.Views.MainPage" d:DataContext="{d:DesignInstance IsDesignTimeCreatable=True,Type=data:MainSample}" mc:Ignorable="d"> : (中略) : <Chart:C1Chart x:Name="Chart" ChartType="Line" Grid.Row="1" Theme="Office2007Blue" Palette="Metro"> <Chart:C1Chart.Data> <Chart:ChartData ItemsSource="{Binding HandData}"> <Chart:ChartData.Children> <Chart:DataSeries Label="X" ValueBinding="{Binding ValueX}"/> <Chart:DataSeries Label="Y" ValueBinding="{Binding ValueY}"/> <Chart:DataSeries Label="Z" ValueBinding="{Binding ValueZ}"/> </Chart:ChartData.Children> </Chart:ChartData> </Chart:C1Chart.Data> <Chart:C1ChartLegend Position="Right" VerticalContentAlignment="Center"/> </Chart:C1Chart> </Grid> </Page>
(1)デザイン時インスタンスの指定
「d:DataContext="{d:DesignInstance IsDesignTimeCreatable=True,Type=data:MainSample}"」とPageタグに定義することで、XAMLデザイナで開いたときにMainSampleクラスが生成されてDataContextに割り当たります。これはあくまでもデザイナで開いた時だけで実行時には無視されます。
(2)グラフ化プロパティの指定
「<Chart:ChartData ItemsSource="{Binding HandData}">」で、グラフにはHandDataコレクションに設定されている値を使うことを定義します。
(3)個々の要素の指定
「<Chart:DataSeries Label="X" ValueBinding="{Binding ValueX}"/>」で、グラフの1要素にはHandDataコレクションのValueXプロパティの値を割り当てることを定義します。
これでXAMLエディタでサンプルデータがグラフ表示できます。
Leapデータの取得
LeapModelの定義
テンプレートから新規にプロジェクトを作成し、その中にあるLeapModelクラスを参考にして次のようなLealModelクラスを作成します。
Imports System.Runtime.Serialization Imports Newtonsoft.Json Imports Windows.Networking.Sockets Imports Windows.Storage.Streams Imports Windows.Web Namespace Models Public Class TValue Public Property ValueX As Integer Public Property ValueY As Integer Public Property ValueZ As Integer End Class Public Class LeapModel Implements INotifyPropertyChanged Public Property HandData As New ObservableCollection(Of TValue) Private WithEvents Timer As New DispatcherTimer Private WithEvents LeapListener As SampleListener Public Sub New() If Not DesignMode.DesignModeEnabled Then For index As Integer = 0 To 99 Me.HandData.Add(New TValue) Next Me.LeapListener = New SampleListener Me.Timer.Interval = New TimeSpan(0, 0, 0, 0, 0.1) Me.Timer.Start() End If End Sub Private Sub Timer_Tick(sender As Object, e As Object) Handles Timer.Tick Me.Timer.Stop() Dim data = LeapListener.Pos If data.Hand IsNot Nothing Then Me.HandData.Add(New TValue With {.ValueX = data.Hand(0).X, .ValueY = data.Hand(0).Y, .ValueZ = data.Hand(0).Z}) If Me.HandData.Count >= 100 Then Me.HandData.RemoveAt(0) End If Else Me.HandData.Add(New TValue) End If Me.Timer.Start() End Sub Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged Protected Sub OnPropertyChanged(<CallerMemberName> Optional propertyName As String = Nothing) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub End Class Friend Class SampleListener : (中略) : Private Function NormalizeVector3(tip As String()) As SharpDX.Vector3 Return New SharpDX.Vector3(CType(tip(0), Single), CType(tip(1), Single), CType(tip(2), Single)) End Function End Class End Namespace
これでLeapからあがってきた手のひらの三次元位置が随時HandDataコレクションに設定され、100個を超えたら古いものが削除されてX軸が常に最大100データでデータが随時更新されるようになります。
MainViewModelの対応
次にMainViewModelでLeapModelを使うように設定します。
Private Model As New Models.LeapModel Public Sub New() AddHandler Model.PropertyChanged, AddressOf Model_PropertyChanged End Sub Public Property HandData As ObservableCollection(Of Models.TValue) Get Return Me.Model.HandData End Get Set(value As ObservableCollection(Of Models.TValue)) Me.Model.HandData = value End Set End Property
これでHandDataコレクションのデータ供給元がLeapModelです。
MainPage.xaml.vbの対応
最後にXAMLにMainViewModelをつなぎこむ部分を記載しましょう。MainViewModelはapp.xaml.vbでMainVMというプロパティで公開していますので次ようなコードをMainPage.xaml.vbに追記します。
Private Sub NavigationHelper_LoadState(sender As Object, e As Common.LoadStateEventArgs) Me.DataContext = App.MainVM End Sub
実行
http://youtu.be/5Kbvm-_X0Zo
手の動きに反応して機敏にグラフが変化するのが分かると思います。このようにグラフ化というのは値の動きや変化というデータに対して人に何らかの思考を促すのに適したアウトプット形式なのです。
まとめ
複雑になりがちなグラフ表示もC1Chartを使えば非常に簡単に見た目もよいグラフ表示が可能です。また、リアルタイムにデータを表示させてみて、その表示性能の良さも特筆すべきものであるという実感を得ました。
今回は手のひら1つでしたが、両掌の位置、各指の位置などもC1Chartで見える化すればLeap Motionで特定の動きを検出したいときの分析に役立つでしょう。そして入力を切り替えて各種環境センサーなどの値を使うことで環境の見えるかなど快適な住空間などのデザインなどの役立つ情報が得られるかもしれません。
今年のトレンドの一つであるIoTをC1Chartで加速してみてはいかがでしょうか。