Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update model and marker in cluster #90

Closed
artworkad opened this issue Jun 3, 2014 · 18 comments · Fixed by #627
Closed

Update model and marker in cluster #90

artworkad opened this issue Jun 3, 2014 · 18 comments · Fixed by #627
Assignees
Labels
priority: p1 Important issue which blocks shipping the next release. Will be fixed prior to next release. type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.
Milestone

Comments

@artworkad
Copy link

When you put your model objects/items into the cluster manager, it will create the markers and group them to clusters if necessary. This is awesome!!

But what if the items change and upon this change you want to have different marker?

Idea 1 (Ideal)

Update the api with an

 updateItem(Item)

method. Its the same instance of the added item but has updated attributes.

Idea 2

What I first tried was just replacing the item list

getMap().clear();
mClusterManager.clearItems()
mClusterManager.addItems(items);
mClusterManager.cluster();

This worked only for visible clusters when map was zoomed out. However when map was zoomed in, visible map markers were removed by the above code. You have to zoom out until cluster appears and zoom in again to see the markers.

Idea 3

Currently I am doing this, completely reinitiate the clusterManager when I need to update models and markers. I am not dealing with so much markers (max 200).

@broady Is this ok regarding performance/memory? Nevertheless the cluster api could need some rewrite 👍

@artworkad artworkad changed the title Update model and marker Update model and marker in cluster Jun 3, 2014
@Shusshu
Copy link

Shusshu commented Aug 11, 2014

This commit makes it possible: 49d42d6

@Flyview
Copy link

Flyview commented May 26, 2015

@Shusshu How? I am having the same problem with clearing items and adding them back. Please see this issue: #168

@stahlnow
Copy link

stahlnow commented Apr 8, 2017

i would love to know if there's a solution to updating a model and having the info window refreshed.

@dxshindeo
Copy link

I guess we still need to kill all markers and then re-add them back as new ones, sadness eternal ...

@lujop
Copy link

lujop commented May 18, 2017

I can confirm that Idea 2 works correctly. In fact I can confirm that removeItem()+addItem()+cluster() works correctly if you have a correct Renderer.

You must ensure you use a custom renderer that overrides onClusterItemRendered() and onClusterRendered()
Default renderer only uses onBeforeClusterRendered() that is only called once to create the initial marker.

@xsorifc28
Copy link

@lujop How did you go about overriding onClusterItemRendered() and onClusterRendered()? I'm trying to re-draw a marker after it's position changes. The new position will show if I zoom in/out but not if I'm zoomed in to the marker level. cluster() works on clusters, but not individual markers.

@dxshindeo
Copy link

As I said - if you want to update a single marker, remove the item (not the marker), add the item again, and run cluster() - the function gets the updated set of items from its array and then refreshes all of its markers on the map.
In nativescript:

// remove previous item
dis.cluster_manager.removeItem(cluster_item);

// if following data goes into constructor directly, they somehow become null, so save them seperately outside here
var temp_data = data_units.existing[i];
var updated_position = new dis.maps.model.LatLng(temp_data.latitude, temp_data.longitude);

// adding item
var item = new dis.clustering.ClusterItem({
	getTitle: function() {},
	getSnippet: function() {},
	getPosition: function () {
		return updated_position;
	},
	userData: temp_data
});
dis.cluster_manager.addItem(item);

// commit changes
dis.cluster_manager.cluster();

@Murtowski
Copy link

Murtowski commented Jul 26, 2017

By me object it it still not working correctly

I Use

fun setUpMarkers(poiList:List<ClusterItem>){
mGoogleMap?.clear()
mClusterManager?.clearItems()

poiList.forEachIndexed { index, poi -> run{
                mClusterManager?.addItem(poi)
                if (index < 6)
                    boundBuilder.include(poi.latLng)
            }}
[...]
mGoogleMap?.animateCamera(CameraUpdateFactory.newLatLngBounds(latLngBounds, mapPadding), cameraUpdateListener)
}

camerUpdateListener

private val cameraUpdateListener = object : GoogleMap.CancelableCallback {
        override fun onFinish() {
                    mClusterManager?.cluster()            
        }

        override fun onCancel() {
                    mClusterManager?.cluster()
        }
    }

In run time I delivere different list of CluterItems to my function, after animated zoom, sometimes some of markers are not visible - until i zoom out/in (force cluter algorithm to run). A assume that clearItems if also woring inpredictable - sometimes remove more soetimes less items

@paduy
Copy link

paduy commented Aug 1, 2017

I also cannot refresh marker icons, the "java.lang.IllegalArgumentException: Unmanaged descriptor" always appears when I try to get a marker from Renderer and setIcon for it. Thanks!

@holakeshav
Copy link

Hello all,

I was facing similar issue when downloading images via Glide.

Now it is working fine for me.

What I did was call mClusterManager.cluster(); after all images were downloaded in onPostExecute.


 @Override
            protected Void doInBackground(Void... voids) {

                if (fetchUserMapData != null) {
                    for (int i = 0; i < fetchUserMapData.getDiscoveredUsers().size(); i++) {
                        LatLng latLng = new LatLng(fetchUserMapData.getDiscoveredUsers().get(i).getUserLatitude().doubleValue(),
                                fetchUserMapData.getDiscoveredUsers().get(i).getUserLongitude().doubleValue());

                        Bitmap bitmap;
                        try {
                            bitmap = Glide.with(MapFragment.this)
                                    .load(fetchUserMapData.getDiscoveredUsers().get(i).getProfilePictureUrl())
                                    .asBitmap()
                                    .into(200, 200).get();
                        } catch (Exception e) {
                            e.printStackTrace();
                            bitmap = BitmapFactory.decodeResource(getResources(),
                                    R.drawable.user_image_placeholder);
                        }


                        mClusterManager.addItem(new Person(latLng,
                                fetchUserMapData.getDiscoveredUsers().get(i).getUsername(),
                                bitmap));

                    }
                }

                return null;
            }

I have tested a couple of times. But, I would prefer good amount of testing before being sure.

@Kolyall
Copy link

Kolyall commented Jun 27, 2018

Like @Shusshu said , getMarker() can solve the issue
In my case it looks like this:
In Activity:

  @Override
    public void onMapReady(GoogleMap googleMap) {
        super.onMapReady(googleMap);
        FragmentActivity activity = getActivity();
        if (activity == null) {
            return;
        }
        mClusterManager = new ClusterManager<>(activity, getGoogleMap());
        mClusterManager.setAlgorithm(new NonHierarchicalDistanceBasedAlgorithm<>());
        mClusterManager.setRenderer(new PlaceRenderer(activity, getGoogleMap(), mClusterManager));
        mClusterManager.setOnClusterClickListener(this);
        mClusterManager.setOnClusterItemClickListener(this);
        getGoogleMap().setOnCameraIdleListener(mClusterManager);
        getGoogleMap().setOnMarkerClickListener(mClusterManager);
        getGoogleMap().setOnInfoWindowClickListener(mClusterManager);
        getGoogleMap().setOnMapClickListener(latLng -> {
            setItemChecked(null);
        });
    }

    @Override
    public boolean onClusterItemClick(PlaceCluster placesCluster) {
       mGoogleMap.animateCamera(CameraUpdateFactory
                .newLatLngZoom(placesCluster.getPosition(), getGoogleMap().getCameraPosition().zoom));
        setItemChecked(placesCluster);
        return true;
    }

private void setItemChecked(@Nullable PlaceCluster placesCluster) {
        PlaceRenderer renderer = (PlaceRenderer) mClusterManager.getRenderer();
        for (PlaceCluster item: mClusterManager.getAlgorithm().getItems()) {
            item.setChecked(false);

            if (placesCluster!=null && item.equals(placesCluster)){
                placesCluster.setChecked(true);
            }
            renderer.setUpdateMarker(item);
        }
    }

PlaceRenderer :

public class PlaceRenderer extends DefaultClusterRenderer<PlaceCluster> {
...
  public void setUpdateMarker(PlaceCluster place) {
        Marker marker = getMarker(place);
        if (marker != null) {
            marker.setIcon(place.isChecked() ? mPlaceMarkerDescriptorChecked : mPlaceMarkerDescriptorDefault);
        }
    }
...

PlaceCluster :

public class PlaceCluster implements ClusterItem, Checkable {
...
}

@mitchtabian
Copy link

Thank you @Kolyall !!
Making a "setUpdateMarker" method worked perfectly in my Renderer!

@SimonHaenle2
Copy link

@Kolyall getMarker() always returns null for me. No way to get that code of yours working..

@alboteanud
Copy link

alboteanud commented Jul 22, 2019

idea 2 also worked nice for me
`
val persons: HashMap<String, Person> = hashMapOf()

fun refreshCluster() {
      clusterManager?.clearItems()
      clusterManager?.addItems(persons.values)
      clusterManager?.cluster()
}`

where Person is a

data class Person(val userDoc: QueryDocumentSnapshot) : ClusterItem { ...

@stale
Copy link

stale bot commented Oct 3, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the status: will not fix Invalid (untrue/unsound/erroneous), inconsistent with product, not on roadmap. label Oct 3, 2019
@jpoehnelt jpoehnelt removed the status: will not fix Invalid (untrue/unsound/erroneous), inconsistent with product, not on roadmap. label Oct 3, 2019
@stale
Copy link

stale bot commented Jan 1, 2020

This issue has been automatically marked as stale because it has not had recent activity. Please comment here if it is still valid so that we can reprioritize. Thank you!

@stale stale bot added the stale label Jan 1, 2020
@arriolac arriolac added the triage me I really want to be triaged. label Jan 29, 2020
@stale stale bot removed the stale label Jan 29, 2020
@arriolac arriolac added priority: p1 Important issue which blocks shipping the next release. Will be fixed prior to next release. type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design. and removed triage me I really want to be triaged. labels Jan 29, 2020
@arriolac arriolac added this to the 1.0 milestone Feb 20, 2020
barbeau added a commit that referenced this issue Feb 20, 2020
As discussed in #90, when an item instance is removed, updated, and re-added the ClusterManager, the updated item state wouldn't be reflected on the map.

The reason for this is that DefaultClusterRenderer.CreateMarkerTask.perform() was using a marker from its own mMarkerCache associated with that item to display that item on the map (even if ClusterManager.clearItems() was previously called). This resulted in the updated item contents not being reflected on the map, because the cached marker was not updated.

This PR does the following:

* Add new protected methods in DefaultClusterRenderer - onClusterItemUpdated() and onClusterUpdated() - which are called when an existing cached marker is found for an item/cluster when that item/cluster is being added back into the ClusterManager. The default implementation of these methods update the marker contents on the map, but then can also be overridden by applications for custom behavior (similar to onBeforeClusterItemRendered() and onBeforeClusterRendered()). Following this change, you can now do ClusterManager.clearItems() and re-add the same list instance (or remove() and then add() the same item) and then call cluster() and the map markers will be updated with the new item model contents.
* Add a new ClusterManager.updateItem() helper method for updating the ClusterManager and algorithms with an existing item. The current implementation in NonHierarchicalDistanceBasedAlgorithm is a wrapper for remove() and then add().
* Add more descriptive Javadocs to ClusterManager, DefaultClusterRenderer, and NonHierarchicalDistanceBasedAlgorithm to describe expected behavior, especially that cluster() should be invoked after any changes to ClusterManager to see changes on the map.
* Add boolean return values to Algorithm add/remove/update operations - Implementation follows the Java Collection definitions of the matching methods

Refs: 90

Fixes #90
@nikeru8
Copy link

nikeru8 commented Jul 18, 2022

arker marker = getMarker(place);

Like @Shusshu said , getMarker() can solve the issue In my case it looks like this: In Activity:

  @Override
    public void onMapReady(GoogleMap googleMap) {
        super.onMapReady(googleMap);
        FragmentActivity activity = getActivity();
        if (activity == null) {
            return;
        }
        mClusterManager = new ClusterManager<>(activity, getGoogleMap());
        mClusterManager.setAlgorithm(new NonHierarchicalDistanceBasedAlgorithm<>());
        mClusterManager.setRenderer(new PlaceRenderer(activity, getGoogleMap(), mClusterManager));
        mClusterManager.setOnClusterClickListener(this);
        mClusterManager.setOnClusterItemClickListener(this);
        getGoogleMap().setOnCameraIdleListener(mClusterManager);
        getGoogleMap().setOnMarkerClickListener(mClusterManager);
        getGoogleMap().setOnInfoWindowClickListener(mClusterManager);
        getGoogleMap().setOnMapClickListener(latLng -> {
            setItemChecked(null);
        });
    }

    @Override
    public boolean onClusterItemClick(PlaceCluster placesCluster) {
       mGoogleMap.animateCamera(CameraUpdateFactory
                .newLatLngZoom(placesCluster.getPosition(), getGoogleMap().getCameraPosition().zoom));
        setItemChecked(placesCluster);
        return true;
    }

private void setItemChecked(@Nullable PlaceCluster placesCluster) {
        PlaceRenderer renderer = (PlaceRenderer) mClusterManager.getRenderer();
        for (PlaceCluster item: mClusterManager.getAlgorithm().getItems()) {
            item.setChecked(false);

            if (placesCluster!=null && item.equals(placesCluster)){
                placesCluster.setChecked(true);
            }
            renderer.setUpdateMarker(item);
        }
    }

PlaceRenderer :

public class PlaceRenderer extends DefaultClusterRenderer<PlaceCluster> {
...
  public void setUpdateMarker(PlaceCluster place) {
        Marker marker = getMarker(place);
        if (marker != null) {
            marker.setIcon(place.isChecked() ? mPlaceMarkerDescriptorChecked : mPlaceMarkerDescriptorDefault);
        }
    }
...

PlaceCluster :

public class PlaceCluster implements ClusterItem, Checkable {
...
}

Marker marker = getMarker(place);
DefaultClusterRenderer getMarker() returning null when zooming

@barbeau
Copy link
Collaborator

barbeau commented Jul 18, 2022

@nikeru8 Could you please open a new issue with more details so we can better understand the exact issue you're encountering here? Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
priority: p1 Important issue which blocks shipping the next release. Will be fixed prior to next release. type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.
Projects
None yet
Development

Successfully merging a pull request may close this issue.