421 lines
54 KiB
JavaScript
421 lines
54 KiB
JavaScript
|
/**
|
||
|
* @license
|
||
|
* Copyright Google LLC All Rights Reserved.
|
||
|
*
|
||
|
* Use of this source code is governed by an MIT-style license that can be
|
||
|
* found in the LICENSE file at https://angular.io/license
|
||
|
*/
|
||
|
// Workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1265
|
||
|
/// <reference types="google.maps" preserve="true" />
|
||
|
import { ChangeDetectionStrategy, Component, ContentChildren, EventEmitter, Input, NgZone, Output, QueryList, ViewEncapsulation, inject, } from '@angular/core';
|
||
|
import { Observable, Subject } from 'rxjs';
|
||
|
import { take, takeUntil } from 'rxjs/operators';
|
||
|
import { GoogleMap } from '../google-map/google-map';
|
||
|
import { MapEventManager } from '../map-event-manager';
|
||
|
import { MapMarker } from '../map-marker/map-marker';
|
||
|
import * as i0 from "@angular/core";
|
||
|
import * as i1 from "../google-map/google-map";
|
||
|
/** Default options for a clusterer. */
|
||
|
const DEFAULT_CLUSTERER_OPTIONS = {};
|
||
|
/**
|
||
|
* Angular component for implementing a Google Maps Marker Clusterer.
|
||
|
*
|
||
|
* See https://developers.google.com/maps/documentation/javascript/marker-clustering
|
||
|
*/
|
||
|
export class MapMarkerClusterer {
|
||
|
set averageCenter(averageCenter) {
|
||
|
this._averageCenter = averageCenter;
|
||
|
}
|
||
|
set batchSizeIE(batchSizeIE) {
|
||
|
this._batchSizeIE = batchSizeIE;
|
||
|
}
|
||
|
set calculator(calculator) {
|
||
|
this._calculator = calculator;
|
||
|
}
|
||
|
set clusterClass(clusterClass) {
|
||
|
this._clusterClass = clusterClass;
|
||
|
}
|
||
|
set enableRetinaIcons(enableRetinaIcons) {
|
||
|
this._enableRetinaIcons = enableRetinaIcons;
|
||
|
}
|
||
|
set gridSize(gridSize) {
|
||
|
this._gridSize = gridSize;
|
||
|
}
|
||
|
set ignoreHidden(ignoreHidden) {
|
||
|
this._ignoreHidden = ignoreHidden;
|
||
|
}
|
||
|
set imageExtension(imageExtension) {
|
||
|
this._imageExtension = imageExtension;
|
||
|
}
|
||
|
set imagePath(imagePath) {
|
||
|
this._imagePath = imagePath;
|
||
|
}
|
||
|
set imageSizes(imageSizes) {
|
||
|
this._imageSizes = imageSizes;
|
||
|
}
|
||
|
set maxZoom(maxZoom) {
|
||
|
this._maxZoom = maxZoom;
|
||
|
}
|
||
|
set minimumClusterSize(minimumClusterSize) {
|
||
|
this._minimumClusterSize = minimumClusterSize;
|
||
|
}
|
||
|
set styles(styles) {
|
||
|
this._styles = styles;
|
||
|
}
|
||
|
set title(title) {
|
||
|
this._title = title;
|
||
|
}
|
||
|
set zIndex(zIndex) {
|
||
|
this._zIndex = zIndex;
|
||
|
}
|
||
|
set zoomOnClick(zoomOnClick) {
|
||
|
this._zoomOnClick = zoomOnClick;
|
||
|
}
|
||
|
set options(options) {
|
||
|
this._options = options;
|
||
|
}
|
||
|
constructor(_googleMap, _ngZone) {
|
||
|
this._googleMap = _googleMap;
|
||
|
this._ngZone = _ngZone;
|
||
|
this._currentMarkers = new Set();
|
||
|
this._eventManager = new MapEventManager(inject(NgZone));
|
||
|
this._destroy = new Subject();
|
||
|
this.ariaLabelFn = () => '';
|
||
|
/**
|
||
|
* See
|
||
|
* googlemaps.github.io/v3-utility-library/modules/
|
||
|
* _google_markerclustererplus.html#clusteringbegin
|
||
|
*/
|
||
|
this.clusteringbegin = this._eventManager.getLazyEmitter('clusteringbegin');
|
||
|
/**
|
||
|
* See
|
||
|
* googlemaps.github.io/v3-utility-library/modules/_google_markerclustererplus.html#clusteringend
|
||
|
*/
|
||
|
this.clusteringend = this._eventManager.getLazyEmitter('clusteringend');
|
||
|
/** Emits when a cluster has been clicked. */
|
||
|
this.clusterClick = this._eventManager.getLazyEmitter('click');
|
||
|
/** Event emitted when the clusterer is initialized. */
|
||
|
this.markerClustererInitialized = new EventEmitter();
|
||
|
this._canInitialize = _googleMap._isBrowser;
|
||
|
}
|
||
|
ngOnInit() {
|
||
|
if (this._canInitialize) {
|
||
|
this._ngZone.runOutsideAngular(() => {
|
||
|
this._googleMap._resolveMap().then(map => {
|
||
|
if (typeof MarkerClusterer !== 'function' &&
|
||
|
(typeof ngDevMode === 'undefined' || ngDevMode)) {
|
||
|
throw Error('MarkerClusterer class not found, cannot construct a marker cluster. ' +
|
||
|
'Please install the MarkerClustererPlus library: ' +
|
||
|
'https://github.com/googlemaps/js-markerclustererplus');
|
||
|
}
|
||
|
// Create the object outside the zone so its events don't trigger change detection.
|
||
|
// We'll bring it back in inside the `MapEventManager` only for the events that the
|
||
|
// user has subscribed to.
|
||
|
this.markerClusterer = this._ngZone.runOutsideAngular(() => {
|
||
|
return new MarkerClusterer(map, [], this._combineOptions());
|
||
|
});
|
||
|
this._assertInitialized();
|
||
|
this._eventManager.setTarget(this.markerClusterer);
|
||
|
this.markerClustererInitialized.emit(this.markerClusterer);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
ngAfterContentInit() {
|
||
|
if (this._canInitialize) {
|
||
|
if (this.markerClusterer) {
|
||
|
this._watchForMarkerChanges();
|
||
|
}
|
||
|
else {
|
||
|
this.markerClustererInitialized
|
||
|
.pipe(take(1), takeUntil(this._destroy))
|
||
|
.subscribe(() => this._watchForMarkerChanges());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ngOnChanges(changes) {
|
||
|
const { markerClusterer: clusterer, ariaLabelFn, _averageCenter, _batchSizeIE, _calculator, _styles, _clusterClass, _enableRetinaIcons, _gridSize, _ignoreHidden, _imageExtension, _imagePath, _imageSizes, _maxZoom, _minimumClusterSize, _title, _zIndex, _zoomOnClick, } = this;
|
||
|
if (clusterer) {
|
||
|
if (changes['options']) {
|
||
|
clusterer.setOptions(this._combineOptions());
|
||
|
}
|
||
|
if (changes['ariaLabelFn']) {
|
||
|
clusterer.ariaLabelFn = ariaLabelFn;
|
||
|
}
|
||
|
if (changes['averageCenter'] && _averageCenter !== undefined) {
|
||
|
clusterer.setAverageCenter(_averageCenter);
|
||
|
}
|
||
|
if (changes['batchSizeIE'] && _batchSizeIE !== undefined) {
|
||
|
clusterer.setBatchSizeIE(_batchSizeIE);
|
||
|
}
|
||
|
if (changes['calculator'] && !!_calculator) {
|
||
|
clusterer.setCalculator(_calculator);
|
||
|
}
|
||
|
if (changes['clusterClass'] && _clusterClass !== undefined) {
|
||
|
clusterer.setClusterClass(_clusterClass);
|
||
|
}
|
||
|
if (changes['enableRetinaIcons'] && _enableRetinaIcons !== undefined) {
|
||
|
clusterer.setEnableRetinaIcons(_enableRetinaIcons);
|
||
|
}
|
||
|
if (changes['gridSize'] && _gridSize !== undefined) {
|
||
|
clusterer.setGridSize(_gridSize);
|
||
|
}
|
||
|
if (changes['ignoreHidden'] && _ignoreHidden !== undefined) {
|
||
|
clusterer.setIgnoreHidden(_ignoreHidden);
|
||
|
}
|
||
|
if (changes['imageExtension'] && _imageExtension !== undefined) {
|
||
|
clusterer.setImageExtension(_imageExtension);
|
||
|
}
|
||
|
if (changes['imagePath'] && _imagePath !== undefined) {
|
||
|
clusterer.setImagePath(_imagePath);
|
||
|
}
|
||
|
if (changes['imageSizes'] && _imageSizes) {
|
||
|
clusterer.setImageSizes(_imageSizes);
|
||
|
}
|
||
|
if (changes['maxZoom'] && _maxZoom !== undefined) {
|
||
|
clusterer.setMaxZoom(_maxZoom);
|
||
|
}
|
||
|
if (changes['minimumClusterSize'] && _minimumClusterSize !== undefined) {
|
||
|
clusterer.setMinimumClusterSize(_minimumClusterSize);
|
||
|
}
|
||
|
if (changes['styles'] && _styles) {
|
||
|
clusterer.setStyles(_styles);
|
||
|
}
|
||
|
if (changes['title'] && _title !== undefined) {
|
||
|
clusterer.setTitle(_title);
|
||
|
}
|
||
|
if (changes['zIndex'] && _zIndex !== undefined) {
|
||
|
clusterer.setZIndex(_zIndex);
|
||
|
}
|
||
|
if (changes['zoomOnClick'] && _zoomOnClick !== undefined) {
|
||
|
clusterer.setZoomOnClick(_zoomOnClick);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ngOnDestroy() {
|
||
|
this._destroy.next();
|
||
|
this._destroy.complete();
|
||
|
this._eventManager.destroy();
|
||
|
this.markerClusterer?.setMap(null);
|
||
|
}
|
||
|
fitMapToMarkers(padding) {
|
||
|
this._assertInitialized();
|
||
|
this.markerClusterer.fitMapToMarkers(padding);
|
||
|
}
|
||
|
getAverageCenter() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getAverageCenter();
|
||
|
}
|
||
|
getBatchSizeIE() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getBatchSizeIE();
|
||
|
}
|
||
|
getCalculator() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getCalculator();
|
||
|
}
|
||
|
getClusterClass() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getClusterClass();
|
||
|
}
|
||
|
getClusters() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getClusters();
|
||
|
}
|
||
|
getEnableRetinaIcons() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getEnableRetinaIcons();
|
||
|
}
|
||
|
getGridSize() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getGridSize();
|
||
|
}
|
||
|
getIgnoreHidden() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getIgnoreHidden();
|
||
|
}
|
||
|
getImageExtension() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getImageExtension();
|
||
|
}
|
||
|
getImagePath() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getImagePath();
|
||
|
}
|
||
|
getImageSizes() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getImageSizes();
|
||
|
}
|
||
|
getMaxZoom() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getMaxZoom();
|
||
|
}
|
||
|
getMinimumClusterSize() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getMinimumClusterSize();
|
||
|
}
|
||
|
getStyles() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getStyles();
|
||
|
}
|
||
|
getTitle() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getTitle();
|
||
|
}
|
||
|
getTotalClusters() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getTotalClusters();
|
||
|
}
|
||
|
getTotalMarkers() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getTotalMarkers();
|
||
|
}
|
||
|
getZIndex() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getZIndex();
|
||
|
}
|
||
|
getZoomOnClick() {
|
||
|
this._assertInitialized();
|
||
|
return this.markerClusterer.getZoomOnClick();
|
||
|
}
|
||
|
_combineOptions() {
|
||
|
const options = this._options || DEFAULT_CLUSTERER_OPTIONS;
|
||
|
return {
|
||
|
...options,
|
||
|
ariaLabelFn: this.ariaLabelFn ?? options.ariaLabelFn,
|
||
|
averageCenter: this._averageCenter ?? options.averageCenter,
|
||
|
batchSize: this.batchSize ?? options.batchSize,
|
||
|
batchSizeIE: this._batchSizeIE ?? options.batchSizeIE,
|
||
|
calculator: this._calculator ?? options.calculator,
|
||
|
clusterClass: this._clusterClass ?? options.clusterClass,
|
||
|
enableRetinaIcons: this._enableRetinaIcons ?? options.enableRetinaIcons,
|
||
|
gridSize: this._gridSize ?? options.gridSize,
|
||
|
ignoreHidden: this._ignoreHidden ?? options.ignoreHidden,
|
||
|
imageExtension: this._imageExtension ?? options.imageExtension,
|
||
|
imagePath: this._imagePath ?? options.imagePath,
|
||
|
imageSizes: this._imageSizes ?? options.imageSizes,
|
||
|
maxZoom: this._maxZoom ?? options.maxZoom,
|
||
|
minimumClusterSize: this._minimumClusterSize ?? options.minimumClusterSize,
|
||
|
styles: this._styles ?? options.styles,
|
||
|
title: this._title ?? options.title,
|
||
|
zIndex: this._zIndex ?? options.zIndex,
|
||
|
zoomOnClick: this._zoomOnClick ?? options.zoomOnClick,
|
||
|
};
|
||
|
}
|
||
|
_watchForMarkerChanges() {
|
||
|
this._assertInitialized();
|
||
|
this._ngZone.runOutsideAngular(() => {
|
||
|
this._getInternalMarkers(this._markers).then(markers => {
|
||
|
const initialMarkers = [];
|
||
|
for (const marker of markers) {
|
||
|
this._currentMarkers.add(marker);
|
||
|
initialMarkers.push(marker);
|
||
|
}
|
||
|
this.markerClusterer.addMarkers(initialMarkers);
|
||
|
});
|
||
|
});
|
||
|
this._markers.changes
|
||
|
.pipe(takeUntil(this._destroy))
|
||
|
.subscribe((markerComponents) => {
|
||
|
this._assertInitialized();
|
||
|
this._ngZone.runOutsideAngular(() => {
|
||
|
this._getInternalMarkers(markerComponents).then(markers => {
|
||
|
const newMarkers = new Set(markers);
|
||
|
const markersToAdd = [];
|
||
|
const markersToRemove = [];
|
||
|
for (const marker of Array.from(newMarkers)) {
|
||
|
if (!this._currentMarkers.has(marker)) {
|
||
|
this._currentMarkers.add(marker);
|
||
|
markersToAdd.push(marker);
|
||
|
}
|
||
|
}
|
||
|
for (const marker of Array.from(this._currentMarkers)) {
|
||
|
if (!newMarkers.has(marker)) {
|
||
|
markersToRemove.push(marker);
|
||
|
}
|
||
|
}
|
||
|
this.markerClusterer.addMarkers(markersToAdd, true);
|
||
|
this.markerClusterer.removeMarkers(markersToRemove, true);
|
||
|
this.markerClusterer.repaint();
|
||
|
for (const marker of markersToRemove) {
|
||
|
this._currentMarkers.delete(marker);
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
_getInternalMarkers(markers) {
|
||
|
return Promise.all(markers.map(markerComponent => markerComponent._resolveMarker()));
|
||
|
}
|
||
|
_assertInitialized() {
|
||
|
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||
|
if (!this.markerClusterer) {
|
||
|
throw Error('Cannot interact with a MarkerClusterer before it has been initialized. ' +
|
||
|
'Please wait for the MarkerClusterer to load before trying to interact with it.');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0-next.2", ngImport: i0, type: MapMarkerClusterer, deps: [{ token: i1.GoogleMap }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); }
|
||
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.0-next.2", type: MapMarkerClusterer, isStandalone: true, selector: "map-marker-clusterer", inputs: { ariaLabelFn: "ariaLabelFn", averageCenter: "averageCenter", batchSize: "batchSize", batchSizeIE: "batchSizeIE", calculator: "calculator", clusterClass: "clusterClass", enableRetinaIcons: "enableRetinaIcons", gridSize: "gridSize", ignoreHidden: "ignoreHidden", imageExtension: "imageExtension", imagePath: "imagePath", imageSizes: "imageSizes", maxZoom: "maxZoom", minimumClusterSize: "minimumClusterSize", styles: "styles", title: "title", zIndex: "zIndex", zoomOnClick: "zoomOnClick", options: "options" }, outputs: { clusteringbegin: "clusteringbegin", clusteringend: "clusteringend", clusterClick: "clusterClick", markerClustererInitialized: "markerClustererInitialized" }, queries: [{ propertyName: "_markers", predicate: MapMarker, descendants: true }], exportAs: ["mapMarkerClusterer"], usesOnChanges: true, ngImport: i0, template: '<ng-content />', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
||
|
}
|
||
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0-next.2", ngImport: i0, type: MapMarkerClusterer, decorators: [{
|
||
|
type: Component,
|
||
|
args: [{
|
||
|
selector: 'map-marker-clusterer',
|
||
|
exportAs: 'mapMarkerClusterer',
|
||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||
|
standalone: true,
|
||
|
template: '<ng-content />',
|
||
|
encapsulation: ViewEncapsulation.None,
|
||
|
}]
|
||
|
}], ctorParameters: () => [{ type: i1.GoogleMap }, { type: i0.NgZone }], propDecorators: { ariaLabelFn: [{
|
||
|
type: Input
|
||
|
}], averageCenter: [{
|
||
|
type: Input
|
||
|
}], batchSize: [{
|
||
|
type: Input
|
||
|
}], batchSizeIE: [{
|
||
|
type: Input
|
||
|
}], calculator: [{
|
||
|
type: Input
|
||
|
}], clusterClass: [{
|
||
|
type: Input
|
||
|
}], enableRetinaIcons: [{
|
||
|
type: Input
|
||
|
}], gridSize: [{
|
||
|
type: Input
|
||
|
}], ignoreHidden: [{
|
||
|
type: Input
|
||
|
}], imageExtension: [{
|
||
|
type: Input
|
||
|
}], imagePath: [{
|
||
|
type: Input
|
||
|
}], imageSizes: [{
|
||
|
type: Input
|
||
|
}], maxZoom: [{
|
||
|
type: Input
|
||
|
}], minimumClusterSize: [{
|
||
|
type: Input
|
||
|
}], styles: [{
|
||
|
type: Input
|
||
|
}], title: [{
|
||
|
type: Input
|
||
|
}], zIndex: [{
|
||
|
type: Input
|
||
|
}], zoomOnClick: [{
|
||
|
type: Input
|
||
|
}], options: [{
|
||
|
type: Input
|
||
|
}], clusteringbegin: [{
|
||
|
type: Output
|
||
|
}], clusteringend: [{
|
||
|
type: Output
|
||
|
}], clusterClick: [{
|
||
|
type: Output
|
||
|
}], _markers: [{
|
||
|
type: ContentChildren,
|
||
|
args: [MapMarker, { descendants: true }]
|
||
|
}], markerClustererInitialized: [{
|
||
|
type: Output
|
||
|
}] } });
|
||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFwLW1hcmtlci1jbHVzdGVyZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvZ29vZ2xlLW1hcHMvbWFwLW1hcmtlci1jbHVzdGVyZXIvbWFwLW1hcmtlci1jbHVzdGVyZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HO0FBRUgseUVBQXlFO0FBQ3pFLHFEQUFxRDtBQUVyRCxPQUFPLEVBRUwsdUJBQXVCLEVBQ3ZCLFNBQVMsRUFDVCxlQUFlLEVBQ2YsWUFBWSxFQUNaLEtBQUssRUFDTCxNQUFNLEVBSU4sTUFBTSxFQUNOLFNBQVMsRUFFVCxpQkFBaUIsRUFDakIsTUFBTSxHQUNQLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBQyxVQUFVLEVBQUUsT0FBTyxFQUFDLE1BQU0sTUFBTSxDQUFDO0FBQ3pDLE9BQU8sRUFBQyxJQUFJLEVBQUUsU0FBUyxFQUFDLE1BQU0sZ0JBQWdCLENBQUM7QUFFL0MsT0FBTyxFQUFDLFNBQVMsRUFBQyxNQUFNLDBCQUEwQixDQUFDO0FBQ25ELE9BQU8sRUFBQyxlQUFlLEVBQUMsTUFBTSxzQkFBc0IsQ0FBQztBQUNyRCxPQUFPLEVBQUMsU0FBUyxFQUFDLE1BQU0sMEJBQTBCLENBQUM7OztBQVVuRCx1Q0FBdUM7QUFDdkMsTUFBTSx5QkFBeUIsR0FBMkIsRUFBRSxDQUFDO0FBUTdEOzs7O0dBSUc7QUFTSCxNQUFNLE9BQU8sa0JBQWtCO0lBVzdCLElBQ0ksYUFBYSxDQUFDLGFBQXNCO1FBQ3RDLElBQUksQ0FBQyxjQUFjLEdBQUcsYUFBYSxDQUFDO0lBQ3RDLENBQUM7SUFLRCxJQUNJLFdBQVcsQ0FBQyxXQUFtQjtRQUNqQyxJQUFJLENBQUMsWUFBWSxHQUFHLFdBQVcsQ0FBQztJQUNsQyxDQUFDO0lBR0QsSUFDSSxVQUFVLENBQUMsVUFBc0I7UUFDbkMsSUFBSSxDQUFDLFdBQVcsR0FBRyxVQUFVLENBQUM7SUFDaEMsQ0FBQztJQUdELElB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
|