Skip to content

MapBox Cluster Focus Implementation Investigation #1036

@vladc-eastwolf

Description

@vladc-eastwolf

My flutter setup:
Flutter 3.29.1 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 09de023485 (7 months ago) • 2025-02-28 13:44:05 -0800
Engine • revision 871f65ac1b
Tools • Dart 3.7.0 • DevTools 2.42.2
Currently using version: 2.11.0 for the mapbox-maps-flutter

During development of a package tracking application with MapBox Flutter SDK, I attempted to implement focus functionality for map clusters that would visually highlight clusters(by enlarging their markers) when the onCameraChanged is triggered by dragging the map and a shipment from the cluster is focused.

Attempted Solutions:
1. Feature-State API Approach (Failed)
Method: Applied the same feature-state API used for individual shipments to clusters.
Implementation: Used setFeatureState('shipments-source', null, clusterId, '{"focused":true}')
Result: No visual effect. Clusters did not respond to feature-state changes.
Cause: MapBox documentation states "feature-state is not available for clusters"

2. ClusterProperties Approach (Performance Issues)
Method: Used clusterProperties to aggregate focus state from constituent shipments.
Implementation: Configured cluster aggregation to track focused shipments within clusters.
Result: Functional but severe performance degradation.
Cause: Required full GeoJSON source rebuilds on every focus change, causing 99.7% slower performance compared to feature-state updates.

3. Abandonment and Accidental Discovery
Decision: Abandoned cluster focus functionality due to performance constraints.
Discovery: During testing, observed mysterious behavior where certain clusters would unexpectedly enlarge when focusing on specific shipments.

The Undocumented Inheritance Discovery
Finding: Focusing on Shipment ID 4 caused its containing cluster to enlarge because MapBox's clustering algorithm had designated Shipment 4 as the cluster representative, giving the cluster ID "4" and causing it to inherit the shipment's focused feature-state.
Root Cause: When shipment becomes cluster representative point, MapBox assigns the cluster the same ID as the representative shipment.

The reason I discovered this was that I had some leftover code from the feature-state attempt on the cluster maker:

Future<void> _addClusterLayers() async {
    // Normal cluster layer
    final clusterLayerNormal = SymbolLayer(
      id: 'shipment-clusters-normal',
      sourceId: 'shipments-source',
      filter: <Object>[
        'has', 'point_count', // Simple cluster detection
      ],
      iconImage: 'cluster-{point_count}', // MapBox will interpolate the value
      iconSize: 1,
      iconOpacityExpression: [
        'case',
        [
          '==',
          ['feature-state', 'focused'],
          true,
        ],
        0.0, // Hidden when focused
        1.0, // Visible when not focused
      ],
      iconAllowOverlap: true,
      iconIgnorePlacement: true,
    );

    // Focused cluster layer
    final clusterLayerFocused = SymbolLayer(
      id: 'shipment-clusters-focused',
      sourceId: 'shipments-source',
      filter: <Object>[
        'has', 'point_count', // Simple cluster detection
      ],
      iconImage: 'cluster-{point_count}-focused', // MapBox will interpolate the value with "-focused" suffix
      iconSize: 1, // The image itself is already 1.3x scaled
      iconOpacityExpression: [
        'case',
        [
          '==',
          ['feature-state', 'focused'],
          true,
        ],
        1.0, // Visible when focused
        0.0, // Hidden when not focused
      ],
      iconAllowOverlap: true,
      iconIgnorePlacement: true,
    );

    await _mapboxController.style.addLayer(clusterLayerNormal);
    await _mapboxController.style.addLayer(clusterLayerFocused);
    debugPrint('[GeoJsonManager] ✅ Added dual cluster layers with opacity switching');

    // Add detailed logging for cluster investigation
    await logClusterDebugInfo();
  }

This behavior presents an opportunity to achieve the originally desired cluster focus functionality through a performant feature-state approach rather than costly GeoJSON source rebuilds. However, questions remain:

  1. Are there methods to leverage this inheritance pattern more reliably?
  2. Can we programmatically influence representative point selection to ensure focused shipments become cluster representatives?
  3. Does MapBox recommend alternative approaches for cluster focus management that maintain the performance benefits of feature-state while providing consistent behavior across all cluster scenarios? Understanding whether this ID collision pattern can be utilized systematically, or if there are other undiscovered methods for efficient cluster styling, would significantly impact the implementation strategy for cluster focus functionality.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions