mapbox / mapbox/mapbox-maps-android

Flicker of CircleLayer and 3D Model when tilting/rotating camera (Mapbox Android SDK 11.17.0)

Open
#2,694 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug :beetle:
Dominant language
Kotlin
Stars
578
Forks
161
PR merge metrics
No merged PRs in 30d

Description

## Environment

- Android OS version: 15
- Devices affected: Nothing Phone 2 (also Xiaomi MI 9T on Android 10)
- Maps SDK Version: com.mapbox.maps:android:11.7.0

## Observed behavior and steps to reproduce

When the camera is pitched/tilted or moved quickly the on-screen circle and a 3D glb model briefly flicker. This is reproducible with this code:

```kotlin

package com.example.mapduck

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.mapduck.databinding.ActivityMainBinding
import com.mapbox.geojson.Point
import com.mapbox.maps.CameraOptions
import com.mapbox.maps.Style
import com.mapbox.maps.extension.style.expressions.generated.Expression
import com.mapbox.maps.extension.style.layers.generated.circleLayer
import com.mapbox.maps.extension.style.layers.generated.modelLayer
import com.mapbox.maps.extension.style.layers.properties.generated.CirclePitchAlignment
import com.mapbox.maps.extension.style.layers.properties.generated.CirclePitchScale
import com.mapbox.maps.extension.style.layers.properties.generated.CircleTranslateAnchor
import com.mapbox.maps.extension.style.layers.addLayer
import com.mapbox.maps.extension.style.model.addModel
import com.mapbox.maps.extension.style.model.model
import com.mapbox.maps.extension.style.sources.addSource
import com.mapbox.maps.extension.style.sources.generated.geoJsonSource

class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding

private val duckCenter = Point.fromLngLat(8.81645, 45.557)
private val circleCenter = Point.fromLngLat(8.819, 45.559)
private val duckModelId = "duck"
private val duckModelUrl =
"https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Duck/glTF-Binary/Duck.glb"

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

val mapView = binding.mapView
val mapboxMap = mapView.mapboxMap

mapboxMap.setCamera(
CameraOptions.Builder()
.center(duckCenter)
.zoom(17.0)
.pitch(60.0)
.build()
)

mapboxMap.loadStyle(Style.STANDARD) { style ->
addDuckModel(style)
addCircle(style)
}
}

private fun addDuckModel(style: Style) {
// Model source
style.addSource(
geoJsonSource("duck-model-source") {
geometry(duckCenter)
}
)

// Register the remote model asset so the layer can reference it
style.addModel(
model(duckModelId) {
uri(duckModelUrl)
}
)

style.addLayer(
modelLayer("duck-model-layer", "duck-model-source") {
modelId(duckModelId)
modelScale(listOf(50.0, 50.0, 50.0))
modelRotation(listOf(0.0, 0.0, 0.0))
}
)
}

private fun addCircle(style: Style) {
style.addSource(
geoJsonSource("circle-source") {
geometry(circleCenter)
}
)

style.addLayer(
circleLayer("user-location-ring-layer", "circle-source") {
circleRadius(40.0)
circleColor("rgba(30, 136, 229, 0.25)")
circleOpacity(0.7)
circleStrokeWidth(2.0)
circleStrokeColor("rgba(30, 136, 229, 0.5)")
circlePitchAlignment(CirclePitchAlignment.VIEWPORT)
circlePitchScale(CirclePitchScale.VIEWPORT)
circleTranslateAnchor(CircleTranslateAnchor.VIEWPORT)
circleSortKey(9999.0)
}
)

style.addLayer(
circleLayer("user-location-dot-layer", "circle-source") {
circleRadius(6.0)
circleColor("#1E88E5")
circleStrokeWidth(2.0)
circleStrokeColor("#FFFFFF")
circlePitchAlignment(CirclePitchAlignment.VIEWPORT)
circlePitchScale(CirclePitchScale.VIEWPORT)
circleTranslateAnchor(CircleTranslateAnchor.VIEWPORT)
circleSortKey(10000.0)
}
)
}

override fun onStart() {
super.onStart()
binding.mapView.onStart()
}

override fun onStop() {
super.onStop()
binding.mapView.onStop()
}

override fun onLowMemory() {
super.onLowMemory()
binding.mapView.onLowMemory()
}

override fun onDestroy() {
super.onDestroy()
binding.mapView.onDestroy()
}
}

```

This video shows the glitch:

https://github.com/user-attachments/assets/3a2a4467-ce45-40a7-88a7-96bd2e2885bf

## Expected behavior

That flicker should not happen.

## Notes / preliminary analysis

It looks like the flicker happens when "loaded: false":

`12-11 18:36:40.826 341 341 D MapDebug: SourceDataLoaded: [sourceId: composite, type: Tile, loaded: false, tileId: [z: 12, x: 2148, y: 1465], dataId: , timeInterval: [begin: Thu Dec 11 18:36:40 GMT+01:00 2025, end: Thu Dec 11 18:36:40 GMT+01:00 2025]]`

I have no idea why this happens. If this could help, I also tried with rnmapbox in react native and the same behaviour happens. Thank you for your time.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the issue with the provided Kotlin MainActivity, using the rapid camera tilt or rotation on the listed Android devices and SDK version. Observe the circle, 3D GLB model, and SourceDataLoaded log during movement; done means the flicker no longer occurs.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, kotlin
Domain
computer-graphics, mobile-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.