Skip to content
This repository was archived by the owner on Jun 23, 2025. It is now read-only.

Commit 5cbc515

Browse files
jigfoxsebholstein
authored andcommitted
feat(*): add clustered markers support
Closes #1044
1 parent dd9a087 commit 5cbc515

19 files changed

Lines changed: 1276 additions & 31 deletions

docs/content/api-docs/index.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ title = "API Docs for Angular Google Maps"
99
* [@agm/core](./agm-core/modules/AgmCoreModule.html)
1010
Provides Angular integration solutions for the official Google Maps Core API v3
1111
* [@agm/snazzy-info-window](./agm-snazzy-info-window/modules/AgmSnazzyInfoWindowModule.html)
12-
Styled Info Windows with [Snazzy Info Window](https://github.com/atmist/snazzy-info-window)
12+
Styled Info Windows with [Snazzy Info Window](https://github.com/atmist/snazzy-info-window)
13+
* [@agm/js-marker-clusterer](./js-marker-clusterer/modules/AgmJsMarkerClusterer.html)
14+
Clustered markers with [googlemaps/js-marker-clusterer](https://github.com/googlemaps/js-marker-clusterer)

package.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "@agm/dummy",
2+
"name": "@agm/_dev",
33
"private": true,
44
"version": "1.0.0-beta.0",
55
"description": "Angular 2+ components for Google Maps",
@@ -25,8 +25,9 @@
2525
"packagejson": "node ./scripts/create-package-json.js",
2626
"copyassets": "node ./scripts/copy-package-assets.js",
2727
"scripts": "npm run ngc:esm && npm run bundle",
28-
"bundle": "npm run bundle:umd:core",
28+
"bundle": "npm run bundle:umd:core && npm run bundle:umd:jsmarkerclusterer",
2929
"bundle:umd:core": "rollup -c rollup.core.config.js",
30+
"bundle:umd:jsmarkerclusterer": "rollup -c rollup.js-marker-clusterer.config.js",
3031
"ngc:esm": "ngc -p tsconfig.json",
3132
"clang:format": "clang-format --glob=packages/**/*.ts -i",
3233
"postngc:esm": "mkdir -p dist/ && cp -R .tmp/esm/* dist/ && rimraf packages/**/*.ngfactory.ts",
@@ -35,9 +36,10 @@
3536
"ci": "npm run build && npm run test",
3637
"docs:clean": "rimraf docs/public",
3738
"docs:hugo": "cd docs && hugo",
38-
"docs": "npm run docs:hugo && npm run apidocs:core && npm run apidocs:snazzy",
39+
"docs": "npm run docs:hugo && npm run apidocs:core && npm run apidocs:snazzy && npm run apidocs:jsmarkerclusterer",
3940
"apidocs:core": "compodoc -p packages/core/tsconfig.compodoc.json --name @agm/core --output docs/public/api-docs/agm-core/ --disablePrivateOrInternalSupport --hideGenerator --disableCoverage",
40-
"apidocs:snazzy": "compodoc -p packages/snazzy-info-window/tsconfig.compodoc.json --name @agm/snazzy-info-window --output docs/public/api-docs/agm-snazzy-info-window/ --disablePrivateOrInternalSupport --hideGenerator --disableCoverage"
41+
"apidocs:snazzy": "compodoc -p packages/snazzy-info-window/tsconfig.compodoc.json --name @agm/snazzy-info-window --output docs/public/api-docs/agm-snazzy-info-window/ --disablePrivateOrInternalSupport --hideGenerator --disableCoverage",
42+
"apidocs:jsmarkerclusterer": "compodoc -p packages/js-marker-clusterer/tsconfig.compodoc.json --name @agm/js-marker-clusterer --output docs/public/api-docs/js-marker-clusterer/ --disablePrivateOrInternalSupport --hideGenerator --disableCoverage"
4143
},
4244
"author": "Sebastian Müller <info@sebastian-mueller.net>",
4345
"license": "MIT",
@@ -73,6 +75,7 @@
7375
"html-webpack-plugin": "^2.8.1",
7476
"istanbul-instrumenter-loader": "^2.0.0",
7577
"jasmine-core": "2.5.0",
78+
"js-marker-clusterer": "^1.0.0",
7679
"karma": "^1.3.0",
7780
"karma-chrome-launcher": "2.0.0",
7881
"karma-coverage": "^1.1.1",

packages/core/services/google-maps-api-wrapper.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,12 @@ export class GoogleMapsAPIWrapper {
3939
/**
4040
* Creates a google map marker with the map context
4141
*/
42-
createMarker(options: mapTypes.MarkerOptions = <mapTypes.MarkerOptions>{}):
42+
createMarker(options: mapTypes.MarkerOptions = <mapTypes.MarkerOptions>{}, addToMap: boolean = true):
4343
Promise<mapTypes.Marker> {
4444
return this._map.then((map: mapTypes.GoogleMap) => {
45-
options.map = map;
45+
if (addToMap) {
46+
options.map = map;
47+
}
4648
return new google.maps.Marker(options);
4749
});
4850
}

packages/core/services/managers/marker-manager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import {Marker} from './../google-maps-types';
99

1010
@Injectable()
1111
export class MarkerManager {
12-
private _markers: Map<AgmMarker, Promise<Marker>> =
12+
protected _markers: Map<AgmMarker, Promise<Marker>> =
1313
new Map<AgmMarker, Promise<Marker>>();
1414

15-
constructor(private _mapsWrapper: GoogleMapsAPIWrapper, private _zone: NgZone) {}
15+
constructor(protected _mapsWrapper: GoogleMapsAPIWrapper, protected _zone: NgZone) {}
1616

1717
deleteMarker(marker: AgmMarker): Promise<void> {
1818
const m = this._markers.get(marker);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {AgmMarkerCluster} from './directives/marker-cluster';
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import {Directive, Input, OnDestroy, OnChanges, OnInit, SimpleChange} from '@angular/core';
2+
3+
import {ClusterManager} from '../services/managers/cluster-manager';
4+
import {MarkerManager} from '@agm/core';
5+
6+
import {ClusterOptions, ClusterStyle} from '../services/google-clusterer-types';
7+
8+
/**
9+
* AgmMarkerCluster clusters map marker if they are near together
10+
*
11+
* ### Example
12+
* ```typescript
13+
* import { Component } from '@angular/core';
14+
*
15+
* @Component({
16+
* selector: 'my-map-cmp',
17+
* styles: [`
18+
* agm-map {
19+
* height: 300px;
20+
* }
21+
* `],
22+
* template: `
23+
* <agm-map [latitude]="lat" [longitude]="lng" [zoom]="zoom">
24+
* <agm-marker-cluster>
25+
* <agm-marker [latitude]="lat" [longitude]="lng" [label]="'M'">
26+
* </agm-marker>
27+
* <agm-marker [latitude]="lat2" [longitude]="lng2" [label]="'N'">
28+
* </agm-marker>
29+
* </agm-marker-cluster>
30+
* </agm-map>
31+
* `
32+
* })
33+
* ```
34+
*/
35+
@Directive({
36+
selector: 'agm-marker-cluster',
37+
providers: [ClusterManager, {provide: MarkerManager, useExisting: ClusterManager}]
38+
})
39+
export class AgmMarkerCluster implements OnDestroy, OnChanges, OnInit, ClusterOptions {
40+
/**
41+
* The grid size of a cluster in pixels
42+
*/
43+
@Input() gridSize: number;
44+
45+
/**
46+
* The maximum zoom level that a marker can be part of a cluster.
47+
*/
48+
@Input() maxZoom: number;
49+
50+
/**
51+
* Whether the default behaviour of clicking on a cluster is to zoom into it.
52+
*/
53+
@Input() zoomOnClick: boolean;
54+
55+
/**
56+
* Whether the center of each cluster should be the average of all markers in the cluster.
57+
*/
58+
@Input() averageCenter: boolean;
59+
60+
/**
61+
* The minimum number of markers to be in a cluster before the markers are hidden and a count is shown.
62+
*/
63+
@Input() minimumClusterSize: number;
64+
65+
/**
66+
* An object that has style properties.
67+
*/
68+
@Input() styles: ClusterStyle;
69+
70+
@Input() imagePath: string;
71+
@Input() imageExtension: string;
72+
73+
constructor(private _clusterManager: ClusterManager) {}
74+
75+
/** @internal */
76+
ngOnDestroy() {
77+
this._clusterManager.clearMarkers();
78+
}
79+
80+
/** @internal */
81+
ngOnChanges(changes: {[key: string]: SimpleChange }) {
82+
if (changes['gridSize']) {
83+
this._clusterManager.setGridSize(this);
84+
}
85+
if (changes['maxZoom']) {
86+
this._clusterManager.setMaxZoom(this);
87+
}
88+
if (changes['styles']) {
89+
this._clusterManager.setStyles(this);
90+
}
91+
if (changes['zoomOnClick']) {
92+
this._clusterManager.setZoomOnClick(this);
93+
}
94+
if (changes['averageCenter']) {
95+
this._clusterManager.setAverageCenter(this);
96+
}
97+
if (changes['minimumClusterSize']) {
98+
this._clusterManager.setMinimumClusterSize(this);
99+
}
100+
if (changes['styles']) {
101+
this._clusterManager.setStyles(this);
102+
}
103+
if (changes['imagePath']) {
104+
this._clusterManager.setImagePath(this);
105+
}
106+
if (changes['imageExtension']) {
107+
this._clusterManager.setImageExtension(this);
108+
}
109+
}
110+
111+
/** @internal */
112+
ngOnInit() {
113+
this._clusterManager.init({
114+
gridSize: this.gridSize,
115+
maxZoom: this.maxZoom,
116+
zoomOnClick: this.zoomOnClick,
117+
averageCenter: this.averageCenter,
118+
minimumClusterSize: this.minimumClusterSize,
119+
styles: this.styles,
120+
imagePath: this.imagePath,
121+
imageExtension: this.imageExtension,
122+
});
123+
}
124+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// main modules
2+
export * from './directives';
3+
export * from './services';
4+
5+
// we explicitly export the module here to prevent this Ionic 2 bug:
6+
// http://stevemichelotti.com/integrate-angular-2-google-maps-into-ionic-2/
7+
export { AgmJsMarkerClustererModule } from './js-marker-clusterer.module';
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {NgModule} from '@angular/core';
2+
import {AgmCoreModule} from '@agm/core';
3+
import {AgmMarkerCluster} from './directives/marker-cluster';
4+
5+
@NgModule({
6+
imports: [AgmCoreModule],
7+
declarations: [AgmMarkerCluster],
8+
exports: [AgmMarkerCluster]
9+
})
10+
export class AgmJsMarkerClustererModule {
11+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "@agm/clusterer-src",
3+
"dependencies": {
4+
"@agm/core": "file:../core"
5+
}
6+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "@agm/js-marker-clusterer",
3+
"version": "INSERT_HERE_VIA_BUILD_PROCESS",
4+
"private": true,
5+
"description": "Angular Google Maps (AGM) extension for js-marker-clusterer support",
6+
"repository": {
7+
"type": "git",
8+
"url": "git+https://github.com/SebastianM/angular-google-maps.git"
9+
},
10+
"keywords": [
11+
"angular-google-maps",
12+
"angular",
13+
"js-marker-clusterer",
14+
"google-maps",
15+
"agm"
16+
],
17+
"author": "Jens Fahnenbruck <jigfox@me.com>",
18+
"license": "MIT",
19+
"bugs": {
20+
"url": "https://github.com/SebastianM/angular-google-maps/issues"
21+
},
22+
"peerDependencies": {
23+
"@angular/core": "^4.0.0",
24+
"@agm/core": "^1.0.0-beta.0",
25+
"js-marker-clusterer": "^1.0.0"
26+
},
27+
"homepage": "https://github.com/SebastianM/angular-google-maps#readme"
28+
}

0 commit comments

Comments
 (0)