Skip to content

Commit c70aae1

Browse files
committed
feat(google-maps): implement new marker clusterer
Adds a new `MapMarkerClusterer` component that is based on the most up-to-date clustering library, and supports both regular and advanced markers. Fixes #23695.
1 parent a05475e commit c70aae1

File tree

13 files changed

+799
-40
lines changed

13 files changed

+799
-40
lines changed

src/dev-app/google-map/google-map-demo.html

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,25 @@
1010
(mapRightclick)="handleRightclick()"
1111
[mapTypeId]="mapTypeId"
1212
[mapId]="mapId">
13-
<deprecated-map-marker-clusterer [imagePath]="markerClustererImagePath">
14-
<map-marker #firstMarker="mapMarker"
15-
[position]="center"
16-
(mapClick)="infoWindow.open(firstMarker)"></map-marker>
13+
<map-marker-clusterer>
14+
<map-advanced-marker
15+
#firstMarker="mapAdvancedMarker"
16+
[position]="center"
17+
(mapClick)="infoWindow.open(firstMarker)"></map-advanced-marker>
1718
@for (markerPosition of markerPositions; track markerPosition) {
18-
<map-marker #marker="mapMarker"
19+
<map-advanced-marker #marker="mapAdvancedMarker"
1920
[position]="markerPosition"
20-
[options]="markerOptions"
21-
(mapClick)="infoWindow.open(marker)"></map-marker>
21+
(mapClick)="infoWindow.open(marker)"></map-advanced-marker>
2222
}
23-
</deprecated-map-marker-clusterer>
24-
@if (hasAdvancedMarker) {
23+
</map-marker-clusterer>
24+
@if (hasCustomContentMarker) {
2525
<map-advanced-marker
2626
#secondMarker="mapAdvancedMarker"
2727
(mapClick)="infoWindow.open(secondMarker)"
2828
title="Advanced Marker"
2929
[gmpDraggable]="false"
30-
[content]="hasAdvancedMarkerCustomContent ? advancedMarkerContent : null"
30+
[content]="advancedMarkerContent"
3131
[position]="mapAdvancedMarkerPosition">
32-
3332
<svg #advancedMarkerContent fill="oklch(69.02% .277 332.77)" viewBox="0 0 960 960" width="50px" height="50px" xml:space="preserve">
3433
<g>
3534
<polygon points="562.6,109.8 804.1,629.5 829.2,233.1"/>
@@ -216,18 +215,8 @@
216215

217216
<div>
218217
<label>
219-
Toggle Advanced Marker
220-
<input type="checkbox" [(ngModel)]="hasAdvancedMarker">
221-
</label>
222-
</div>
223-
224-
<div>
225-
<label>
226-
Toggle custom content for Advanced Marker
227-
<input
228-
type="checkbox"
229-
[(ngModel)]="hasAdvancedMarkerCustomContent"
230-
[disabled]="!hasAdvancedMarker">
218+
Toggle Advanced Marker with custom content
219+
<input type="checkbox" [(ngModel)]="hasCustomContentMarker">
231220
</label>
232221
</div>
233222

src/dev-app/google-map/google-map-demo.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,12 @@ import {
2525
MapHeatmapLayer,
2626
MapInfoWindow,
2727
MapKmlLayer,
28-
MapMarker,
29-
DeprecatedMapMarkerClusterer,
3028
MapPolygon,
3129
MapPolyline,
3230
MapRectangle,
3331
MapTrafficLayer,
3432
MapTransitLayer,
33+
MapMarkerClusterer,
3534
} from '@angular/google-maps';
3635

3736
const POLYLINE_PATH: google.maps.LatLngLiteral[] = [
@@ -75,8 +74,7 @@ let apiLoadingPromise: Promise<unknown> | null = null;
7574
MapHeatmapLayer,
7675
MapInfoWindow,
7776
MapKmlLayer,
78-
MapMarker,
79-
DeprecatedMapMarkerClusterer,
77+
MapMarkerClusterer,
8078
MapAdvancedMarker,
8179
MapPolygon,
8280
MapPolyline,
@@ -98,7 +96,6 @@ export class GoogleMapDemo {
9896

9997
center = {lat: 24, lng: 12};
10098
mapAdvancedMarkerPosition = {lat: 22, lng: 21};
101-
markerOptions = {draggable: false};
10299
markerPositions: google.maps.LatLngLiteral[] = [];
103100
zoom = 4;
104101
display?: google.maps.LatLngLiteral;
@@ -153,17 +150,13 @@ export class GoogleMapDemo {
153150
isTrafficLayerDisplayed = false;
154151
isTransitLayerDisplayed = false;
155152
isBicyclingLayerDisplayed = false;
156-
hasAdvancedMarker = false;
157-
hasAdvancedMarkerCustomContent = true;
153+
hasCustomContentMarker = false;
158154
// This is necessary for testing advanced markers. It seems like any value works locally.
159155
mapId = '123';
160156

161157
mapTypeId: google.maps.MapTypeId;
162158
mapTypeIds = ['hybrid', 'roadmap', 'satellite', 'terrain'] as google.maps.MapTypeId[];
163159

164-
markerClustererImagePath =
165-
'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m';
166-
167160
directionsResult?: google.maps.DirectionsResult;
168161

169162
constructor() {
@@ -262,7 +255,7 @@ export class GoogleMapDemo {
262255

263256
if (!apiLoadingPromise) {
264257
apiLoadingPromise = this._loadScript(
265-
'https://unpkg.com/@googlemaps/markerclustererplus/dist/index.min.js',
258+
'https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js',
266259
);
267260
}
268261

src/google-maps/google-maps-module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {MapTrafficLayer} from './map-traffic-layer/map-traffic-layer';
2525
import {MapTransitLayer} from './map-transit-layer/map-transit-layer';
2626
import {MapHeatmapLayer} from './map-heatmap-layer/map-heatmap-layer';
2727
import {MapAdvancedMarker} from './map-advanced-marker/map-advanced-marker';
28+
import {MapMarkerClusterer} from './map-marker-clusterer/map-marker-clusterer';
2829

2930
const COMPONENTS = [
3031
GoogleMap,
@@ -44,6 +45,7 @@ const COMPONENTS = [
4445
MapRectangle,
4546
MapTrafficLayer,
4647
MapTransitLayer,
48+
MapMarkerClusterer,
4749
];
4850

4951
@NgModule({

src/google-maps/map-advanced-marker/map-advanced-marker.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ import {
2424

2525
import {GoogleMap} from '../google-map/google-map';
2626
import {MapEventManager} from '../map-event-manager';
27-
import {Observable} from 'rxjs';
2827
import {MapAnchorPoint} from '../map-anchor-point';
28+
import {MAP_MARKER, MarkerDirective} from '../marker-utilities';
29+
import {Observable} from 'rxjs';
30+
import {take} from 'rxjs/operators';
2931

3032
/**
3133
* Default options for the Google Maps marker component. Displays a marker
@@ -43,8 +45,16 @@ export const DEFAULT_MARKER_OPTIONS = {
4345
@Directive({
4446
selector: 'map-advanced-marker',
4547
exportAs: 'mapAdvancedMarker',
48+
providers: [
49+
{
50+
provide: MAP_MARKER,
51+
useExisting: MapAdvancedMarker,
52+
},
53+
],
4654
})
47-
export class MapAdvancedMarker implements OnInit, OnChanges, OnDestroy, MapAnchorPoint {
55+
export class MapAdvancedMarker
56+
implements OnInit, OnChanges, OnDestroy, MapAnchorPoint, MarkerDirective
57+
{
4858
private readonly _googleMap = inject(GoogleMap);
4959
private _ngZone = inject(NgZone);
5060
private _eventManager = new MapEventManager(inject(NgZone));
@@ -262,6 +272,13 @@ export class MapAdvancedMarker implements OnInit, OnChanges, OnDestroy, MapAncho
262272
return this.advancedMarker;
263273
}
264274

275+
/** Returns a promise that resolves when the marker has been initialized. */
276+
_resolveMarker(): Promise<google.maps.marker.AdvancedMarkerElement> {
277+
return this.advancedMarker
278+
? Promise.resolve(this.advancedMarker)
279+
: this.markerInitialized.pipe(take(1)).toPromise();
280+
}
281+
265282
/** Creates a combined options object using the passed-in options and the individual inputs. */
266283
private _combineOptions(): google.maps.marker.AdvancedMarkerElementOptions {
267284
const options = this._options || DEFAULT_MARKER_OPTIONS;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# MapMarkerClusterer
2+
3+
The `MapMarkerClusterer` component wraps the [`MarkerClusterer` class](https://googlemaps.github.io/js-markerclusterer/classes/MarkerClusterer.html) from the [Google Maps JavaScript MarkerClusterer Library](https://github.com/googlemaps/js-markerclusterer). The `MapMarkerClusterer` component displays a cluster of markers that are children of the `<map-marker-clusterer>` tag. Unlike the other Google Maps components, MapMarkerClusterer does not have an `options` input, so any input (listed in the [documentation](https://googlemaps.github.io/js-markerclusterer/) for the `MarkerClusterer` class) should be set directly.
4+
5+
## Loading the Library
6+
7+
Like the Google Maps JavaScript API, the MarkerClusterer library needs to be loaded separately. This can be accomplished by using this script tag:
8+
9+
```html
10+
<script src="https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js"></script>
11+
```
12+
13+
Additional information can be found by looking at [Marker Clustering](https://developers.google.com/maps/documentation/javascript/marker-clustering) in the Google Maps JavaScript API documentation.
14+
15+
## Example
16+
17+
```typescript
18+
// google-map-demo.component.ts
19+
import {Component} from '@angular/core';
20+
import {GoogleMap, MapMarkerClusterer, MapAdvancedMarker} from '@angular/google-maps';
21+
22+
@Component({
23+
selector: 'google-map-demo',
24+
templateUrl: 'google-map-demo.html',
25+
imports: [GoogleMap, MapMarkerClusterer, MapAdvancedMarker],
26+
})
27+
export class GoogleMapDemo {
28+
center: google.maps.LatLngLiteral = {lat: 24, lng: 12};
29+
zoom = 4;
30+
markerPositions: google.maps.LatLngLiteral[] = [];
31+
32+
addMarker(event: google.maps.MapMouseEvent) {
33+
this.markerPositions.push(event.latLng.toJSON());
34+
}
35+
}
36+
```
37+
38+
```html
39+
<google-map
40+
height="400px"
41+
width="750px"
42+
[center]="center"
43+
[zoom]="zoom"
44+
(mapClick)="addMarker($event)">
45+
<map-marker-clusterer>
46+
@for (markerPosition of markerPositions; track $index) {
47+
<map-advanced-marker [position]="markerPosition"/>
48+
}
49+
</map-marker-clusterer>
50+
</google-map>
51+
```

0 commit comments

Comments
 (0)