mapbox-maps-android icon indicating copy to clipboard operation
mapbox-maps-android copied to clipboard

Icon blinks zooming in/out with PointAnnotationGroup/Compose

Open kevinMoonware opened this issue 1 year ago • 4 comments

Environment

  • Android OS version: 14
  • Devices affected: Not device specific but is seen on Samsung s21 ultra as well as Google Pixel 6.
  • Maps SDK Version: Can be observed from 10.x all the way to 11.2.2 (can not verify on 11.3 at this point)
  • Compose version: 1.6.2

Observed behavior and steps to reproduce

Icon blinks when zoom in/out. The blinking intensifies when zooming out and the icons are getting closer to each other. Blinking appears much less often when there are few than 3 icons visible within the map region (but it does still happen sometimes).

Please see video capture here: https://youtube.com/shorts/es0iWngndEo?feature=share

Expected behavior

Icon(s) do not blink, or only blink when clustering ops are happening. Blinking should not happen during typical zoom in/out operation when zoom level is large enough that clustering is not a factor.

Notes / preliminary analysis

        MapboxMap(
            modifier = Modifier.fillMaxSize(),
            mapViewportState = mapViewportState,
            mapInitOptionsFactory = { context ->
                MapInitOptions(
                    context = context,
                    styleUri = "mapbox://styles/moonmapper/cloxllb7700q601qj462k2q76",
                )
            },
            gesturesSettings = GesturesSettings {
                quickZoomEnabled = true
                doubleTapToZoomInEnabled = true
                pitchEnabled = false
                this.build()
            },
            onMapClickListener = { point ->
            ....
            }
        ) {
            MapEffect(mapViewportState.cameraState.center) { mapView ->
                mapView.location.updateSettings {
                    enabled = true
                }
                val mapboxMap = mapView.mapboxMap
                mapView.scalebar.enabled = false
                .....
            }
            ...

            PointAnnotationGroup(
                iconOptional = true,
                iconAllowOverlap = true,
                iconIgnorePlacement = true,
                iconPitchAlignment = IconPitchAlignment.MAP,
                annotations = listOf(
                    flightWithPositions.toFlightAnnotationOptions(),
                    vehicleWithPositions.toVehicleAnnotationOptions(),
                    userWithPositions.toUserAnnotationOptions()
                ).flatten()
                    .filter {
                        it.getPoint()?.let { point ->
                            ComposeMapboxManager.bounds.value?.contains(point, true) == true
                        } ?: false },
                annotationConfig = AnnotationConfig(
                    annotationSourceOptions = AnnotationSourceOptions(
                        clusterOptions = ClusterOptions(
                            clusterMaxZoom = 16,
                            textColor = Color(0xFF000000).toArgb(),
                            textSize = 12.0,
                            circleRadiusExpression = literal(15.0),
                            colorLevels = listOf(
                                Pair(100, Color.Red.toArgb()),
                                Pair(50, Color.Blue.toArgb()),
                                Pair(0, Color(0xFFB0B7C3).toArgb())
                            )
                        )
                    )
                ),
                onClick = { annotation ->
                    ....
                }
            )
        }

Here is how I typically handle the creation of PointAnnotationOptions

@Composable
fun List<VehicleWithPosition>.toVehicleAnnotationOptions(): List<PointAnnotationOptions> {
    return this.map { vehicle ->
        val vehiclePoint = Point.fromLngLat(
            vehicle.positions[0].longitude,
            vehicle.positions[0].latitude
        )
        var options by remember {
            mutableStateOf(
                PointAnnotationOptions()
                    .withPoint(vehiclePoint)
                    .withData(
                        gson.toJsonTree(
                            AnnotationItem(
                                vehicleLasId = vehicle.vehicle.lasId
                            ), AnnotationItem::class.java
                        )
                    )
            )
        }

        LaunchedEffect(
            vehicle.positions[0].heading,
            vehicle.positions[0].latitude,
            vehicle.positions[0].longitude,
            vehicle.vehicle.getGseStatus(),
            vehicle.matchSelectedVehicle(),
            if (!ComposeMapboxManager.mapMoving.value) ComposeMapboxManager.cameraOptions.value?.bearing else null
        ) {
            val selected = vehicle.matchSelectedVehicle()
            val relativeHeading = vehicle.positions[0].heading - (ComposeMapboxManager.cameraOptions.value?.bearing?.toInt() ?: 0)
            val key = VehicleKey(
                vehicle.vehicle.name,
                vehicle.vehicle.getGseStatus(),
                relativeHeading,
                selected
            )
            withContext(Dispatchers.IO) {
                val iconImage = if (vehicleIconMap.get(key) == null) {
                    vehicleMarkerView(
                        heading = relativeHeading,
                        name = vehicle.vehicle.name,
                        type = vehicle.vehicle.type,
                        status = vehicle.vehicle.getGseStatus(),
                        batteryLevel = vehicle.positions[0].battery,
                        selected = selected
                    ).also {
                        vehicleIconMap.put(key, it)
                    }
                } else {
                    vehicleIconMap[key]!!
                }
                val updatedVehicleMarker = PointAnnotationOptions()
                    .withData(
                        gson.toJsonTree(
                            AnnotationItem(
                                vehicleLasId = vehicle.vehicle.lasId
                            ), AnnotationItem::class.java
                        )
                    )
                    .withIconImage(iconImage)
                    .withPoint(vehiclePoint)
                options = updatedVehicleMarker
            }
        }
        options.symbolSortKey = 3.0
        options.iconAnchor = IconAnchor.CENTER
        options
    }
}`

Additional links and references

kevinMoonware avatar Apr 08 '24 17:04 kevinMoonware