> For the complete documentation index, see [llms.txt](https://docs.mapbox.com/android/maps/llms.txt)

# Geojson performance

![Display long route as large geojson.](https://docs.mapbox.com/android/assets/ideal-img/maps-examples-geojson-performance.07baa5c.480.png)

This example demonstrates how to handle the processing of large GeoJSON data efficiently without blocking the UI thread with the **Maps SDK for Android**. The `LargeGeojsonPerformanceActivity` class showcases the utilization of various Mapbox Maps SDK functionalities.

The activity loads a [`MapView`](https://docs.mapbox.com/android/maps/api/latest/mapbox-maps-android/com.mapbox.maps/-map-view/) and populates it with GeoJSON data, ensuring that the UI thread remains responsive during the parsing process. By using coroutines and worker threads, the GeoJSON data is converted to `Feature` objects without disrupting the user interface. The example also includes the addition of symbols and layers to the map, while logging updates and utilizing animations for a seamless user experience.

> **Note: Android Examples App Available**
> 
> This example code is part of the **Maps SDK for Android Examples App**, a working Android project [available on GitHub](https://github.com/mapbox/mapbox-maps-android/tree/v11.29.0/). Android developers are encouraged to run the examples app locally to interact with this example in an emulator and explore other features of the Maps SDK.
> 
> See our [Run the Maps SDK for Android Examples App](https://docs.mapbox.com/help/tutorials/maps-sdk-android-examples-app/) tutorial for step-by-step instructions.

**Kotlin**

Title: `LargeGeojsonPerformanceActivity.kt`

[View on GitHub](https://github.com/mapbox/mapbox-maps-android/blob/v11.29.0/app/src/main/java/com/mapbox/maps/testapp/examples/LargeGeojsonPerformanceActivity.kt)

```kt
package com.mapbox.maps.testapp.examples

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap
import androidx.lifecycle.lifecycleScope
import com.mapbox.geojson.Feature
import com.mapbox.geojson.Point
import com.mapbox.maps.CameraOptions
import com.mapbox.maps.MapView
import com.mapbox.maps.MapboxStyleManager
import com.mapbox.maps.Style
import com.mapbox.maps.extension.style.image.image
import com.mapbox.maps.extension.style.layers.addLayer
import com.mapbox.maps.extension.style.layers.generated.lineLayer
import com.mapbox.maps.extension.style.layers.generated.symbolLayer
import com.mapbox.maps.extension.style.layers.properties.generated.IconAnchor
import com.mapbox.maps.extension.style.sources.addSource
import com.mapbox.maps.extension.style.sources.generated.geoJsonSource
import com.mapbox.maps.extension.style.style
import com.mapbox.maps.logI
import com.mapbox.maps.plugin.animation.MapAnimationOptions
import com.mapbox.maps.plugin.animation.flyTo
import com.mapbox.maps.testapp.R
import com.mapbox.maps.testapp.examples.annotation.AnnotationUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

/**
 * Example of passing large geojson to verify it does not block UI thread
 * for parsing before passed to core.
 */
class LargeGeojsonPerformanceActivity : AppCompatActivity() {

  private var jsonUpdateCounter = 0

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val mapView = MapView(this)
    setContentView(mapView)

    mapView.mapboxMap.apply {
      subscribeSourceDataLoaded {
        if (it.dataId.isNullOrBlank().not()) {
          // log whenever update with corresponding data-id is rendered on the map
          logI(TAG, "GeoJsonSource was updated, data-id : ${it.dataId}")
        }
      }

      setCamera(
        CameraOptions.Builder()
          .center(Point.fromLngLat(LONGITUDE, LATITUDE))
          .zoom(START_ZOOM)
          .build()
      )
      loadStyle(
        style(Style.STANDARD) {
          // add an icon that uses very small geojson source
          +image(
            "icon",
            ContextCompat.getDrawable(
              this@LargeGeojsonPerformanceActivity,
              R.drawable.ic_blue_marker
            )!!.toBitmap()
          )
          +geoJsonSource("${SOURCE}_marker") {
            logI(TAG, "Update marker ${SOURCE}_marker, data-id : $jsonUpdateCounter")
            geometry(Point.fromLngLat(LONGITUDE, LATITUDE), jsonUpdateCounter++.toString())
          }
          +symbolLayer("${LAYER}_marker", "${SOURCE}_marker") {
            iconImage("icon")
            iconAnchor(IconAnchor.BOTTOM)
          }
        }
      ) {
        lifecycleScope.launch {
          // start an animation that uses UI thread to update map camera
          flyTo(
            CameraOptions.Builder().zoom(END_ZOOM).build(),
            MapAnimationOptions.mapAnimationOptions { duration(ANIMATION_DURATION_MS) }
          )
          // Converting a big String to Feature takes some time so we'll do it in a worker thread
          val routePoints = withContext(Dispatchers.Default) {
            Feature.fromJson(
              AnnotationUtils.loadStringFromAssets(
                this@LargeGeojsonPerformanceActivity,
                LARGE_GEOJSON_ASSET_NAME
              )
            )
          }

          addGeoJsonLines(routePoints, mapView.mapboxMap)
        }
      }
    }
  }

  private fun addGeoJsonLines(routePoints: Feature, mapboxStyleManager: MapboxStyleManager) {
    repeat(LARGE_SOURCE_COUNT) {
      mapboxStyleManager.addSource(
        geoJsonSource("${SOURCE}_$it") {
          logI(TAG, "Update routePoints ${SOURCE}_$it, data-id : $jsonUpdateCounter")
          feature(routePoints, jsonUpdateCounter++.toString())
        }
      )
      mapboxStyleManager.addLayer(
        lineLayer("${LAYER}_$it", "${SOURCE}_$it") {
          lineColor("blue")
          lineOffset(5.0 * it)
        }
      )
    }
  }

  companion object {
    private const val LARGE_GEOJSON_ASSET_NAME = "long_route.json"
    private const val ANIMATION_DURATION_MS = 10_000L
    private const val SOURCE = "source"
    private const val LAYER = "layer"
    private const val LARGE_SOURCE_COUNT = 6
    private const val LATITUDE = 51.1079
    private const val LONGITUDE = 17.0385
    private const val START_ZOOM = 5.9
    private const val END_ZOOM = 2.0
    private const val TAG = "LargeGeojsonUpdate"
  }
}
```