bpillon / google_maps_cluster_manager

Simple Flutter clustering library for Google Maps
https://pub.dev/packages/google_maps_cluster_manager
MIT License
132 stars 116 forks source link

[Feature] Zoom in to decluster #36

Open cedvdb opened 3 years ago

cedvdb commented 3 years ago

On the vanilla google cluster API when you click on a cluster the map will zoom in until some of the points are markers (stand by themselves).

https://developers.google.com/maps/documentation/javascript/marker-clustering

It has a method getMaxZoom() which allows the client to zoom into the cluster without losing any marker.

It would be nice if this plugin supported such a feature, or at least made it easier to implement.

baldax95 commented 2 years ago

I'm trying to work on it,

inside the property onTap of the markerBuilder function I implemented this code:

onTap: cluster.isMultiple
          ? () async {
              var gMapController = await gMapCompleter.future;
              var actualZoom = await gMapController.getZoomLevel();
              var startingClusterCount = cluster.count;
              while (cluster.count == startingClusterCount) {
                actualZoom =
                    manager.levels.firstWhere((level) => level > actualZoom);
                gMapController.animateCamera(
                  CameraUpdate.newLatLngZoom(
                    cluster.location,
                    actualZoom,
                  ),
                );
              }
            }
          : () => showModalBottomSheet(
                backgroundColor: Colors.amber.withOpacity(0.90),
                shape: const RoundedRectangleBorder(
                  borderRadius: BorderRadius.vertical(top: Radius.circular(30)),
                ),
                context: context,
                builder: (BuildContext context) {
                  return const SizedBox(
                    width: double.infinity,
                    height: 350,
                  );
                },
              ),

The interesting part is the cluster.isMultiple branch.

The problem is that it continues to zoom till the last level, it's like if the cluster.count doesn't update.

Any suggestion?

cedvdb commented 2 years ago

yes your while loop is problematic because it is synchronous so count will always be the same.

What you'd want is somehting like this


    // pseudo code
    int getClusterZoomLevel() async {
             final startingCount = cluster.count;
             var endCount = startingCount;
             var endZoom = await getCurrentZoomLevel();
             while(endCount == startingCount && endZoom <= maxZoomLevel) {
                endZoom++;
                endCount = await getClusterCountForZoomLevel(endZoom)
             }
             return endZoom;
    }

Therefor you need a way to calculate the end zoom before animation even starts.

baldax95 commented 2 years ago

The problem is that there isn't a way to calculate the cluster count for a certain zoom level before actually zooming the map.

Or am I wrong?

cedvdb commented 2 years ago

As far as I know there is no public method in the package that does it. A Pull request would certainly help to resolve this issue if that's not the case

masreplay commented 2 years ago

@Jollastro @cedvdb any updates!

cedvdb commented 2 years ago

I don't think either of us are working on that, feel free to implement it.

jonasPfeifhofer commented 2 years ago

Any updates?

tuoku commented 2 years ago

I implemented this by zooming to the LatLngBounds of the markers in the cluster:

...

LatLngBounds boundsFromLatLngList(List<LatLng> list) {
    double? x0, x1, y0, y1;
    for (LatLng latLng in list) {
      if (x0 == null || x1 == null || y0 == null || y1 == null) {
        x0 = x1 = latLng.latitude;
        y0 = y1 = latLng.longitude;
      } else {
        if (latLng.latitude > x1) x1 = latLng.latitude;
        if (latLng.latitude < x0) x0 = latLng.latitude;
        if (latLng.longitude > y1) y1 = latLng.longitude;
        if (latLng.longitude < y0) y0 = latLng.longitude;
      }
    }

    return LatLngBounds(
        northeast: LatLng(x1!, y1!), southwest: LatLng(x0!, y0!));
  }

...

onTap: () async {
            if (cluster.isMultiple) {
              final controller = await _mapsController.future;
              controller.animateCamera(CameraUpdate.newLatLngBounds(
                  boundsFromLatLngList(
                      cluster.items.map((e) => e.location).toList()),
                  100));
            }
...