googlemaps / googlemaps/js-markerclusterer

SuperClusterAlgorithm Properties Not Accessible in Renderer

Open
#1,068 0 comments 0 reactions 0 assignees View on GitHub
triage me type: bug
Dominant language
TypeScript
Stars
293
Forks
104
Avg merge
3m
Merged PRs (30d)
18

Description

# SuperClusterAlgorithm Properties Not Accessible in Renderer

## Issue Description

When using `SuperClusterAlgorithm` with custom `map` and `reduce` functions in `@googlemaps/markerclusterer`, the aggregated properties computed by the algorithm are not accessible in the renderer function. The cluster object passed to the renderer does not contain the properties that were calculated and returned by the algorithm's map/reduce functions.

## Expected Behavior

The aggregated properties computed by SuperCluster's map/reduce functions should be accessible in the renderer, either:
1. Through `cluster.markers[0].properties`
2. Through the `stats` parameter
3. Through some other documented mechanism

## Actual Behavior

- `cluster.markers[0].properties` is `undefined`
- `stats` parameter doesn't contain the aggregated properties
- No way to access the computed aggregation data in the renderer

## Reproduction Code

```typescript
import { SuperClusterAlgorithm, MarkerClusterer } from '@googlemaps/markerclusterer';

// Define aggregated properties interface
interface AggregatedProps {
total_assets: number;
asset_type_counts: Record;
selected_count: number;
status_counts: Record;
most_severe_status: string;
}

// Create SuperCluster algorithm with map/reduce functions
const algorithm = new SuperClusterAlgorithm({
maxZoom: 22,
radius: 80,

// MAP: Process individual markers
map: (props) => {
const aggregated: AggregatedProps = {
total_assets: 1,
asset_type_counts: { [props.assetType]: 1 },
selected_count: props.isSelected ? 1 : 0,
status_counts: { [props.status]: 1 },
most_severe_status: props.status,
};
console.log('MAP function returning:', aggregated);
return aggregated;
},

// REDUCE: Merge properties of clusters
reduce: (accumulated, props) => {
accumulated.total_assets += props.total_assets;

// Merge asset type counts
for (const type in props.asset_type_counts) {
accumulated.asset_type_counts[type] =
(accumulated.asset_type_counts[type] || 0) + props.asset_type_counts[type];
}

// Merge other properties...
accumulated.selected_count += props.selected_count;

console.log('REDUCE function result:', accumulated);
},
});

// Custom renderer that should access aggregated properties
const renderer = {
render: (cluster, stats, map) => {
console.log('Renderer cluster:', cluster);
console.log('Renderer stats:', stats);
console.log('Cluster markers:', cluster.markers);
console.log('First marker properties:', cluster.markers[0]?.properties);

// ❌ These are all undefined:
// - cluster.markers[0].properties
// - stats.properties
// - stats (doesn't contain our aggregated data)

// Expected: Access to AggregatedProps computed by map/reduce
// Actual: No access to computed aggregation data

// Create marker without access to aggregated data
return new google.maps.Marker({
position: cluster.position,
label: String(cluster.count), // Can only use basic cluster.count
});
}
};

// Initialize clusterer
const clusterer = new MarkerClusterer({
map: googleMap,
algorithm: algorithm,
renderer: renderer,
});
```

## Console Output

```
MAP function returning: { total_assets: 1, asset_type_counts: {...}, ... }
REDUCE function result: { total_assets: 5, asset_type_counts: {...}, ... }
Renderer cluster: { count: 5, position: {...}, markers: [...] }
Renderer stats: { ... } // Basic stats, no custom properties
Cluster markers: [Marker, Marker, Marker, Marker, Marker]
First marker properties: undefined
```

## Environment

- `@googlemaps/markerclusterer`: `^2.5.3`
- TypeScript: `^5.0.0`
- Browser: Chrome 118+

## Workaround

Currently having to manually re-aggregate data in the renderer by processing `cluster.markers`, which defeats the purpose of using SuperCluster's efficient map/reduce aggregation:

```typescript
const renderer = {
render: (cluster, stats, map) => {
// Manual re-aggregation (inefficient)
const clusterAssets = cluster.markers.map(getAssetFromMarker);
const manuallyAggregated = {
total_assets: clusterAssets.length,
selected_count: clusterAssets.filter(a => a.isSelected).length,
// ... re-compute everything the algorithm already computed
};

// Use manually aggregated data for rendering
return createMarkerWithAggregatedData(manuallyAggregated);
}
};
```

## Suggested Fix

The renderer should have access to the aggregated properties, ideally through one of these approaches:

1. **Option 1**: `cluster.properties` containing the aggregated data
2. **Option 2**: `cluster.markers[0].properties` containing the aggregated data
3. **Option 3**: `stats` parameter containing the aggregated data
4. **Option 4**: Additional parameter with aggregated data

## Related Documentation

Could not find documentation on how to access custom properties computed by SuperCluster's map/reduce functions in the MarkerClusterer renderer.

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the issue with SuperClusterAlgorithm's map/reduce functions and the MarkerClusterer renderer, then inspect how the cluster and stats values are passed to render. Define and document a supported path for the aggregated properties, and verify that the renderer can access them without re-aggregating marker data.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
frontend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.