GroundImageState
GroundImageState manages the configuration and behavior of ground image overlays on the map. It provides reactive properties for geographic bounds, image resources, and opacity settings.
Constructor
GroundImageState( bounds: GeoRectBounds, image: Drawable, opacity: Float = 1.0f, id: String? = null, extra: Serializable? = null)Properties
Core Properties
id: String: Unique identifier (auto-generated if not provided)bounds: GeoRectBounds: Geographic rectangular bounds for image placementimage: Drawable: The drawable image to displayopacity: Float: Transparency level (0.0 = transparent, 1.0 = opaque)extra: Serializable?: Additional data attached to the ground image
Methods
fun fingerPrint(): GroundImageFingerPrint // Change detectionfun asFlow(): Flow<GroundImageFingerPrint> // Reactive updatesUsage Examples
Basic Ground Image
@Composablefun BasicGroundImageExample() { val context = LocalContext.current val drawable = AppCompatResources.getDrawable(context, R.drawable.overlay_image)
val groundImageState = drawable?.let { GroundImageState( bounds = GeoRectBounds( southwest = GeoPoint.fromLatLong(37.7649, -122.4294), northeast = GeoPoint.fromLatLong(37.7849, -122.4094) ), image = it, opacity = 0.7f, extra = "Base overlay" ) }
MapView(state = mapViewState) { groundImageState?.let { state -> GroundImage(state) } }}Interactive Ground Image
@Composablefun InteractiveGroundImageExample() { var groundImageState by remember { mutableStateOf<GroundImageState?>(null) } var opacity by remember { mutableStateOf(0.7f) }
val context = LocalContext.current
LaunchedEffect(opacity) { val drawable = AppCompatResources.getDrawable(context, R.drawable.map_overlay) drawable?.let { groundImageState = GroundImageState( bounds = GeoRectBounds( southwest = GeoPoint.fromLatLong(37.7649, -122.4294), northeast = GeoPoint.fromLatLong(37.7849, -122.4094) ), image = it, opacity = opacity, extra = "Interactive overlay", onClick = { event -> println("Ground image clicked at: ${event.clicked}") } ) } }
Column { Slider( value = opacity, onValueChange = { opacity = it }, valueRange = 0f..1f, modifier = Modifier.padding(16.dp) ) Text("Opacity: ${(opacity * 100).toInt()}%")
MapView( state = mapViewState ) { groundImageState?.let { state -> GroundImage(state) } } }}Multiple Overlays
@Composablefun MultiLayerGroundImageExample() { val context = LocalContext.current
val overlayStates = remember { listOf( Triple(R.drawable.base_layer, 0.3f, "Base satellite"), Triple(R.drawable.weather_layer, 0.6f, "Weather data"), Triple(R.drawable.traffic_layer, 0.8f, "Traffic info") ).mapNotNull { (resId, opacity, description) -> AppCompatResources.getDrawable(context, resId)?.let { drawable -> GroundImageState( bounds = GeoRectBounds( southwest = GeoPoint.fromLatLong(37.7649, -122.4294), northeast = GeoPoint.fromLatLong(37.7849, -122.4094) ), image = drawable, opacity = opacity, extra = description ) } } }
MapView(state = mapViewState) { overlayStates.forEach { state -> GroundImage(state) } }}Dynamic Bounds
@Composablefun DynamicBoundsExample() { var southwest by remember { mutableStateOf(GeoPoint.fromLatLong(37.7649, -122.4294)) } var northeast by remember { mutableStateOf(GeoPoint.fromLatLong(37.7849, -122.4094)) }
val context = LocalContext.current val drawable = AppCompatResources.getDrawable(context, R.drawable.floor_plan)
val groundImageState = drawable?.let { GroundImageState( bounds = GeoRectBounds(southwest = southwest, northeast = northeast), image = it, opacity = 0.8f, extra = "Floor plan" ) } val onCornerMarkerDrag: (MarkerState) -> Unit = { markerState -> when (markerState.extra) { "SW" -> southwest = markerState.position "NE" -> northeast = markerState.position } }
MapView( state = mapViewState ) { groundImageState?.let { state -> GroundImage(state) }
// Corner markers Marker( position = southwest, icon = ColorDefaultIcon(fillColor = Color.Green, label = "SW"), draggable = true, extra = "SW", onDrag = onCornerMarkerDrag )
Marker( position = northeast, icon = ColorDefaultIcon(fillColor = Color.Red, label = "NE"), draggable = true, extra = "NE", onDrag = onCornerMarkerDrag ) }}Animated Overlay
@Composablefun AnimatedGroundImageExample() { var isAnimating by remember { mutableStateOf(false) } var opacity by remember { mutableStateOf(0.5f) }
LaunchedEffect(isAnimating) { if (isAnimating) { while (isAnimating) { delay(100) opacity = (sin(System.currentTimeMillis() / 1000.0).toFloat() + 1f) / 2f } } }
val context = LocalContext.current val drawable = AppCompatResources.getDrawable(context, R.drawable.animated_overlay)
val groundImageState = drawable?.let { GroundImageState( bounds = GeoRectBounds( southwest = GeoPoint.fromLatLong(37.7649, -122.4294), northeast = GeoPoint.fromLatLong(37.7849, -122.4094) ), image = it, opacity = opacity, extra = "Animated overlay" ) }
Column { Button(onClick = { isAnimating = !isAnimating }) { Text(if (isAnimating) "Stop" else "Animate") }
MapView(state = mapViewState) { groundImageState?.let { state -> GroundImage(state) } } }}Event Handling
Ground image events provide both the state and click location:
data class GroundImageEvent( val state: GroundImageState, val clicked: GeoPoint?)
typealias OnGroundImageEventHandler = (GroundImageEvent) -> UnitImage Resource Management
Loading from Different Sources
// From resourcesval drawable = AppCompatResources.getDrawable(context, R.drawable.overlay)
// From assetsval inputStream = context.assets.open("overlays/map.png")val drawableFromAssets = Drawable.createFromStream(inputStream, null)
// From network (using image loading library)// Use libraries like Coil, Glide, or Picasso for network imagesBest Practices
- Image Size: Optimize image resolution for performance
- Bounds Accuracy: Ensure precise geographic alignment
- Opacity: Use appropriate transparency for visibility
- Resource Management: Cache and reuse drawable resources
- Layer Order: Consider drawing order for multiple overlays
- Performance: Limit the number of simultaneous ground images
- Error Handling: Handle cases where images fail to load