# Rotate and tilt with 3D buildings

> **Note (legacy): A newer version of the Maps SDK is available**
> 
> This page uses v9.7.1 of the Mapbox Maps SDK. A newer version of the SDK is available. Learn about the latest version, v11.29.0, in the [Maps SDK documentation](https://docs.mapbox.com/android/ja/maps/guides/).

Your browser doesn't support HTML5 video. Open [link to the video](https://docs.mapbox.com/android/ja/android/ja/assets/medias/maps-example-rotation-extrusion-activity-cee7f01b48234847662b55d93ecf0edb.mp4).

> **Note**
> 
> This example is a part of the [Mapbox Android Demo app](https://github.com/mapbox/mapbox-android-demo). You can find the values for all referenced resources in the [`res`](https://github.com/mapbox/mapbox-android-demo/tree/master/MapboxAndroidDemo/src/main/res) directory. For example, see [`res/values/activity_strings.xml`](https://github.com/mapbox/mapbox-android-demo/tree/master/MapboxAndroidDemo/src/main/res/values/activity_strings.xml) for `R.string.*` references used in this example. The dependencies can be found [here.](https://github.com/mapbox/mapbox-navigation-android-examples/blob/main/app/build.gradle) The examples use View binding. [See setup documention if necessary.](https://developer.android.com/topic/libraries/view-binding)

This example uses the Mapbox [Building Plugin](https://docs.mapbox.com/android/ja/legacy/plugins/guides/building/) for Android. You will need to [install the Building Plugin](https://docs.mapbox.com/android/ja/legacy/plugins/guides/building/#install-the-building-plugin) to use this example.

Title: `activity_extrusion_rotation`

[View on GitHub](https://github.com/mapbox/mapbox-android-demo/tree/master/MapboxAndroidDemo/src/main/res/layout/activity_extrusion_rotation.xml)

```xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             xmlns:mapbox="http://schemas.android.com/apk/res-auto"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:orientation="vertical">

    <com.mapbox.mapboxsdk.maps.MapView
        android:id="@+id/mapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        mapbox:mapbox_cameraTargetLat="40.706"
        mapbox:mapbox_cameraTargetLng="-74.011"
        mapbox:mapbox_cameraTilt="45"
        mapbox:mapbox_cameraZoom="16.5"
        />

</FrameLayout>
```

**Java**

Title: `RotationExtrusionActivity.java`

[View on GitHub](https://github.com/mapbox/mapbox-android-demo/tree/master/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/extrusions/RotationExtrusionActivity.java)

```java
package com.mapbox.mapboxandroiddemo.examples.extrusions;

import android.content.Context;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.widget.Toast;

import com.mapbox.mapboxandroiddemo.R;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.plugins.building.BuildingPlugin;

import timber.log.Timber;

/**
 * Change the camera's bearing and tilt based on device movement while viewing building extrusions
 */
public class RotationExtrusionActivity extends AppCompatActivity implements SensorEventListener {

  private MapView mapView;
  private MapboxMap mapboxMap;
  private SensorManager sensorManager;
  private SensorControl sensorControl;
  private float[] gravityArray;
  private float[] magneticArray;
  private float[] inclinationMatrix = new float[9];
  private float[] rotationMatrix = new float[9];

  // Amplifiers that translate small phone orientation movements into larger viewable map changes.
  // Pitch is negative to compensate for the negative readings from the device while face up
  // 90 is used based on the viewable angle when viewing the map (from phone being flat to facing you).
  private static final int PITCH_AMPLIFIER = -90;
  private static final int BEARING_AMPLIFIER = 90;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Mapbox access token is configured here. This needs to be called either in your application
    // object or in the same activity which contains the mapview.
    Mapbox.getInstance(this, getString(R.string.access_token));

    // This contains the MapView in XML and needs to be called after the access token is configured.
    setContentView(R.layout.activity_extrusion_rotation);

    mapView = findViewById(R.id.mapView);
    mapView.onCreate(savedInstanceState);
    mapView.getMapAsync(new OnMapReadyCallback() {
      @Override
      public void onMapReady(@NonNull final MapboxMap map) {
        mapboxMap = map;
        mapboxMap.setStyle(Style.DARK, new Style.OnStyleLoaded() {
          @Override
          public void onStyleLoaded(@NonNull Style style) {
            setupBuildingExtrusionPlugin(style);
          }
        });
      }
    });
  }

  private void setupBuildingExtrusionPlugin(@NonNull Style style) {
    BuildingPlugin buildingPlugin = new BuildingPlugin(mapView, mapboxMap, style);
    buildingPlugin.setColor(Color.LTGRAY);
    buildingPlugin.setOpacity(0.6f);
    buildingPlugin.setMinZoomLevel(15);
    buildingPlugin.setVisibility(true);
  }

  @Override
  protected void onStart() {
    super.onStart();
    mapView.onStart();
    sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
    if (sensorManager != null) {
      sensorControl = new SensorControl(sensorManager);
      registerSensorListeners();
    }
  }

  @Override
  protected void onResume() {
    super.onResume();
    mapView.onResume();
  }

  @Override
  protected void onPause() {
    super.onPause();
    mapView.onPause();
  }

  @Override
  protected void onStop() {
    super.onStop();
    mapView.onStop();
    sensorManager.unregisterListener(this, sensorControl.getGyro());
    sensorManager.unregisterListener(this, sensorControl.getMagnetic());
  }

  @Override
  public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    mapView.onSaveInstanceState(outState);
  }

  @Override
  public void onLowMemory() {
    super.onLowMemory();
    mapView.onLowMemory();
  }

  @Override
  public void onDestroy() {
    super.onDestroy();
    mapView.onDestroy();
  }

  @Override
  public void onSensorChanged(SensorEvent event) {
    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
      gravityArray = event.values;
    }
    if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
      magneticArray = event.values;
    }
    if (gravityArray != null && magneticArray != null) {
      boolean success = SensorManager.getRotationMatrix(rotationMatrix, inclinationMatrix, gravityArray, magneticArray);
      if (success) {
        if (mapboxMap != null) {
          int mapCameraAnimationMillisecondsSpeed = 100;
          mapboxMap.animateCamera(CameraUpdateFactory
            .newCameraPosition(createNewCameraPosition()), mapCameraAnimationMillisecondsSpeed
          );
        }
      }
    }
  }

  @Override
  public void onAccuracyChanged(Sensor sensor, int accuracy) {
    // Intentionally left empty
  }

  private CameraPosition createNewCameraPosition() {
    float[] orientation = new float[3];
    SensorManager.getOrientation(rotationMatrix, orientation);
    float pitch = orientation[1];
    float roll = orientation[2];

    return new CameraPosition.Builder()
      .tilt(pitch * PITCH_AMPLIFIER)
      .bearing(roll * BEARING_AMPLIFIER)
      .build();
  }

  private void registerSensorListeners() {
    int sensorEventDeliveryRate = 200;
    if (sensorControl.getGyro() != null) {
      sensorManager.registerListener(this, sensorControl.getGyro(), sensorEventDeliveryRate);
    } else {
      Timber.d("Whoops, no accelerometer sensor");
      Toast.makeText(this, R.string.no_accelerometer, Toast.LENGTH_SHORT).show();
    }
    if (sensorControl.getMagnetic() != null) {
      sensorManager.registerListener(this, sensorControl.getMagnetic(), sensorEventDeliveryRate);
    } else {
      Timber.d("Whoops, no magnetic sensor");
      Toast.makeText(this, R.string.no_magnetic, Toast.LENGTH_SHORT).show();
    }
  }

  private class SensorControl {
    private Sensor gyro;
    private Sensor magnetic;

    SensorControl(SensorManager sensorManager) {
      this.gyro = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
      this.magnetic = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
    }

    Sensor getGyro() {
      return gyro;
    }

    Sensor getMagnetic() {
      return magnetic;
    }
  }
}

```