# Indoor Map

> **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.30.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-indoor-map-activity-746b7e2ab046ad5495ca38686a85f719.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)

Title: `activity_lab_indoor_map`

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

```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    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">

    <com.mapbox.mapboxsdk.maps.MapView
        android:id="@+id/mapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        mapbox:mapbox_cameraTargetLat="38.89770"
        mapbox:mapbox_cameraTargetLng="-77.03655"
        mapbox:mapbox_cameraZoom="18"/>

    <LinearLayout
        android:id="@+id/floor_level_buttons"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="16dp"
        android:background="@color/mapboxBlue"
        android:orientation="vertical">

        <Button
            android:id="@+id/second_level_button"
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            android:background="?android:attr/selectableItemBackground"
            android:text="2"
            android:textColor="@color/mapboxWhite"/>

        <Button
            android:id="@+id/ground_level_button"
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            android:background="?android:attr/selectableItemBackground"
            android:text="G"
            android:textColor="@color/mapboxWhite"/>

    </LinearLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout>
```

**Java**

Title: `IndoorMapActivity.java`

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

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

import android.graphics.Color;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.widget.Button;

import com.mapbox.geojson.Point;
import com.mapbox.geojson.Polygon;
import com.mapbox.mapboxandroiddemo.R;
import com.mapbox.mapboxsdk.Mapbox;
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.style.layers.FillLayer;
import com.mapbox.mapboxsdk.style.layers.LineLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.turf.TurfJoins;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential;
import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillOpacity;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineOpacity;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineWidth;

/**
 * Display an indoor map of a building with toggles to switch between floor levels
 */
public class IndoorMapActivity extends AppCompatActivity {

  private GeoJsonSource indoorBuildingSource;

  private List<List<Point>> boundingBoxList;
  private View levelButtons;
  private MapView mapView;

  @Override
  protected void onCreate(@Nullable 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_lab_indoor_map);

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

            final List<Point> boundingBox = new ArrayList<>();

            boundingBox.add(Point.fromLngLat(-77.03791, 38.89715));
            boundingBox.add(Point.fromLngLat(-77.03791, 38.89811));
            boundingBox.add(Point.fromLngLat(-77.03532, 38.89811));
            boundingBox.add(Point.fromLngLat(-77.03532, 38.89708));

            boundingBoxList = new ArrayList<>();
            boundingBoxList.add(boundingBox);

            mapboxMap.addOnCameraMoveListener(new MapboxMap.OnCameraMoveListener() {
              @Override
              public void onCameraMove() {
                if (mapboxMap.getCameraPosition().zoom > 16) {
                  if (TurfJoins.inside(Point.fromLngLat(mapboxMap.getCameraPosition().target.getLongitude(),
                    mapboxMap.getCameraPosition().target.getLatitude()), Polygon.fromLngLats(boundingBoxList))) {
                    if (levelButtons.getVisibility() != View.VISIBLE) {
                      showLevelButton();
                    }
                  } else {
                    if (levelButtons.getVisibility() == View.VISIBLE) {
                      hideLevelButton();
                    }
                  }
                } else if (levelButtons.getVisibility() == View.VISIBLE) {
                  hideLevelButton();
                }
              }
            });
            indoorBuildingSource = new GeoJsonSource(
              "indoor-building", loadJsonFromAsset("white_house_lvl_0.geojson"));
            style.addSource(indoorBuildingSource);

            // Add the building layers since we know zoom levels in range
            loadBuildingLayer(style);
          }
        });

        Button buttonSecondLevel = findViewById(R.id.second_level_button);
        buttonSecondLevel.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {
            indoorBuildingSource.setGeoJson(loadJsonFromAsset("white_house_lvl_1.geojson"));
          }
        });

        Button buttonGroundLevel = findViewById(R.id.ground_level_button);
        buttonGroundLevel.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {
            indoorBuildingSource.setGeoJson(loadJsonFromAsset("white_house_lvl_0.geojson"));
          }
        });
      }
    });
  }

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

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

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

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

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

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

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

  private void hideLevelButton() {
    // When the user moves away from our bounding box region or zooms out far enough the floor level
    // buttons are faded out and hidden.
    AlphaAnimation animation = new AlphaAnimation(1.0f, 0.0f);
    animation.setDuration(500);
    levelButtons.startAnimation(animation);
    levelButtons.setVisibility(View.GONE);
  }

  private void showLevelButton() {
    // When the user moves inside our bounding box region or zooms in to a high enough zoom level,
    // the floor level buttons are faded out and hidden.
    AlphaAnimation animation = new AlphaAnimation(0.0f, 1.0f);
    animation.setDuration(500);
    levelButtons.startAnimation(animation);
    levelButtons.setVisibility(View.VISIBLE);
  }

  private void loadBuildingLayer(@NonNull Style style) {
    // Method used to load the indoor layer on the map. First the fill layer is drawn and then the
    // line layer is added.

    FillLayer indoorBuildingLayer = new FillLayer("indoor-building-fill", "indoor-building").withProperties(
      fillColor(Color.parseColor("#eeeeee")),
      // Function.zoom is used here to fade out the indoor layer if zoom level is beyond 16. Only
      // necessary to show the indoor map at high zoom levels.
      fillOpacity(interpolate(exponential(1f), zoom(),
        stop(16f, 0f),
        stop(16.5f, 0.5f),
        stop(17f, 1f))));

    style.addLayer(indoorBuildingLayer);

    LineLayer indoorBuildingLineLayer = new LineLayer("indoor-building-line", "indoor-building").withProperties(
      lineColor(Color.parseColor("#50667f")),
      lineWidth(0.5f),
      lineOpacity(interpolate(exponential(1f), zoom(),
        stop(16f, 0f),
        stop(16.5f, 0.5f),
        stop(17f, 1f))));
    style.addLayer(indoorBuildingLineLayer);
  }

  private String loadJsonFromAsset(String filename) {
    // Using this method to load in GeoJSON files from the assets folder.

    try {
      InputStream is = getAssets().open(filename);
      int size = is.available();
      byte[] buffer = new byte[size];
      is.read(buffer);
      is.close();
      return new String(buffer, Charset.forName("UTF-8"));

    } catch (IOException ex) {
      ex.printStackTrace();
      return null;
    }
  }
}
```