Xamarin.Android 方位の取得2(磁気センサー/加速度センサー)
1 方位の取得方法
Androidで方位を取得する方法は、次の2つがあります。
(1) TYPE_ORIENTATION(傾きセンサー)による方法
(2) TYPE_MAGNETIC_FIELD(磁気センサー)とTYPE_ACCELEROMETER(加速度センサー)による方法
今回は、(2)の磁気センサーと加速度センサーを使用してみます。
なお、API Level8 で「方位角」及び「傾き」を求める場合は、TYPE_ORIENTATIONは非推奨になっているようです。
2 磁気センサー(TYPE_MAGNETIC_FIELD)及び加速度センサー(TYPE_ACCELEROMETER)
センサー自体の使用方法は、傾きセンサーの時と同じですが、「方位角」と「傾き」は、SensorManagerのメソッドで計算しています。http://developer.android.com/reference/android/hardware/SensorManager.html
// 加速度センサーと磁気センサーの値から回転行列を求める public static bool GetRotationMatrix(float[] R, float[] I, float[] gravity, float[] geomagnetic) // 端末の画面設定に合わせる変換行列を求める(以下は, 縦表示で画面を上にした場合) public static bool RemapCoordinateSystem(float[] inR, Android.Hardware.Axis X, Android.Hardware.Axis Y, float[] outR) // 方位角及び傾きを求める public static float[] GetOrientation(float[] R, float[] values)
サンプルは、傾きセンサーの値も合わせて表示して違いを比べて見ましたが、ほとんど同じでした。
※回転角の符号が逆転しているのに注意が必要です。
[Activity(Label = "AndroidApplication1", MainLauncher = true, Icon = "@drawable/icon")] public class MainActivity : Activity, ISensorEventListener{ private SensorManager _senseManager; private TextView _textView; protected override void OnCreate(Bundle bundle){ base.OnCreate(bundle); SetContentView(Resource.Layout.Main); _senseManager = (SensorManager) GetSystemService(SensorService); _textView = FindViewById<TextView>(Resource.Id.textView); } protected override void OnResume(){ base.OnResume(); //リスナー登録 _senseManager.RegisterListener(this, _senseManager.GetDefaultSensor(SensorType.MagneticField),SensorDelay.Ui); _senseManager.RegisterListener(this, _senseManager.GetDefaultSensor(SensorType.Accelerometer),SensorDelay.Ui); _senseManager.RegisterListener(this, _senseManager.GetDefaultSensor(SensorType.Orientation), SensorDelay.Ui); } protected override void OnPause(){ base.OnPause(); //リスナー解除 _senseManager.UnregisterListener(this); } //ISensorEventListenerで必須 public void OnAccuracyChanged(Sensor sensor, SensorStatus accuracy){ } float [] _aVal; float [] _mVal; float[] _oVal; public void OnSensorChanged(SensorEvent e) { //センサー値の取得 switch (e.Sensor.Type){ case SensorType.Accelerometer: //加速度センサー _aVal = new float[3]; _aVal = e.Values.ToArray(); break; case SensorType.MagneticField: //磁気センサー _mVal = new float[3]; _mVal = e.Values.ToArray(); break; case SensorType.Orientation: //傾きセンサー _oVal = new float[3]; _oVal = e.Values.ToArray(); break; } var sb = new System.Text.StringBuilder(); if (_oVal != null) { sb.Append(String.Format("傾きセンサー\n")); sb.Append(String.Format("方位角:{0:F1}\n", _oVal[0])); sb.Append(String.Format("傾斜角:{0:F1}\n", _oVal[1])); sb.Append(String.Format("回転角:{0:F1}\n", _oVal[2])); sb.Append(String.Format("\n")); } if (_aVal != null && _mVal != null) { var R1 = new float[16]; var R2= new float[16]; var I = new float[16]; var val = new float[3]; // 加速度センサーと磁気センサーの値から回転行列を求める SensorManager.GetRotationMatrix(R1, I, _aVal, _mVal); // 端末の画面設定に合わせる変換行列を求める(以下は, 縦表示で画面を上にした場合) SensorManager.RemapCoordinateSystem(R1, Axis.X, Axis.Y, R2); // 方位角及び傾きを求める SensorManager.GetOrientation(R2, val); //ラジアンを角度に変換 for (var i = 0; i < 3; i++) { val[i] = (float)(val[i] * 180 / Math.PI); } sb.Append(String.Format("磁気センサー + 加速度センサー\n")); sb.Append(String.Format("方位角:{0:F1}\n", (val[0] < 0) ? val[0] + 360 : val[0])); sb.Append(String.Format("傾斜角:{0:F1}\n", val[1])); sb.Append(String.Format("回転角:{0:F1}\n", val[2])); } //表示 _textView.Text = sb.ToString(); } }
Xamarin.Android 方位の取得(傾きセンサー)
1 方位の取得方法
Androidで方位を取得する方法は、次の2つがあります。(1) TYPE_ORIENTATION(傾きセンサー)による方法
(2) TYPE_MAGNETIC_FIELD(磁界センサー)とTYPE_ACCELEROMETER(加速度センサー)による方法
今回は、(1)の傾きセンサーを使用してみます。
針の指している方向がちょっと?となりますが・・・
磁北は、真北(地図の真上)より、左(西)に少しずれています。(偏角)
2 傾きセンサー(TYPE_ORIENTATION)
![f:id:furuya02:20140526061452p:image:w150:left f:id:furuya02:20140526061452p:image:w150:left](http://cdn-ak.f.st-hatena.com/images/fotolife/f/furuya02/20140526/20140526061452.png)
まずは、OnCreate()で、SensorManagerのインスタンスや、表示のための初期化を行っています。
リスナー(ISensorEventListener)は、自分自身として実装し、登録及び解除は、OnResume()及びOnPause()で行っています。
OnSensorChanged()で取得できるのは、「方位角」「傾斜角」「回転角」の3つですが、
この方位角は、現在の角度(0〜360の左回り)です。
サンプルでは、この値(角度)を1回分保存し、前回との差分で画像を回転させています。
[Activity(Label = "AndroidApplication1", MainLauncher = true, Icon = "@drawable/icon")] public class MainActivity : Activity,ISensorEventListener{ private SensorManager _senseManager; private ImageView _imageView; private TextView _textView; private Matrix _matrix; private float _centerX; //回転の中心座標 private float _centerY; //回転の中心座標 protected override void OnCreate(Bundle bundle){ base.OnCreate(bundle); SetContentView(Resource.Layout.Main); _senseManager = (SensorManager) GetSystemService(SensorService); _imageView = FindViewById<ImageView>(Resource.Id.image); _textView = FindViewById<TextView>(Resource.Id.textView); _matrix = new Matrix(); //ディスプレイのサイズ取得 var display = WindowManager.DefaultDisplay; var dispWidth = display.Width; var dispHeight = display.Height; //画像のサイズ取得 var d = Resources.GetDrawable(Resource.Drawable.magnet); var imageWidth = d.IntrinsicWidth; var imageHeight = d.IntrinsicHeight; //画像を画面の中央に移動する _matrix.PostTranslate((dispWidth - imageWidth) / 2, (dispHeight - imageHeight) / 2); //回転させる際の中心座標を保存しておく _centerX = dispWidth/2; _centerY = dispHeight/2; } protected override void OnResume(){ base.OnResume(); //傾きセンサー取得 var list = _senseManager.GetSensorList(SensorType.Orientation); //取得に成功した場合のみ処理 if (list.Count > 0){ //リスナーに登録(第3引数は感度) _senseManager.RegisterListener(this,list[0], SensorDelay.Normal); } } protected override void OnPause(){ base.OnPause(); //リスナー解除 _senseManager.UnregisterListener(this); } //ISensorEventListenerで必須 public void OnAccuracyChanged(Sensor sensor, SensorStatus accuracy){ } private float _keepValue;//前回の角度 public void OnSensorChanged(SensorEvent e) { //傾きセンサーのみ処理 if (e.Sensor.Type == SensorType.Orientation){ //e.Values[0] 方位角 //e.Values[1] 傾斜角 //e.Values[2] 回転角 //現在の角度を取得 var value = e.Values[0]; //中心座標と回転角(前回角度ー現在角度)をセットする _matrix.PostRotate(_keepValue - value, _centerX, _centerY); //画像を回転させる _imageView.ImageMatrix = _matrix; //テキスト表示 _textView.Text = string.Format("{0} - {1} = {2}", _keepValue,value,_keepValue - value); //次回用に角度を保存する _keepValue = value; } } }