Android GoogleMapのマーカーを端末の向きに応じて回転させる
前回は、GoogleMapに現在位置を表示するところまでやりました。
今回は、センサーを使用して端末が向いている向きに応じてマーカーを回転させたいと思います。
方針
- 向いている方向がわかりやすいようにマップに表示するマーカーをカスタマイズする。
- 端末の向き(方位角)は、SensorManagerを使用して取得した地磁気と加速度の値を利用して求める。
- センサーを扱う処理は、MySensorManagerクラスに集約し、getAzumith()で最新の方位角を取得できるようにする。
- 前回作成したMapsActivityクラスからMySensorManagerクラスを利用する。
ソースコード
/** * MySensorManagerクラス */ public class MySensorManager implements SensorEventListener { private final SensorManager sensorManager; private float[] gravity = new float[3]; private float[] geomagnetic = new float[3]; private float azimuth = 0.0f; /** * コンストラクタ * @param context コンテキスト */ public MySensorManager(Context context) { // SensorManagerのインスタンス取得 sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); } @Override public void onSensorChanged(SensorEvent event) { // センサーのタイプに応じて値を取得 switch (event.sensor.getType()) { case Sensor.TYPE_MAGNETIC_FIELD: geomagnetic = event.values.clone(); break; case Sensor.TYPE_ACCELEROMETER: gravity = event.values.clone(); break; } // 地磁気と加速度の両方の値が揃っていない場合、方位角の算出処理をスキップ if (null == geomagnetic || null == gravity) { return; } float[] R = new float[16]; // 回転行列Rの値を格納する float[] value = new float[3]; // 方位角、ピッチ、ロールの回転角を格納する配列 // 地磁気と加速度の値から回転行列を求める SensorManager.getRotationMatrix(R, null, gravity, geomagnetic); // 回転行列に基づいて方位角と傾きを算出 SensorManager.getOrientation(R, value); // 方位角をラジアンから度に変換 azimuth = (float) (value[0] * 180 / Math.PI); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) {} /** * センサー値の取得を開始する。 */ public void startSensor() { // 地磁気センサー値の取得を開始 sensorManager.registerListener( this, sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_NORMAL); // 加速度センサー値の取得を開始 sensorManager.registerListener( this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL); } /** * センサー値の取得を停止する。 */ public void stopSensor() { sensorManager.unregisterListener(this); } /** * 方位角を取得する。 * @return 方位角 */ public float getAzimuth() { return azimuth; } }
ソースのざっくり解説
- MySensorManagerクラスは、SensorEventListenerをimplements
- コンストラクタで、SensorManagerのインスタンスを取得
- startSensor()で、地磁気と加速度の取得を開始
- stopSensor()で、地磁気と加速度の取得を停止
- onSensorChanged()は、センサー値が変更した際に呼び出される
onSensorChanged()にて、取得したセンサー値から方位角を算出 - getAzumith()で、方位角を返却
今回のポイント
マーカーのアイコンをカスタマイズ
向きがわかりやすいようにアイコンをカスタマイズします。
アイコン画像をdrawableフォルダに配置することで、オリジナル画像をマーカーとして使うことができます。
BitmapDescriptorFactory.fromResource()で指定してます。
@Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; LatLng sydney = new LatLng(-34, 151); marker = mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney")); marker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.icon)); mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney)); }
方位角の算出
方位角は、onSensorChanged()のメソッド内で計算しています。
今回は、ソースコードのコメントに私自身の解釈で説明を記載しました。
正直、地磁気と加速度の値から回転行列を求め、回転行列から方位角が算出される仕組みまでは理解していません。下記の参考サイトを参考にさせていただきました。
個人的なポイントとしては、MySensorManagerクラスにセンサー関連の処理を集約させることで、MapsActivityクラスのコード量を減らしているところが工夫した点です。
Marker.setRotation()でアイコンの向きをセット
方位角は、MySensorManager.getAzimuth()で取得し、setRotation()でセットしています。
locationCallback = new LocationCallback() { @Override public void onLocationResult(@NonNull LocationResult locationResult) { super.onLocationResult(locationResult); Location location = locationResult.getLastLocation(); LatLng latlng = new LatLng(location.getLatitude(), location.getLongitude()); mMap.moveCamera(CameraUpdateFactory.newLatLng(latlng)); marker.setPosition(latlng); marker.setRotation(mySensorManager.getAzimuth()); } };
実行結果
実行すると、このように向きに応じてマーカーが回転するようになりました。 デフォルトのマーカーよりも移動している感じが格段にわかりやすくなりました。 なお、マーカーのアイコンは、icon8の素材を使用させていただいています。
参考にしたサイト
端末の向きと傾きを取得する方法 - 加速度センサーと地磁気センサーの利用 - Androidプログラミングの基礎 - Android 開発入門
Androidで世界座標系の加速度と方位を取得する - Qiita
リファレンス
Sensor | Android Developers
SensorManager | Android Developers
SensorEventListener | Android Developers