Closed gwendall closed 1 year ago
Can you provide a minimal reproducible live test case? It's impossible to tell otherwise.
Same issue for me. When zoom level is under 4.5 all clusters and markers disappear.
You can fin code used to define cluster:
interface IClusterProps extends React.PropsWithChildren<any> {
minZoom?: number;
maxZoom?: number;
radius?: number;
extent?: number;
nodeSize?: number;
}
interface IClusterComponentProps extends IClusterProps {
map: MB.MapRef;
}
interface IClusterComponentState {
clusters: (supercluster.ClusterFeature<supercluster.AnyProps> | supercluster.PointFeature<supercluster.AnyProps>)[];
}
const childrenKeys = (children: any) => React.Children.toArray(children).map((child: any) => child.key);
const shallowCompareChildren = (prevChildren: any, newChildren: any) => {
if (React.Children.count(prevChildren) !== React.Children.count(newChildren)) {
return false;
}
const prevKeys = childrenKeys(prevChildren);
const newKeys = new Set(childrenKeys(newChildren));
return prevKeys.length === newKeys.size && prevKeys.every((key) => newKeys.has(key));
};
class ClusterComponent extends React.Component<IClusterComponentProps, IClusterComponentState, {}> {
private cluster?: supercluster;
private readonly defaultOptions: IClusterProps = {
minZoom: 0,
maxZoom: 16,
minPoints: 2,
radius: 40,
extent: 512,
nodeSize: 64,
};
constructor(props: any) {
super(props);
this.state = { clusters: [] };
}
componentDidMount() {
this.createCluster(this.props);
this.recalculate();
this.props.map.on('moveend', this.recalculate);
}
componentDidUpdate(prevProps: Readonly<IClusterComponentProps>, prevState: Readonly<IClusterComponentState>, snapshot?: {} | undefined): void {
const shouldUpdate =
prevProps.minZoom !== this.props.minZoom ||
prevProps.maxZoom !== this.props.maxZoom ||
prevProps.radius !== this.props.radius ||
prevProps.extent !== this.props.extent ||
prevProps.nodeSize !== this.props.nodeSize ||
!shallowCompareChildren(prevProps.children, this.props.children);
if (shouldUpdate) {
this.createCluster(this.props);
this.recalculate();
}
}
private createCluster = (props: IClusterProps) => {
const options = { ...this.defaultOptions, ...props };
const cluster = new supercluster(options);
const points = React.Children.map(props.children, (child) => {
if (child) {
return point([child.props.longitude, child.props.latitude], child);
}
return null;
});
cluster.load(points ?? []);
this.cluster = cluster;
if (props.innerRef) {
props.innerRef(this.cluster);
}
};
private recalculate = () => {
if (!this.cluster) {
return;
}
const zoom = this.props.map.getZoom();
const bounds = this.props.map.getBounds().toArray();
const bbox: GeoJSON.BBox = [bounds[0][0], bounds[0][1], bounds[1][0], bounds[1][1]];
const clusters = this.cluster.getClusters(bbox, Math.floor(zoom));
this.setState({ clusters: clusters });
};
render() {
const clusters = this.state.clusters.map((cluster) => {
if (cluster.properties.cluster) {
const [longitude, latitude] = cluster.geometry.coordinates;
return (
<Marker key={`cluster-${cluster.properties.cluster_id}`} latitude={latitude} longitude={longitude} style={{ zIndex: 1 }}>
<div
className="marker-cluster"
onClick={(e) => {
if (this.cluster && cluster.id) {
const expansionZoom = Math.min(this.cluster.getClusterExpansionZoom(cluster.id as number), 20);
this.props.map.setZoom(expansionZoom);
this.props.map.panTo({ lat: latitude, lng: longitude });
}
}}
>
{cluster.properties.point_count <= 99 ? cluster.properties.point_count : '+99'}
</div>
</Marker>
);
}
const { type, key, props } = cluster.properties;
return React.createElement(type, { key, ...props });
});
return clusters;
}
}
export default function Cluster(props: IClusterProps) {
const map = useMap();
return map.current ? <ClusterComponent map={map.current} {...props}></ClusterComponent> : null;
}
export default class Map extends React.Component<IMapProps, IMapState, any> {
render() {
return (
<MB.Map
ref={(o) => (this.mapRef = o)}
initialViewState={{
longitude: this.state.currentPosition.longitude,
latitude: this.state.currentPosition.latitude,
zoom: this.state.currentZoom,
}}
mapStyle={this.props.settings.style}
mapboxAccessToken={this.props.settings.accessToken}
projection={this.props.settings.projection}
onMoveEnd={(e: MB.ViewStateChangeEvent) => this.setCurrentMapState({ latitude: e.viewState.latitude, longitude: e.viewState.longitude }, e.viewState.zoom)}
>
<Cluster maxZoom={this.props.settings.maxZoom - 1}>
{this.state.pushPins.features.map((f) => (
<MB.Marker key={f.id} latitude={f.properties.latitude} longitude={f.properties.longitude} onClick={() => this.onMarkerClick(f.properties)}>
<div className="wander-icon">
<div className="marker-pin">
<img src="/assets/AnchorIcon.svg" alt='anchor icon' />
</div>
</div>
</MB.Marker>
))}
</Cluster>
</MB.Map>
);
}
}
I created a project hosted in codesandbox. So you can see the bug and all code at https://codesandbox.io/s/aged-waterfall-fqe64k
After some tests, this issue is only present with Globe
projection. If I use Mercator
all is fine.
I hope it will help you to identify this issue.
Hi, I am facing a similar issue this may help, we have placed the map in a resizable container when we drag and resize the container this happens although not always
Thanks for the reproduction @Spaeda.
I'm unable to reproduce this in vanilla GL JS, so I can't tell if this is coming from GL JS, your code, or possibly from react-map-gl
. Are you able to reproduce this without React?
Thanks for reply @SnailBones
I test with vanilla JS and I have same issue (https://jsfiddle.net/Spaeda/snrkLz5d/4/).
Like I said previously, it happens only with globe
projection.
In this example, you can find this warning:
Globe projection does not support getBounds API, this API may behave unexpectedly
Could this be source of our issue?
According to Mapbox GL JS documentation, it exists some limitations with Globe projection.
So issue seem to come from the impossibility to get clusters with a BBox in Globe
projection with a lower zoom level.
How can we get clusters for lower zoom?
Thanks @Spaeda!
Globe projection does not support getBounds API, this API may behave unexpectedly
Could this be source of our issue?
Yes, this is likely the cause of this issue. We're working on a fix for getBounds with globe and it will be included in the next GL JS release.
In the meantime, here's a workaround: at low zooms, set the bound to the entire world, like so:
const bbox = [-180, -90, 180, 80];
const zoom = map.getZoom();
const clusters = index.getClusters(bbox, Math.floor(zoom));
Thx for workaround. I will wait new release of Mapbox GL JS
Same problem here! Im using google-maps-react-markers
with use-supercluster
hook. Any approach?
@oviedo97fer What version of mapbox-gl-js
are you using? This should be fixed as of v2.11.0.
Likely fixed on the GL JS side so closing.
Any idea why?