Skip to main content

Data driven styling

Contents

The Mobile Maps SDK examples require that you first complete the initial project setup.

This example provides a reference of how to apply data-driven styling in a Jetpack Compose environment. It displays points colored differently based on the state in which they are located (Green - Pennsylvania; Red - New Jersey; Blue - New York), similar to the Data Driven Styling code example for Java and Kotlin.

Data Driven Styling

import android.content.Context
import android.graphics.Color
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import com.trimblemaps.account.LicensedFeature
import com.trimblemaps.account.TrimbleMapsAccountManager
import com.trimblemaps.account.models.TrimbleMapsAccount
import com.trimblemaps.mapsdk.TrimbleMaps
import com.trimblemaps.mapsdk.camera.CameraPosition
import com.trimblemaps.mapsdk.geometry.LatLng
import com.trimblemaps.mapsdk.maps.MapView
import com.trimblemaps.mapsdk.maps.OnMapReadyCallback
import com.trimblemaps.mapsdk.maps.Style
import com.trimblemaps.mapsdk.maps.TrimbleMapsMap
import com.trimblemaps.mapsdk.style.expressions.Expression
import com.trimblemaps.mapsdk.style.expressions.Expression.interpolate
import com.trimblemaps.mapsdk.style.expressions.Expression.linear
import com.trimblemaps.mapsdk.style.expressions.Expression.stop
import com.trimblemaps.mapsdk.style.expressions.Expression.zoom
import com.trimblemaps.mapsdk.style.layers.CircleLayer
import com.trimblemaps.mapsdk.style.layers.PropertyFactory
import com.trimblemaps.mapsdk.style.sources.GeoJsonSource
import java.net.URI

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            // IMPORTANT: Use your real API key
            DataDrivenStylingScreen(apiKey = "YOUR-API-KEY")
        }
    }
}

@Composable
fun DataDrivenStylingScreen(apiKey: String) {
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current

    // Gate rendering the MapView until account initialization completes
    var isInitialized by remember { mutableStateOf(false) }

    // Initialize the Trimble account/session once.
    LaunchedEffect(apiKey) {
        // Build account with your API key and the Maps SDK licensed feature
        val account = TrimbleMapsAccount.builder()
            .apiKey(apiKey)
            .addLicensedFeature(LicensedFeature.MAPS_SDK)
            .build()

        // Initialize and block until ready
        TrimbleMapsAccountManager.initialize(account)
        TrimbleMapsAccountManager.awaitInitialization()

        // Obtain the Trimble Maps instance BEFORE creating/using MapView
        TrimbleMaps.getInstance(context)

        isInitialized = true
    }

    if (isInitialized) {
        val mapView = rememberMapViewWithLifecycle(context, lifecycleOwner.lifecycle)
        val SOURCE_ID = "tristatepoints"
        val LAYER_ID = "tristatepoints"

        AndroidView(
            modifier = Modifier.fillMaxSize(),
            factory = { mapView },
            update = { view ->
                // Request a TrimbleMapsMap asynchronously
                view.getMapAsync(OnMapReadyCallback { trimbleMap ->
                    // Set camera position
                    val position = CameraPosition.Builder()
                        .target(LatLng(41.36290180612575, -74.6946761628674))
                        .zoom(13.0)
                        .build()
                    trimbleMap.cameraPosition = position

                    // Set style with OnStyleLoaded callback to add data-driven styling
                    trimbleMap.setStyle(Style.TrimbleMobileStyle.MOBILE_NIGHT) { style ->
                        try {
                            // Create a source and add it to the style
                            // In this example a .json file from the assets folder is being used
                            // See sample JSON file (tristate.json) in the original example
                            style.addSource(
                                GeoJsonSource(SOURCE_ID, URI("asset://tristate.json"))
                            )

                            // Create a CircleLayer to display our source information
                            val circleLayer = CircleLayer(LAYER_ID, SOURCE_ID)
                            circleLayer.setProperties(
                                // Changing the circle radius based on the zoom level
                                PropertyFactory.circleRadius(
                                    interpolate(
                                        linear(),
                                        zoom(),
                                        stop(12f, 2f),
                                        stop(22f, 10f)
                                    )
                                ),
                                // Changing the stroke width based on the zoom level
                                PropertyFactory.circleStrokeWidth(
                                    interpolate(
                                        linear(),
                                        zoom(),
                                        stop(12f, 1f),
                                        stop(22f, 12f)
                                    )
                                ),
                                // Change the color of the circle stroke based on the "state" property
                                // in the json source data. The first color used is the default color
                                // if no 'match' is found
                                PropertyFactory.circleStrokeColor(
                                    Expression.match(
                                        Expression.get("state"),
                                        Expression.color(Color.BLACK),
                                        Expression.stop("PA", Expression.color(Color.GREEN)),
                                        Expression.stop("NY", Expression.color(Color.BLUE)),
                                        Expression.stop("NJ", Expression.color(Color.RED))
                                    )
                                ),
                                PropertyFactory.circleColor(Color.WHITE)
                            )

                            // Add the layer
                            style.addLayer(circleLayer)
                        } catch (e: Exception) {
                            e.printStackTrace()
                        }
                    }
                })
            }
        )
    }
}

/**
 * Handle the lifecycle events from the map view, onDestroy etc.
 */
@Composable
private fun rememberMapViewWithLifecycle(
    context: Context,
    lifecycle: Lifecycle
): MapView {
    // Create once per composition
    val mapView = remember {
        MapView(context)
    }

    // Hook the MapView into the lifecycle
    DisposableEffect(lifecycle, mapView) {
        val observer = LifecycleEventObserver { _, event ->
            when (event) {
                Lifecycle.Event.ON_CREATE -> { /* no-op (Trimble handled) */ }
                Lifecycle.Event.ON_START -> mapView.onStart()
                Lifecycle.Event.ON_RESUME -> mapView.onResume()
                Lifecycle.Event.ON_PAUSE -> mapView.onPause()
                Lifecycle.Event.ON_STOP -> mapView.onStop()
                Lifecycle.Event.ON_DESTROY -> mapView.onDestroy()
                else -> Unit
            }
        }
        lifecycle.addObserver(observer)

        onDispose {
            lifecycle.removeObserver(observer)
            try {
                mapView.onDestroy()
            } catch (_: Throwable) {
            }
        }
    }

    return mapView
}

Sample JSON (tristate.json)

Place this file in your assets folder:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [-74.68954, 41.369863]
      },
      "properties": {
        "state": "NY"
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [-74.687882, 41.36892]
      },
      "properties": {
        "state": "NY"
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [-74.687007, 41.349811]
      },
      "properties": {
        "state": "NJ"
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [-74.68222, 41.36056]
      },
      "properties": {
        "state": "NY"
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [-74.704292, 41.364475]
      },
      "properties": {
        "state": "PA"
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [-74.710335, 41.360504]
      },
      "properties": {
        "state": "PA"
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [-74.695556, 41.367119]
      },
      "properties": {
        "state": "PA"
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [-74.694232, 41.354974]
      },
      "properties": {
        "state": "NJ"
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [-74.698967, 41.370361]
      },
      "properties": {
        "state": "PA"
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [-74.709572, 41.360474]
      },
      "properties": {
        "state": "PA"
      }
    }
  ]
}
Last updated November 26, 2025.
Contents