Skip to content

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

@AuraSdrogo

Description

@AuraSdrogo

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:

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:

screen-20251210-012834.2.mp4

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug 🪲Something isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions