Skip to content

Commit

Permalink
feat: 커스텀 마커 클러스터러, 커스텀 클러스터 알고리즘 #222
Browse files Browse the repository at this point in the history
  • Loading branch information
1119wj committed Dec 3, 2024
1 parent 764e12c commit 5043362
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 4 deletions.
73 changes: 69 additions & 4 deletions frontend/src/lib/CustomMarkerClusterer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
ClusterStats,
Marker,
MarkerClusterer,
MarkerClustererEvents,
Expand All @@ -17,11 +18,20 @@ export class CustomMarkerClusterer extends MarkerClusterer {
this.markerLatLngSet = new Set();
}

public onAdd(): void {
const map = this.getMap();
if (!map) return;
this.idleListener = map.addListener('idle', () => {
this.render();
});
this.render();
}

public addMarker(
marker: google.maps.marker.AdvancedMarkerElement,
noDraw?: boolean,
): void {
const markerLatLng = `${marker.position?.lat}${marker.position?.lng}`;
const markerLatLng = `${marker.position?.lat} ${marker.position?.lng}`;
if (this.markerLatLngSet.has(markerLatLng)) {
return;
}
Expand All @@ -34,6 +44,29 @@ export class CustomMarkerClusterer extends MarkerClusterer {
}
}

public removeMarker(
marker: google.maps.marker.AdvancedMarkerElement,
noDraw?: boolean,
): boolean {
const index = this.markers.indexOf(marker);
if (index === -1) {
// Marker is not in our list of markers, so do nothing:
return false;
}

const markerLatLng = `${marker.position?.lat} ${marker.position?.lng}`;
this.markerLatLngSet.delete(markerLatLng);

MarkerUtils.setMap(marker, null);
this.markers.splice(index, 1); // Remove the marker from the list of managed markers

if (!noDraw) {
this.render();
}

return true;
}

public render(): void {
const map = this.getMap();
if (map instanceof google.maps.Map && map.getProjection()) {
Expand All @@ -47,7 +80,7 @@ export class CustomMarkerClusterer extends MarkerClusterer {
map,
mapCanvasProjection: this.getProjection(),
});

console.log(changed, 'changed');
// Allow algorithms to return flag on whether the clusters/markers have changed.
if (changed || changed === undefined) {
// Accumulate the markers of the clusters composed of a single marker.
Expand All @@ -71,7 +104,6 @@ export class CustomMarkerClusterer extends MarkerClusterer {
// The marker:
// - was previously rendered because it is from a cluster with 1 marker,
// - should no more be rendered as it is not in singleMarker.
console.log('cluster.marker removed', cluster.marker);
MarkerUtils.setMap(cluster.marker!, null);
}
} else {
Expand All @@ -83,7 +115,7 @@ export class CustomMarkerClusterer extends MarkerClusterer {
this.clusters = clusters;
this.renderClusters();
// Delayed removal of the markers of the former groups.
console.log('groupMarkers', groupMarkers);

setTimeout(() => {
groupMarkers.forEach((marker) => {
MarkerUtils.setMap(marker, null);
Expand All @@ -97,6 +129,39 @@ export class CustomMarkerClusterer extends MarkerClusterer {
);
}
}

protected renderClusters(): void {
// Generate stats to pass to renderers.
const stats = new ClusterStats(this.markers, this.clusters);

const map = this.getMap() as google.maps.Map;

this.clusters.forEach((cluster) => {
if (cluster.markers?.length === 1) {
cluster.marker = cluster.markers[0];
} else {
// Generate the marker to represent the group.
cluster.marker = this.renderer.render(cluster, stats, map);
// Make sure all individual markers are removed from the map.
cluster.markers?.forEach((marker) => MarkerUtils.setMap(marker, null));
if (this.onClusterClick) {
cluster.marker.addListener(
'click',
/* istanbul ignore next */
(event: google.maps.MapMouseEvent) => {
google.maps.event.trigger(
this,
MarkerClustererEvents.CLUSTER_CLICK,
cluster,
);
this.onClusterClick(event, cluster, map);
},
);
}
}
MarkerUtils.setMap(cluster.marker, map);
});
}
}

export const clustererOptions = {
Expand Down
52 changes: 52 additions & 0 deletions frontend/src/lib/CustomSuperCluseterAlgorithm.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,62 @@
import {
AlgorithmInput,
AlgorithmOutput,
getPaddedViewport,
MarkerUtils,
SuperClusterViewportAlgorithm,
SuperClusterViewportOptions,
SuperClusterViewportState,
} from '@googlemaps/markerclusterer';
import equal from 'fast-deep-equal';

export class CustomSuperClusterAlgorithm extends SuperClusterViewportAlgorithm {
constructor({ ...options }: SuperClusterViewportOptions) {
super(options);
this.clusters = [];
}

public calculate(input: AlgorithmInput): AlgorithmOutput {
const state: SuperClusterViewportState = {
zoom: Math.round(input.map.getZoom()!),
view: getPaddedViewport(
input.map.getBounds()!,
input.mapCanvasProjection,
this.viewportPadding,
),
};

let changed = !equal(this.state, state);
if (!equal(input.markers, this.markers)) {
// TODO use proxy to avoid copy?
this.markers = [...input.markers];

const points = this.markers.map((marker) => {
const position = MarkerUtils.getPosition(marker);
const coordinates = [position.lng(), position.lat()];
return {
type: 'Feature' as const,
geometry: {
type: 'Point' as const,
coordinates,
},
properties: { marker },
};
});
this.superCluster.load(points);
}

const newClusters = this.cluster(input);
console.log(newClusters.length, this.clusters.length);
//this.clusters.length !== newClusters.length
//!equal(this.clusters, newClusters)
if (this.clusters.length !== newClusters.length) {
this.clusters = newClusters;
} else {
changed = false;
}

this.state = state;

return { clusters: this.clusters, changed };
}
}

0 comments on commit 5043362

Please sign in to comment.