mapbox / mapbox-maps-flutter

Interactive, thoroughly customizable maps for Flutter powered by Mapbox Maps SDK
https://www.mapbox.com/mobile-maps-sdk
Other
245 stars 93 forks source link

Tapping on an unclustered layer item when using clusters #501

Open Jamey-7 opened 4 weeks ago

Jamey-7 commented 4 weeks ago

Has anyone found out a way when using clustering to be able to tap a unclustered item? I have tried everything and I can't figure out how to be able to make it so I can attach a method to a circle that is unclustered? I don't see a way to add clustering to point annotations I have tried but I am unable to get it to work for only the unclustered items of a group.

IMG_8287

Here is my code:

void _addGeoJsonSourceWithClustering() async {
  final mapClusterProvider = Provider.of<MapClusterProvider>(context, listen: false);
  final geoJson = mapClusterProvider.createGeoJson();

  String sourceId = "data";
  Map<String, dynamic> sourceData = {
    "type": "geojson",
    "data": json.decode(geoJson),
    "cluster": true,
    "clusterRadius": 50,
    "clusterMaxZoom": 14,
  };

  String encodedSourceData = jsonEncode(sourceData);

  if (!(await mapboxMap?.style.styleSourceExists(sourceId) ?? true)) {
    await mapboxMap?.style.addStyleSource(sourceId, encodedSourceData);
    print('"data" source added to the map style');
    _addClusteringLayers(sourceId);
  }
}
void _addClusteringLayers(String sourceId) async {
  // Add clusters layer
  var layer = '''
    {
      "id": "clusters",
      "type": "circle",
      "source": "$sourceId",
      "filter": ["has", "point_count"],
      "paint": {
        "circle-color": [
          "step",
          ["get", "point_count"],
          "#51bbd6",
          100,
          "#f1f075",
          750,
          "#f28cb1"
        ],
        "circle-radius": [
          "step",
          ["get", "point_count"],
          20,
          100,
          30,
          750,
          40
        ]
      }
    }
  ''';
  await mapboxMap?.style.addStyleLayer(layer, null);
  print('"clusters" layer added to the map style');

  // Add cluster count layer
  var clusterCountLayer = '''
    {
      "id": "cluster-count",
      "type": "symbol",
      "source": "$sourceId",
      "filter": ["has", "point_count"],
      "layout": {
        "text-field": "{point_count_abbreviated}",
        "text-font": ["DIN Offc Pro Medium", "Arial Unicode MS Bold"],
        "text-size": 12
      }
    }
  ''';
  await mapboxMap?.style.addStyleLayer(clusterCountLayer, null);
  print('"cluster-count" layer added to the map style');

  // Add unclustered point layer
  var unclusteredLayer = '''
    {
      "id": "unclustered-point",
      "type": "circle",
      "source": "$sourceId",
      "filter": ["!", ["has", "point_count"]],
      "paint": {
        "circle-color": "#f62323",
        "circle-radius": 40,
        "circle-stroke-width": 1,
        "circle-stroke-color": "#fff"
      }
    }
  ''';
  await mapboxMap?.style.addStyleLayer(unclusteredLayer, null);
  print('"unclustered-point" layer added to the map style');
}
BassetMan commented 1 week ago

Hi Jamey,

I had a similar issue trying to find a way to assign an event to both clustered and un-clustered annotations. In the end I came up with the below:

onTapListener: (context) async {
  var results = await mapboxMap!.queryRenderedFeatures(
      RenderedQueryGeometry(value: jsonEncode(context.touchPosition.encode()), type: Type.SCREEN_COORDINATE),
      RenderedQueryOptions(layerIds: ["unclustered-point", "cluster-count"])
  );

  // process the results
...
},
...

Add an onTapListener to your MapWidget, then use the queryRenderedFeatures method to query the tapped area. You can specify which layer(s) you are interested in querying in the RenderedQueryOptions. This returns an array of results (if any). You can then either get the first result or iterate the matches. For clustered items I then use getGeoJsonClusterExpansionZoom to zoom in upon tapping a clustered item.

I'm not sure if this is the best method, but it was the only solution I could find.