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

Clustering sometimes show default markers

Open ArisGuimera opened this issue 1 year ago • 16 comments

I have a screen that contain a list of custom markers with clustering and works fine. The problem is sometime after open de screen my google maps show my customs markers AND the default markers on the same place.

Is just random, sometimes when screen is open first time, sometimes when screen is open after 5 times.

Please be sure to include as much information as possible:

Environment details

  • com.google.maps.android:maps-compose:4.4.2
  • com.google.maps.android:maps-compose-utils:4.4.2
  • compileSdk 34
  • Android 12 (API 31)

Steps to reproduce

  1. Navigate to the Map screens
  2. Go back
  3. Repeat

Code example

@Composable
    fun MapScreen(stepResult: HomeState.StepResult) {
    val sheetState = rememberModalBottomSheetState(
        initialValue = ModalBottomSheetValue.Hidden
    )

    stepResult.games.let { games ->
        val items = games.map {
            MapTenant(
                itemPosition = LatLng(it.latLng.latitude, it.latLng.longitude),
                itemTitle = it.name,
                itemSnippet = it.address,
                itemZIndex = 0f,
                item = it
            )
        }

        if (items.isEmpty()) {
            return
        }

        Box {
            TenantMapList(sheetState, items)
        }
    }
}

@Composable
fun GoogleMapClustering(items: List<MapTenant>, onMapSelected: (List<MapTenant>) -> Unit) {
    var cameraState = rememberCameraPositionState {
        position = CameraPosition.fromLatLngZoom(items.first().itemPosition, 10f)
    }

    GoogleMap(
        modifier = Modifier
            .fillMaxSize()
            .padding(top = 32.dp),
        cameraPositionState = cameraState
    ) {
        CustomUiClustering(items = items, onMapSelected = {
            onMapSelected(it)
        }, onItemSelected = {
            onMapSelected(listOf(it))
        })
    }
}

@OptIn(MapsComposeExperimentalApi::class)
@Composable
private fun CustomUiClustering(
    items: List<MapTenant>,
    onMapSelected: (List<MapTenant>) -> Unit,
    onItemSelected: (MapTenant) -> Unit
) {
    Clustering(items = items,
        onClusterClick = {
            onMapSelected(it.items.toList())
            false
        }, onClusterItemClick = {
            onItemSelected(it)
            true
        },
        clusterContent = { cluster ->
            CircleContent(
                modifier = Modifier.size(40.dp),
                text = "%,d".format(cluster.size),
            )
        },
        clusterItemContent = {
            CircleContent(
                modifier = Modifier.size(20.dp),
                text = "",
            )
        })
}

@Composable
private fun CircleContent(
    text: String,
    modifier: Modifier = Modifier,
) {
    Surface(
        modifier,
        shape = CircleShape,
        color = ComplementaryPopUp,
        contentColor = Color.White,
        border = BorderStroke(2.dp, defaultGradient)
    ) {
        Box(contentAlignment = Alignment.Center) {
            Text(
                text, fontSize = 16.sp, fontWeight = FontWeight.Black, textAlign = TextAlign.Center
            )
        }
    }
}

@Composable
fun TenantMapList(
    sheetState: ModalBottomSheetState,
    items: List<MapTenant>,
) {
    var selectedTenants by remember { mutableStateOf<List<MapTenant>>(emptyList()) }

    ModalBottomSheetLayout(sheetState = sheetState,
        sheetShape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
        sheetBackgroundColor = ComplementaryPopUp,
        scrimColor = MaterialTheme.colors.onSurface.copy(alpha = 0.50f),
        sheetContent = {
            TenantsList(Modifier.wrapContentHeight(), selectedTenants)
        }) {

        val scope = rememberCoroutineScope()

        GoogleMapClustering(items) {
            selectedTenants = it
            scope.launch {
                sheetState.show()
            }
        }
    }
}

Thanks!

ArisGuimera avatar May 10 '24 10:05 ArisGuimera

If you would like to upvote the priority of this issue, please comment below or react on the original post above with :+1: so we can see what is popular when we triage.

@ArisGuimera Thank you for opening this issue. 🙏 Please check out these other resources that might help you get to a resolution in the meantime:

This is an automated message, feel free to ignore.

wangela avatar May 10 '24 10:05 wangela

Hi Aris,

I have exactly the same problem, it's very random, sometimes it happens after the 50th launch, sometimes on the first.

Apart from the graphics bug, what's more annoying is the crash if the user clicks on the default marker.

I'll let you know if I make any progress,

Thomas

ThomasLefebvre avatar May 13 '24 19:05 ThomasLefebvre

Hi Aris,

I have exactly the same problem, it's very random, sometimes it happens after the 50th launch, sometimes on the first.

Apart from the graphics bug, what's more annoying is the crash if the user clicks on the default marker.

I'll let you know if I make any progress,

Thomas

I haven't found any solution, but if I fixed I will let you know

ArisGuimera avatar May 14 '24 07:05 ArisGuimera

Hi @ArisGuimera , @ThomasLefebvre ,

What you get is similar to the one described in this issue?

https://github.com/googlemaps/android-maps-compose/issues/549

kikoso avatar May 14 '24 15:05 kikoso

Hi @ArisGuimera , @ThomasLefebvre ,

What you get is similar to the one described in this issue?

#549

I think so but my Android version is 12.

ArisGuimera avatar May 14 '24 16:05 ArisGuimera

I have the same issue, it occasionally happens on debug builds but occurs a lot more for release builds.

I can also replicate the issue if I use

public fun <T : ClusterItem> Clustering(
    items: Collection<T>,
    clusterManager: ClusterManager<T>,
)

where I use rememberClusterRenderer and set the clustering algorithm to NonHierarchicalViewBasedAlgorithm.

If I update the items list to be empty after the default markers appear over my custom ones, then the custom ones get removed, but the default ones remain.

Clearing all the markers with a MapEffect and calling map.clear() works, but I haven't found a way to programmatically detect when the default markers are being displayed.

JakeChandrasakera avatar Jun 17 '24 10:06 JakeChandrasakera

We are seeing the same issue 😔. Any advise or help on this would be greatly appreciated!

Thomaswguy avatar Jun 17 '24 10:06 Thomaswguy

My team has also encountered this issue. We are using the 5.0.4 version of the lib.

mars885 avatar Jun 27 '24 08:06 mars885

My team has also encountered this issue. We are using the 5.0.4 version of the lib.

Amazing!!

ArisGuimera avatar Jun 27 '24 08:06 ArisGuimera

+1

cwsiteplan avatar Jun 28 '24 10:06 cwsiteplan

+1

alexNguyenDM avatar Jun 28 '24 23:06 alexNguyenDM

+1

kbatrak avatar Jul 25 '24 14:07 kbatrak

(I deleted my previous message because it was wrong).

After debugging a bit I think I have a better understanding of what is going on. From what I can see ClusterManager has no way to set immediately a ClusterRenderer and it takes a bit for ComposeUiClusterRenderer to be ready and set as renderer. I suspect that if you have your cluster items ready and add them immediately to the map, the default renderer kicks in before the custom one takes its place.

I'm currently using the suggested implementation for the case in which I need a custom renderer, this one. If I wait for both ClusterManager and ClusterRenderer to be non null before adding the items (here), then I stop seeing the default markers.

If I don't wait for ClusterRenderer and add a delay before setting the renderer here, I first see the default markers and after the delay they get replaced with the custom markers. So I think there is maybe something missing in DefaultClusterRenderer.onRemove(). My guess would be that potentially ongoing/queued RenderTasks are not cancelled, but I did not properly verify this.

gmazzotta-bit avatar Aug 20 '24 09:08 gmazzotta-bit

I forgot to mention that if you are not using a custom renderer, then you are likely using this.

This line should probably be:

if (clusterManager != null && renderer != null) {

You can easily copy that function into your project and see if doing the above helps.

gmazzotta-bit avatar Aug 20 '24 10:08 gmazzotta-bit

@gmazzotta-bit thanks for your hints - that sounds very reasonable 👍

cwsiteplan avatar Aug 21 '24 07:08 cwsiteplan

I ran into this as well on a production app and came up with essentially the same workaround that @gmazzotta-bit suggested in https://github.com/googlemaps/android-maps-compose/issues/569#issuecomment-2298511381

Here's two screenshots of the bug I was experiencing, where the default marker (both single item and cluster) displayed underneath my composable marker content:

image

image

I extracted my fix into PR #615. I have not seen the issue reproduce since incorporating this change into my project.

darronschall avatar Aug 21 '24 13:08 darronschall

:tada: This issue has been resolved in version 6.2.1 :tada:

The release is available on:

Your semantic-release bot :package::rocket:

googlemaps-bot avatar Nov 05 '24 17:11 googlemaps-bot