{"version":3,"file":"markerclustererplus.min.js","sources":["../src/overlay-view-safe.ts","../src/cluster-icon.ts","../src/cluster.ts","../src/markerclusterer.ts"],"sourcesContent":["/**\n * Copyright 2019 Google LLC. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface OverlayViewSafe extends google.maps.OverlayView {}\n\n/**\n * Extends an object's prototype by another's.\n *\n * @param type1 The Type to be extended.\n * @param type2 The Type to extend with.\n * @ignore\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction extend(type1: any, type2: any): void {\n for (const property in type2.prototype) {\n type1.prototype[property] = type2.prototype[property];\n }\n}\n\n/**\n * @ignore\n */\nexport class OverlayViewSafe {\n constructor() {\n // MarkerClusterer implements google.maps.OverlayView interface. We use the\n // extend function to extend MarkerClusterer with google.maps.OverlayView\n // because it might not always be available when the code is defined so we\n // look for it at the last possible moment. If it doesn't exist now then\n // there is no point going ahead :)\n extend(OverlayViewSafe, google.maps.OverlayView);\n }\n}\n","/**\n * Copyright 2019 Google LLC. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * This class represents the object for values in the `styles` array passed\n * to the {@link MarkerClusterer} constructor. The element in this array that is used to\n * style the cluster icon is determined by calling the `calculator` function.\n */\nimport { Cluster } from \"./cluster\";\nimport { OverlayViewSafe } from \"./overlay-view-safe\";\n\nexport interface ClusterIconStyle {\n /** The URL of the cluster icon image file. Required. */\n url: string;\n /** Height The display height (in pixels) of the cluster icon. Required. */\n height: number;\n /** Width The display width (in pixels) of the cluster icon. Required. */\n width: number;\n /**\n * The position (in pixels) from the center of the cluster icon to\n * where the text label is to be centered and drawn. The format is `[yoffset, xoffset]`\n * where `yoffset` increases as you go down from center and `xoffset`\n * increases to the right of center. The default is `[0, 0]`.\n */\n anchorText?: [number, number];\n /**\n * The anchor position (in pixels) of the cluster icon. This is the\n * spot on the cluster icon that is to be aligned with the cluster position. The format is\n * `[yoffset, xoffset]` where `yoffset` increases as you go down and\n * `xoffset` increases to the right of the top-left corner of the icon. The default\n * anchor position is the center of the cluster icon.\n */\n anchorIcon?: [number, number];\n /**\n * The color of the label text shown on the cluster icon.\n * @default `\"black\"`\n */\n textColor?: string;\n /** The size (in pixels) of the label text shown on the cluster icon.\n * @default `11`\n */\n textSize?: number;\n /**\n * The value of the CSS `text-decoration`\n * property for the label text shown on the cluster icon.\n *\n * @default `\"none\"`\n */\n textDecoration?: string;\n /**\n * The value of the CSS `font-weight`\n * property for the label text shown on the cluster icon.\n *\n * @default `\"bold\"`\n */\n fontWeight?: string;\n /**\n * The value of the CSS `font-style`\n * property for the label text shown on the cluster icon.\n *\n * @default `\"normal\"`\n */\n fontStyle?: string;\n /**\n * The value of the CSS `font-family`\n * property for the label text shown on the cluster icon.\n * @default `\"Arial,sans-serif\"`\n */\n fontFamily?: string;\n /**\n * The position of the cluster icon image\n * within the image defined by `url`. The format is `\"xpos ypos\"`\n * (the same format as for the CSS `background-position` property). You must set\n * this property appropriately when the image defined by `url` represents a sprite\n * containing multiple images. Note that the position must be specified in px units.\n *\n * @default `\"0 0\"`\n */\n backgroundPosition?: string;\n}\n\n/**\n * @description This is an object containing general information about a cluster icon. This is\n * the object that a `calculator` function returns.\n */\nexport interface ClusterIconInfo {\n /**\n * The text of the label to be shown on the cluster icon.\n */\n text: string;\n /**\n * The index plus 1 of the element in the `styles`\n */\n index: number;\n /**\n * The tooltip to display when the mouse moves over the cluster icon.\n * If this value is `undefined` or `\"\"`, `title` is set to the\n * value of the `title` property passed to the MarkerClusterer.\n */\n title: string;\n}\n\n/**\n * A cluster icon.\n */\nexport class ClusterIcon extends OverlayViewSafe {\n private className_ = this.cluster_.getMarkerClusterer().getClusterClass();\n private center_: google.maps.LatLng = null;\n private div_: HTMLDivElement = null;\n private sums_: ClusterIconInfo = null;\n private visible_ = false;\n\n private url_: string;\n private height_: number;\n private width_: number;\n private anchorText_: [number, number];\n private anchorIcon_: [number, number];\n private textColor_: string;\n private textSize_: number;\n private textDecoration_: string;\n private fontWeight_: string;\n private fontStyle_: string;\n private fontFamily_: string;\n private backgroundPosition_: string;\n\n private boundsChangedListener_: google.maps.MapsEventListener;\n\n /**\n * @param cluster_ The cluster with which the icon is to be associated.\n * @param styles_ An array of {@link ClusterIconStyle} defining the cluster icons\n * to use for various cluster sizes.\n */\n constructor(private cluster_: Cluster, private styles_: ClusterIconStyle[]) {\n super();\n\n this.setMap(cluster_.getMap()); // Note: this causes onAdd to be called\n }\n\n /**\n * Adds the icon to the DOM.\n */\n onAdd(): void {\n let cMouseDownInCluster: boolean;\n let cDraggingMapByCluster: boolean;\n\n const mc = this.cluster_.getMarkerClusterer();\n\n const [major, minor] = google.maps.version.split(\".\");\n\n const gmVersion = parseInt(major, 10) * 100 + parseInt(minor, 10);\n\n this.div_ = document.createElement(\"div\");\n this.div_.className = this.className_;\n if (this.visible_) {\n this.show();\n }\n\n this.getPanes().overlayMouseTarget.appendChild(this.div_);\n\n // Fix for Issue 157\n this.boundsChangedListener_ = google.maps.event.addListener(\n this.getMap(),\n \"bounds_changed\",\n function() {\n cDraggingMapByCluster = cMouseDownInCluster;\n }\n );\n\n google.maps.event.addDomListener(this.div_, \"mousedown\", () => {\n cMouseDownInCluster = true;\n cDraggingMapByCluster = false;\n });\n\n // March 1, 2018: Fix for this 3.32 exp bug, https://issuetracker.google.com/issues/73571522\n // But it doesn't work with earlier releases so do a version check.\n if (gmVersion >= 332) {\n // Ugly version-dependent code\n google.maps.event.addDomListener(this.div_, \"touchstart\", e => {\n e.stopPropagation();\n });\n }\n\n google.maps.event.addDomListener(this.div_, \"click\", e => {\n cMouseDownInCluster = false;\n if (!cDraggingMapByCluster) {\n /**\n * This event is fired when a cluster marker is clicked.\n * @name MarkerClusterer#click\n * @param {Cluster} c The cluster that was clicked.\n * @event\n */\n google.maps.event.trigger(mc, \"click\", this.cluster_);\n google.maps.event.trigger(mc, \"clusterclick\", this.cluster_); // deprecated name\n\n // The default click handler follows. Disable it by setting\n // the zoomOnClick property to false.\n if (mc.getZoomOnClick()) {\n // Zoom into the cluster.\n const mz = mc.getMaxZoom();\n const theBounds = this.cluster_.getBounds();\n (mc.getMap() as google.maps.Map).fitBounds(theBounds);\n // There is a fix for Issue 170 here:\n setTimeout(function() {\n (mc.getMap() as google.maps.Map).fitBounds(theBounds);\n // Don't zoom beyond the max zoom level\n if (mz !== null && mc.getMap().getZoom() > mz) {\n mc.getMap().setZoom(mz + 1);\n }\n }, 100);\n }\n\n // Prevent event propagation to the map:\n e.cancelBubble = true;\n if (e.stopPropagation) {\n e.stopPropagation();\n }\n }\n });\n\n google.maps.event.addDomListener(this.div_, \"mouseover\", () => {\n /**\n * This event is fired when the mouse moves over a cluster marker.\n * @name MarkerClusterer#mouseover\n * @param {Cluster} c The cluster that the mouse moved over.\n * @event\n */\n google.maps.event.trigger(mc, \"mouseover\", this.cluster_);\n });\n\n google.maps.event.addDomListener(this.div_, \"mouseout\", () => {\n /**\n * This event is fired when the mouse moves out of a cluster marker.\n * @name MarkerClusterer#mouseout\n * @param {Cluster} c The cluster that the mouse moved out of.\n * @event\n */\n google.maps.event.trigger(mc, \"mouseout\", this.cluster_);\n });\n }\n\n /**\n * Removes the icon from the DOM.\n */\n onRemove(): void {\n if (this.div_ && this.div_.parentNode) {\n this.hide();\n google.maps.event.removeListener(this.boundsChangedListener_);\n google.maps.event.clearInstanceListeners(this.div_);\n this.div_.parentNode.removeChild(this.div_);\n this.div_ = null;\n }\n }\n\n /**\n * Draws the icon.\n */\n draw(): void {\n if (this.visible_) {\n const pos = this.getPosFromLatLng_(this.center_);\n this.div_.style.top = pos.y + \"px\";\n this.div_.style.left = pos.x + \"px\";\n }\n }\n\n /**\n * Hides the icon.\n */\n hide(): void {\n if (this.div_) {\n this.div_.style.display = \"none\";\n }\n this.visible_ = false;\n }\n\n /**\n * Positions and shows the icon.\n */\n show(): void {\n if (this.div_) {\n const mc = this.cluster_.getMarkerClusterer();\n const ariaLabel = mc.ariaLabelFn(this.sums_.text);\n // NOTE: values must be specified in px units\n const bp = this.backgroundPosition_.split(\" \");\n const spriteH = parseInt(bp[0].replace(/^\\s+|\\s+$/g, \"\"), 10);\n const spriteV = parseInt(bp[1].replace(/^\\s+|\\s+$/g, \"\"), 10);\n this.div_.style.cssText = this.createCss_(\n this.getPosFromLatLng_(this.center_)\n );\n\n let imgDimensions = \"\";\n if (this.cluster_.getMarkerClusterer().getEnableRetinaIcons()) {\n imgDimensions = `width: ${this.width_}px; height: ${this.height_}px`;\n } else {\n const [Y1, X1, Y2, X2] = [\n -1 * spriteV,\n -1 * spriteH + this.width_,\n -1 * spriteV + this.height_,\n -1 * spriteH\n ];\n\n imgDimensions = `clip: rect(${Y1}px, ${X1}px, ${Y2}px, ${X2}px)`;\n }\n\n const imgStyle = [\n `position: absolute`,\n `top: ${spriteV}px`,\n `left: ${spriteH}px`,\n imgDimensions\n ].join(\";\");\n\n const divStyle = [\n `position: absolute`,\n `top: ${this.anchorText_[0]}px`,\n `left: ${this.anchorText_[1]}px`,\n `color: ${this.textColor_}`,\n `font-size: ${this.textSize_}px`,\n `font-family: ${this.fontFamily_}`,\n `font-weight: ${this.fontWeight_}`,\n `font-style: ${this.fontStyle_}`,\n `text-decoration: ${this.textDecoration_}`,\n `text-align: center`,\n `width: ${this.width_}px`,\n `line-height: ${this.height_}px`\n ].join(\";\");\n\n this.div_.innerHTML = `\n${this.sums_.text}\n
\n ${this.sums_.text}\n
\n`;\n if (typeof this.sums_.title === \"undefined\" || this.sums_.title === \"\") {\n this.div_.title = this.cluster_.getMarkerClusterer().getTitle();\n } else {\n this.div_.title = this.sums_.title;\n }\n this.div_.style.display = \"\";\n }\n this.visible_ = true;\n }\n\n /**\n * Sets the icon styles to the appropriate element in the styles array.\n *\n * @ignore\n * @param sums The icon label text and styles index.\n */\n useStyle(sums: ClusterIconInfo): void {\n this.sums_ = sums;\n let index = Math.max(0, sums.index - 1);\n index = Math.min(this.styles_.length - 1, index);\n const style = this.styles_[index];\n this.url_ = style.url;\n this.height_ = style.height;\n this.width_ = style.width;\n this.anchorText_ = style.anchorText || [0, 0];\n this.anchorIcon_ = style.anchorIcon || [\n Math.floor(this.height_ / 2),\n Math.floor(this.width_ / 2)\n ];\n this.textColor_ = style.textColor || \"black\";\n this.textSize_ = style.textSize || 11;\n this.textDecoration_ = style.textDecoration || \"none\";\n this.fontWeight_ = style.fontWeight || \"bold\";\n this.fontStyle_ = style.fontStyle || \"normal\";\n this.fontFamily_ = style.fontFamily || \"Arial,sans-serif\";\n this.backgroundPosition_ = style.backgroundPosition || \"0 0\";\n }\n\n /**\n * Sets the position at which to center the icon.\n *\n * @param center The latlng to set as the center.\n */\n setCenter(center: google.maps.LatLng): void {\n this.center_ = center;\n }\n\n /**\n * Creates the `cssText` style parameter based on the position of the icon.\n *\n * @param pos The position of the icon.\n * @return The CSS style text.\n */\n private createCss_(pos: google.maps.Point): string {\n return [\n `z-index: ${this.cluster_.getMarkerClusterer().getZIndex()}`,\n \"cursor: pointer\",\n `position: absolute; top: ${pos.y}px; left: ${pos.x}px`,\n `width: ${this.width_}px; height: ${this.height_}px`,\n \"-webkit-user-select: none\",\n \"-khtml-user-select: none\",\n \"-moz-user-select: none\",\n \"-o-user-select: none\",\n \"user-select: none\"\n ].join(\";\");\n }\n\n /**\n * Returns the position at which to place the DIV depending on the latlng.\n *\n * @param latlng The position in latlng.\n * @return The position in pixels.\n */\n private getPosFromLatLng_(latlng: google.maps.LatLng): google.maps.Point {\n const pos = this.getProjection().fromLatLngToDivPixel(latlng);\n pos.x = Math.floor(pos.x - this.anchorIcon_[1]);\n pos.y = Math.floor(pos.y - this.anchorIcon_[0]);\n return pos;\n }\n}\n","/**\n * Copyright 2019 Google LLC. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { MarkerClusterer, ClusterAugmentedMarker } from \"./markerclusterer\";\nimport { ClusterIcon } from \"./cluster-icon\";\n\n/**\n * Creates a single cluster that manages a group of proximate markers.\n * Used internally, do not call this constructor directly.\n */\nexport class Cluster {\n private map_ = this.markerClusterer_.getMap() as google.maps.Map;\n private minClusterSize_: number = this.markerClusterer_.getMinimumClusterSize();\n private averageCenter_: boolean = this.markerClusterer_.getAverageCenter();\n private markers_: ClusterAugmentedMarker[] = []; // TODO: type;\n private center_: google.maps.LatLng = null;\n private bounds_: google.maps.LatLngBounds = null;\n private clusterIcon_ = new ClusterIcon(\n this,\n this.markerClusterer_.getStyles()\n );\n\n /**\n *\n * @param markerClusterer_ The `MarkerClusterer` object with which this\n * cluster is associated.\n */\n constructor(private markerClusterer_: MarkerClusterer) {}\n\n /**\n * Returns the number of markers managed by the cluster. You can call this from\n * a `click`, `mouseover`, or `mouseout` event handler for the `MarkerClusterer` object.\n *\n * @return The number of markers in the cluster.\n */\n public getSize(): number {\n return this.markers_.length;\n }\n\n /**\n * Returns the array of markers managed by the cluster. You can call this from\n * a `click`, `mouseover`, or `mouseout` event handler for the `MarkerClusterer` object.\n *\n * @return The array of markers in the cluster.\n */\n public getMarkers(): google.maps.Marker[] {\n return this.markers_;\n }\n\n /**\n * Returns the center of the cluster. You can call this from\n * a `click`, `mouseover`, or `mouseout` event handler\n * for the `MarkerClusterer` object.\n *\n * @return The center of the cluster.\n */\n public getCenter(): google.maps.LatLng {\n return this.center_;\n }\n\n /**\n * Returns the map with which the cluster is associated.\n *\n * @return The map.\n * @ignore\n */\n public getMap(): google.maps.Map {\n return this.map_;\n }\n\n /**\n * Returns the `MarkerClusterer` object with which the cluster is associated.\n *\n * @return The associated marker clusterer.\n * @ignore\n */\n public getMarkerClusterer(): MarkerClusterer {\n return this.markerClusterer_;\n }\n\n /**\n * Returns the bounds of the cluster.\n *\n * @return the cluster bounds.\n * @ignore\n */\n public getBounds(): google.maps.LatLngBounds {\n const bounds = new google.maps.LatLngBounds(this.center_, this.center_);\n const markers = this.getMarkers();\n for (let i = 0; i < markers.length; i++) {\n bounds.extend(markers[i].getPosition());\n }\n return bounds;\n }\n\n /**\n * Removes the cluster from the map.\n *\n * @ignore\n */\n public remove(): void {\n this.clusterIcon_.setMap(null);\n this.markers_ = [];\n delete this.markers_;\n }\n\n /**\n * Adds a marker to the cluster.\n *\n * @param marker The marker to be added.\n * @return True if the marker was added.\n * @ignore\n */\n public addMarker(\n marker: google.maps.Marker & { isAdded?: boolean }\n ): boolean {\n if (this.isMarkerAlreadyAdded_(marker)) {\n return false;\n }\n\n if (!this.center_) {\n this.center_ = marker.getPosition();\n this.calculateBounds_();\n } else {\n if (this.averageCenter_) {\n const l = this.markers_.length + 1;\n const lat =\n (this.center_.lat() * (l - 1) + marker.getPosition().lat()) / l;\n const lng =\n (this.center_.lng() * (l - 1) + marker.getPosition().lng()) / l;\n this.center_ = new google.maps.LatLng(lat, lng);\n this.calculateBounds_();\n }\n }\n\n marker.isAdded = true;\n this.markers_.push(marker);\n\n const mCount = this.markers_.length;\n const mz = this.markerClusterer_.getMaxZoom();\n if (mz !== null && this.map_.getZoom() > mz) {\n // Zoomed in past max zoom, so show the marker.\n if (marker.getMap() !== this.map_) {\n marker.setMap(this.map_);\n }\n } else if (mCount < this.minClusterSize_) {\n // Min cluster size not reached so show the marker.\n if (marker.getMap() !== this.map_) {\n marker.setMap(this.map_);\n }\n } else if (mCount === this.minClusterSize_) {\n // Hide the markers that were showing.\n for (let i = 0; i < mCount; i++) {\n this.markers_[i].setMap(null);\n }\n } else {\n marker.setMap(null);\n }\n\n this.updateIcon_();\n return true;\n }\n\n /**\n * Determines if a marker lies within the cluster's bounds.\n *\n * @param marker The marker to check.\n * @return True if the marker lies in the bounds.\n * @ignore\n */\n public isMarkerInClusterBounds(marker: google.maps.Marker): boolean {\n return this.bounds_.contains(marker.getPosition());\n }\n\n /**\n * Calculates the extended bounds of the cluster with the grid.\n */\n private calculateBounds_(): void {\n const bounds = new google.maps.LatLngBounds(this.center_, this.center_);\n this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds);\n }\n\n /**\n * Updates the cluster icon.\n */\n private updateIcon_(): void {\n const mCount = this.markers_.length;\n const mz = this.markerClusterer_.getMaxZoom();\n\n if (mz !== null && this.map_.getZoom() > mz) {\n this.clusterIcon_.hide();\n return;\n }\n\n if (mCount < this.minClusterSize_) {\n // Min cluster size not yet reached.\n this.clusterIcon_.hide();\n return;\n }\n\n const numStyles = this.markerClusterer_.getStyles().length;\n const sums = this.markerClusterer_.getCalculator()(\n this.markers_,\n numStyles\n );\n this.clusterIcon_.setCenter(this.center_);\n this.clusterIcon_.useStyle(sums);\n this.clusterIcon_.show();\n }\n\n /**\n * Determines if a marker has already been added to the cluster.\n *\n * @param marker The marker to check.\n * @return True if the marker has already been added.\n */\n private isMarkerAlreadyAdded_(marker: google.maps.Marker): boolean {\n if (this.markers_.indexOf) {\n return this.markers_.indexOf(marker) !== -1;\n } else {\n for (let i = 0; i < this.markers_.length; i++) {\n if (marker === this.markers_[i]) {\n return true;\n }\n }\n }\n return false;\n }\n}\n","/**\n * Copyright 2019 Google LLC. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @name MarkerClustererPlus for Google Maps V3\n * @author Gary Little\n * @fileoverview\n * The library creates and manages per-zoom-level clusters for large amounts of markers.\n *

\n * This is an enhanced V3 implementation of the V2 MarkerClusterer by Xiaoxi Wu. It is\n * based on the V3 MarkerClusterer port by Luke Mahe. MarkerClustererPlus was created\n * by Gary Little.\n *

\n * v2.0 release: MarkerClustererPlus v2.0 is backward compatible with MarkerClusterer v1.0. It\n * adds support for the `ignoreHidden`, `title`, `batchSizeIE`,\n * and `calculator` properties as well as support for four more events. It also allows\n * greater control over the styling of the text that appears on the cluster marker. The\n * documentation has been significantly improved and the overall code has been simplified and\n * polished. Very large numbers of markers can now be managed without causing Javascript timeout\n * errors on Internet Explorer. Note that the name of the `clusterclick` event has been\n * deprecated. The new name is `click`, so please change your application code now.\n */\n\nimport { Cluster } from \"./cluster\";\nimport { ClusterIconInfo, ClusterIconStyle } from \"./cluster-icon\";\nimport { OverlayViewSafe } from \"./overlay-view-safe\";\n\n/**\n * @param text The text attribute of the cluster\n */\ntype AriaLabelFn = (text: string) => string;\n\nexport type Calculator = (\n markers: google.maps.Marker[],\n clusterIconStylesCount: number\n) => ClusterIconInfo;\n\nexport interface ClusterAugmentedMarker extends google.maps.Marker {\n isAdded?: boolean;\n}\n\n/**\n * This event is fired on the {@link MarkerClusterer} instance when the `MarkerClusterer` stops clustering markers.\n *\n * Example:\n * ```typescript\n * mc.addListener('clusteringend', (mc: MarkerClusterer) => {})\n * ```\n *\n * @param mc The MarkerClusterer whose markers are being clustered.\n * @event clusteringend\n */\nexport declare function clusteringend(mc: MarkerClusterer): void;\n\n/**\n * This event is fired on the {@link MarkerClusterer} instance when the `MarkerClusterer` begins clustering markers.\n *\n * Example:\n * ```typescript\n * mc.addListener('clusteringbegin', (mc: MarkerClusterer) => {})\n * ```\n *\n * @param mc The MarkerClusterer whose markers are being clustered.\n * @event clusteringbegin\n */\nexport declare function clusteringbegin(mc: MarkerClusterer): void;\n\n/**\n * Optional parameter passed to the {@link MarkerClusterer} constructor.\n */\nexport interface MarkerClustererOptions {\n /**\n * The grid size of a cluster in pixels. The grid is a square.\n *\n * @default `60`\n */\n gridSize?: number;\n /**\n * The maximum zoom level at which clustering is enabled or\n * `null` if clustering is to be enabled at all zoom levels.\n *\n * @default `null`\n */\n maxZoom?: number;\n\n /**\n * Whether to zoom the map when a cluster marker is\n * clicked. You may want to set this to `false` if you have installed a handler\n * for the `click` event and it deals with zooming on its own.\n *\n * @default `true`\n */\n zoomOnClick?: boolean;\n /***\n * Whether the position of a cluster marker should be\n * the average position of all markers in the cluster. If set to `false`, the\n * cluster marker is positioned at the location of the first marker added to the cluster.\n *\n * @default `false`\n */\n averageCenter?: boolean;\n /**\n * The minimum number of markers needed in a cluster\n * before the markers are hidden and a cluster marker appears.\n *\n * @default `2`\n */\n minimumClusterSize?: number;\n\n /**\n * the z-index of a cluster.\n *\n * @default `google.maps.Marker.MAX_ZINDEX + 1`\n */\n zIndex?: number;\n\n /**\n * Whether to ignore hidden markers in clusters. You\n * may want to set this to `true` to ensure that hidden markers are not included\n * in the marker count that appears on a cluster marker (this count is the value of the\n * `text` property of the result returned by the default {@link calculator}).\n * If set to `true` and you change the visibility of a marker being clustered, be\n * sure to also call {@link MarkerClusterer#repaint()}.\n *\n * @default `false`\n */\n ignoreHidden?: boolean;\n /**\n * The tooltip to display when the mouse moves over a cluster\n * marker. (Alternatively, you can use a custom `calculator` function to specify a\n * different tooltip for each cluster marker.)\n *\n * @default `\"\"`\n */\n title?: string;\n /**\n * The function used to determine\n * the text to be displayed on a cluster marker and the index indicating which style to use\n * for the cluster marker. The input parameters for the function are (1) the array of markers\n * represented by a cluster marker and (2) the number of cluster icon styles. It returns a\n * {@link ClusterIconInfo} object. The default `calculator` returns a\n * `text` property which is the number of markers in the cluster and an\n * `index` property which is one higher than the lowest integer such that\n * `10^i` exceeds the number of markers in the cluster, or the size of the styles\n * array, whichever is less. The `styles` array element used has an index of\n * `index` minus 1. For example, the default `calculator` returns a\n * `text` value of `\"125\"` and an `index` of `3`\n * for a cluster icon representing 125 markers so the element used in the `styles`\n * array is `2`. A `calculator` may also return a `title`\n * property that contains the text of the tooltip to be used for the cluster marker. If\n * `title` is not defined, the tooltip is set to the value of the `title`\n * property for the MarkerClusterer.\n *\n * @default {@link MarkerClusterer.CALCULATOR}\n */\n calculator?: Calculator;\n /**\n * The name of the CSS class defining general styles\n * for the cluster markers. Use this class to define CSS styles that are not set up by the code\n * that processes the `styles` array.\n *\n * @default `\"cluster\"`\n */\n clusterClass?: string;\n /**\n * An array of {@link ClusterIconStyle} elements defining the styles\n * of the cluster markers to be used. The element to be used to style a given cluster marker\n * is determined by the function defined by the `calculator` property.\n * The default is an array of {@link ClusterIconStyle} elements whose properties are derived\n * from the values for `imagePath`, `imageExtension`, and `imageSizes`.\n *\n * @default `styles`\n */\n styles?: ClusterIconStyle[];\n /**\n * Whether to allow the use of cluster icons that\n * have sizes that are some multiple (typically double) of their actual display size. Icons such\n * as these look better when viewed on high-resolution monitors such as Apple's Retina displays.\n * Note: if this property is `true`, sprites cannot be used as cluster icons.\n *\n * @default `false`\n */\n enableRetinaIcons?: boolean;\n /**\n * Set this property to the number of markers to be processed in a single batch when using\n * a browser other than Internet Explorer (for Internet Explorer, use the batchSizeIE property instead).\n *\n * @default `MarkerClusterer.BATCH_SIZE`\n */\n batchSize?: number;\n /**\n * When Internet Explorer is\n * being used, markers are processed in several batches with a small delay inserted between\n * each batch in an attempt to avoid Javascript timeout errors. Set this property to the\n * number of markers to be processed in a single batch; select as high a number as you can\n * without causing a timeout error in the browser. This number might need to be as low as 100\n * if 15,000 markers are being managed, for example.\n *\n * @default `MarkerClusterer.BATCH_SIZE_IE`\n */\n batchSizeIE?: number;\n /**\n * The full URL of the root name of the group of image files to use for cluster icons.\n * The complete file name is of the form `imagePath`n.`imageExtension`\n * where n is the image file number (1, 2, etc.).\n *\n * @default `MarkerClusterer.IMAGE_PATH`\n */\n imagePath?: string;\n /**\n * The extension name for the cluster icon image files (e.g., `\"png\"` or\n * `\"jpg\"`).\n *\n * @default `MarkerClusterer.IMAGE_EXTENSION`\n */\n imageExtension?: string;\n /**\n * An array of numbers containing the widths of the group of\n * `imagePath`n.`imageExtension` image files.\n * (The images are assumed to be square.)\n *\n * @default `MarkerClusterer.IMAGE_SIZES`\n */\n imageSizes?: number[];\n /**\n * A function to take the text attribute associated with the cluster and output a string to attach an\n * ariaLabel to the cluster\n */\n ariaLabelFn?: AriaLabelFn;\n}\n\n/**\n * @ignore\n */\nconst getOption = (\n options: T,\n prop: K,\n def: T[K]\n): T[K] => {\n if (options[prop] !== undefined) {\n return options[prop];\n } else {\n return def;\n }\n};\n\nexport class MarkerClusterer extends OverlayViewSafe {\n /**\n * The number of markers to process in one batch.\n */\n static BATCH_SIZE = 2000;\n\n /**\n * The number of markers to process in one batch (IE only).\n */\n static BATCH_SIZE_IE = 500;\n\n /**\n * The default root name for the marker cluster images.\n */\n static IMAGE_PATH = \"../images/m\";\n\n /**\n * The default extension name for the marker cluster images.\n */\n static IMAGE_EXTENSION = \"png\";\n\n /**\n * The default array of sizes for the marker cluster images.\n */\n static IMAGE_SIZES: number[] = [53, 56, 66, 78, 90];\n\n private markers_: ClusterAugmentedMarker[] = [];\n private clusters_: Cluster[] = [];\n private listeners_: google.maps.MapsEventListener[] = [];\n\n private activeMap_: google.maps.Map = null;\n private ready_ = false;\n\n public ariaLabelFn = this.options.ariaLabelFn || ((): string => \"\");\n\n private zIndex_ = this.options.zIndex || google.maps.Marker.MAX_ZINDEX + 1;\n private gridSize_ = this.options.gridSize || 60;\n private minClusterSize_ = this.options.minimumClusterSize || 2;\n private maxZoom_ = this.options.maxZoom || null;\n private styles_: ClusterIconStyle[] = this.options.styles || [];\n private title_ = this.options.title || \"\";\n\n private zoomOnClick_ = getOption(this.options, \"zoomOnClick\", true);\n private averageCenter_ = getOption(this.options, \"averageCenter\", false);\n\n private ignoreHidden_ = getOption(this.options, \"ignoreHidden\", false);\n private enableRetinaIcons_ = getOption(\n this.options,\n \"enableRetinaIcons\",\n false\n );\n\n private imagePath_ = this.options.imagePath || MarkerClusterer.IMAGE_PATH;\n private imageExtension_ =\n this.options.imageExtension || MarkerClusterer.IMAGE_EXTENSION;\n private imageSizes_ = this.options.imageSizes || MarkerClusterer.IMAGE_SIZES;\n private calculator_ = this.options.calculator || MarkerClusterer.CALCULATOR;\n private batchSize_ = this.options.batchSize || MarkerClusterer.BATCH_SIZE;\n private batchSizeIE_ =\n this.options.batchSizeIE || MarkerClusterer.BATCH_SIZE_IE;\n private clusterClass_ = this.options.clusterClass || \"cluster\";\n\n private prevZoom_: number;\n private timerRefStatic: number;\n\n /**\n * Creates a MarkerClusterer object with the options specified in {@link MarkerClustererOptions}.\n * @param map The Google map to attach to.\n * @param markers The markers to be added to the cluster.\n * @param options The optional parameters.\n */\n constructor(\n map: google.maps.Map,\n markers: google.maps.Marker[] = [],\n private options: MarkerClustererOptions = {}\n ) {\n super();\n\n if (navigator.userAgent.toLowerCase().indexOf(\"msie\") !== -1) {\n // Try to avoid IE timeout when processing a huge number of markers:\n this.batchSize_ = this.batchSizeIE_;\n }\n\n this.setupStyles_();\n\n this.addMarkers(markers, true);\n this.setMap(map); // Note: this causes onAdd to be called\n }\n\n /**\n * Implementation of the onAdd interface method.\n * @ignore\n */\n onAdd(): void {\n this.activeMap_ = this.getMap() as google.maps.Map;\n this.ready_ = true;\n\n this.repaint();\n\n this.prevZoom_ = this.getMap().getZoom();\n\n // Add the map event listeners\n this.listeners_ = [\n google.maps.event.addListener(this.getMap(), \"zoom_changed\", () => {\n const map: google.maps.Map & {\n minZoom: number;\n maxZoom: number;\n mapTypes: { [type: string]: google.maps.MapType };\n } = this.getMap() as any; // eslint-disable-line @typescript-eslint/no-explicit-any\n\n // Fix for bug #407\n // Determines map type and prevents illegal zoom levels\n const minZoom = map.minZoom || 0;\n const maxZoom = Math.min(\n map.maxZoom || 100,\n map.mapTypes[map.getMapTypeId()].maxZoom\n );\n const zoom = Math.min(\n Math.max(this.getMap().getZoom(), minZoom),\n maxZoom\n );\n\n if (this.prevZoom_ != zoom) {\n this.prevZoom_ = zoom;\n this.resetViewport_(false);\n }\n }),\n google.maps.event.addListener(this.getMap(), \"idle\", () => {\n this.redraw_();\n })\n ];\n }\n\n /**\n * Implementation of the onRemove interface method.\n * Removes map event listeners and all cluster icons from the DOM.\n * All managed markers are also put back on the map.\n * @ignore\n */\n onRemove(): void {\n // Put all the managed markers back on the map:\n for (let i = 0; i < this.markers_.length; i++) {\n if (this.markers_[i].getMap() !== this.activeMap_) {\n this.markers_[i].setMap(this.activeMap_);\n }\n }\n\n // Remove all clusters:\n for (let i = 0; i < this.clusters_.length; i++) {\n this.clusters_[i].remove();\n }\n this.clusters_ = [];\n\n // Remove map event listeners:\n for (let i = 0; i < this.listeners_.length; i++) {\n google.maps.event.removeListener(this.listeners_[i]);\n }\n this.listeners_ = [];\n\n this.activeMap_ = null;\n this.ready_ = false;\n }\n\n /**\n * Implementation of the draw interface method.\n * @ignore\n */\n draw(): void {}\n\n /**\n * Sets up the styles object.\n */\n private setupStyles_(): void {\n if (this.styles_.length > 0) {\n return;\n }\n\n for (let i = 0; i < this.imageSizes_.length; i++) {\n const size = this.imageSizes_[i];\n this.styles_.push({\n url: this.imagePath_ + (i + 1) + \".\" + this.imageExtension_,\n height: size,\n width: size\n });\n }\n }\n\n /**\n * Fits the map to the bounds of the markers managed by the clusterer.\n */\n fitMapToMarkers(): void {\n const markers = this.getMarkers();\n const bounds = new google.maps.LatLngBounds();\n for (let i = 0; i < markers.length; i++) {\n // March 3, 2018: Bug fix -- honor the ignoreHidden property\n if (markers[i].getVisible() || !this.getIgnoreHidden()) {\n bounds.extend(markers[i].getPosition());\n }\n }\n\n (this.getMap() as google.maps.Map).fitBounds(bounds);\n }\n\n /**\n * Returns the value of the `gridSize` property.\n *\n * @return The grid size.\n */\n getGridSize(): number {\n return this.gridSize_;\n }\n\n /**\n * Sets the value of the `gridSize` property.\n *\n * @param gridSize The grid size.\n */\n setGridSize(gridSize: number): void {\n this.gridSize_ = gridSize;\n }\n\n /**\n * Returns the value of the `minimumClusterSize` property.\n *\n * @return The minimum cluster size.\n */\n getMinimumClusterSize(): number {\n return this.minClusterSize_;\n }\n\n /**\n * Sets the value of the `minimumClusterSize` property.\n *\n * @param minimumClusterSize The minimum cluster size.\n */\n setMinimumClusterSize(minimumClusterSize: number): void {\n this.minClusterSize_ = minimumClusterSize;\n }\n\n /**\n * Returns the value of the `maxZoom` property.\n *\n * @return The maximum zoom level.\n */\n getMaxZoom(): number {\n return this.maxZoom_;\n }\n\n /**\n * Sets the value of the `maxZoom` property.\n *\n * @param maxZoom The maximum zoom level.\n */\n setMaxZoom(maxZoom: number): void {\n this.maxZoom_ = maxZoom;\n }\n\n getZIndex(): number {\n return this.zIndex_;\n }\n\n setZIndex(zIndex: number): void {\n this.zIndex_ = zIndex;\n }\n\n /**\n * Returns the value of the `styles` property.\n *\n * @return The array of styles defining the cluster markers to be used.\n */\n getStyles(): ClusterIconStyle[] {\n return this.styles_;\n }\n\n /**\n * Sets the value of the `styles` property.\n *\n * @param styles The array of styles to use.\n */\n setStyles(styles: ClusterIconStyle[]): void {\n this.styles_ = styles;\n }\n\n /**\n * Returns the value of the `title` property.\n *\n * @return The content of the title text.\n */\n getTitle(): string {\n return this.title_;\n }\n\n /**\n * Sets the value of the `title` property.\n *\n * @param title The value of the title property.\n */\n setTitle(title: string): void {\n this.title_ = title;\n }\n\n /**\n * Returns the value of the `zoomOnClick` property.\n *\n * @return True if zoomOnClick property is set.\n */\n getZoomOnClick(): boolean {\n return this.zoomOnClick_;\n }\n\n /**\n * Sets the value of the `zoomOnClick` property.\n *\n * @param zoomOnClick The value of the zoomOnClick property.\n */\n setZoomOnClick(zoomOnClick: boolean): void {\n this.zoomOnClick_ = zoomOnClick;\n }\n\n /**\n * Returns the value of the `averageCenter` property.\n *\n * @return True if averageCenter property is set.\n */\n getAverageCenter(): boolean {\n return this.averageCenter_;\n }\n\n /**\n * Sets the value of the `averageCenter` property.\n *\n * @param averageCenter The value of the averageCenter property.\n */\n setAverageCenter(averageCenter: boolean): void {\n this.averageCenter_ = averageCenter;\n }\n\n /**\n * Returns the value of the `ignoreHidden` property.\n *\n * @return True if ignoreHidden property is set.\n */\n getIgnoreHidden(): boolean {\n return this.ignoreHidden_;\n }\n\n /**\n * Sets the value of the `ignoreHidden` property.\n *\n * @param ignoreHidden The value of the ignoreHidden property.\n */\n setIgnoreHidden(ignoreHidden: boolean): void {\n this.ignoreHidden_ = ignoreHidden;\n }\n\n /**\n * Returns the value of the `enableRetinaIcons` property.\n *\n * @return True if enableRetinaIcons property is set.\n */\n getEnableRetinaIcons(): boolean {\n return this.enableRetinaIcons_;\n }\n\n /**\n * Sets the value of the `enableRetinaIcons` property.\n *\n * @param enableRetinaIcons The value of the enableRetinaIcons property.\n */\n setEnableRetinaIcons(enableRetinaIcons: boolean): void {\n this.enableRetinaIcons_ = enableRetinaIcons;\n }\n\n /**\n * Returns the value of the `imageExtension` property.\n *\n * @return The value of the imageExtension property.\n */\n getImageExtension(): string {\n return this.imageExtension_;\n }\n\n /**\n * Sets the value of the `imageExtension` property.\n *\n * @param imageExtension The value of the imageExtension property.\n */\n setImageExtension(imageExtension: string): void {\n this.imageExtension_ = imageExtension;\n }\n\n /**\n * Returns the value of the `imagePath` property.\n *\n * @return The value of the imagePath property.\n */\n getImagePath(): string {\n return this.imagePath_;\n }\n\n /**\n * Sets the value of the `imagePath` property.\n *\n * @param imagePath The value of the imagePath property.\n */\n setImagePath(imagePath: string): void {\n this.imagePath_ = imagePath;\n }\n\n /**\n * Returns the value of the `imageSizes` property.\n *\n * @return The value of the imageSizes property.\n */\n getImageSizes(): number[] {\n return this.imageSizes_;\n }\n\n /**\n * Sets the value of the `imageSizes` property.\n *\n * @param imageSizes The value of the imageSizes property.\n */\n setImageSizes(imageSizes: number[]): void {\n this.imageSizes_ = imageSizes;\n }\n\n /**\n * Returns the value of the `calculator` property.\n *\n * @return the value of the calculator property.\n */\n getCalculator(): Calculator {\n return this.calculator_;\n }\n\n /**\n * Sets the value of the `calculator` property.\n *\n * @param calculator The value of the calculator property.\n */\n setCalculator(calculator: Calculator): void {\n this.calculator_ = calculator;\n }\n\n /**\n * Returns the value of the `batchSizeIE` property.\n *\n * @return the value of the batchSizeIE property.\n */\n getBatchSizeIE(): number {\n return this.batchSizeIE_;\n }\n\n /**\n * Sets the value of the `batchSizeIE` property.\n *\n * @param batchSizeIE The value of the batchSizeIE property.\n */\n setBatchSizeIE(batchSizeIE: number): void {\n this.batchSizeIE_ = batchSizeIE;\n }\n\n /**\n * Returns the value of the `clusterClass` property.\n *\n * @return the value of the clusterClass property.\n */\n getClusterClass(): string {\n return this.clusterClass_;\n }\n\n /**\n * Sets the value of the `clusterClass` property.\n *\n * @param clusterClass The value of the clusterClass property.\n */\n setClusterClass(clusterClass: string): void {\n this.clusterClass_ = clusterClass;\n }\n\n /**\n * Returns the array of markers managed by the clusterer.\n *\n * @return The array of markers managed by the clusterer.\n */\n getMarkers(): google.maps.Marker[] {\n return this.markers_;\n }\n\n /**\n * Returns the number of markers managed by the clusterer.\n *\n * @return The number of markers.\n */\n getTotalMarkers(): number {\n return this.markers_.length;\n }\n\n /**\n * Returns the current array of clusters formed by the clusterer.\n *\n * @return The array of clusters formed by the clusterer.\n */\n getClusters(): Cluster[] {\n return this.clusters_;\n }\n\n /**\n * Returns the number of clusters formed by the clusterer.\n *\n * @return The number of clusters formed by the clusterer.\n */\n getTotalClusters(): number {\n return this.clusters_.length;\n }\n\n /**\n * Adds a marker to the clusterer. The clusters are redrawn unless\n * `nodraw` is set to `true`.\n *\n * @param marker The marker to add.\n * @param nodraw Set to `true` to prevent redrawing.\n */\n addMarker(marker: google.maps.Marker, nodraw?: boolean): void {\n this.pushMarkerTo_(marker);\n if (!nodraw) {\n this.redraw_();\n }\n }\n\n /**\n * Adds an array of markers to the clusterer. The clusters are redrawn unless\n * `nodraw` is set to `true`.\n *\n * @param markers The markers to add.\n * @param nodraw Set to `true` to prevent redrawing.\n */\n addMarkers(markers: google.maps.Marker[], nodraw?: boolean): void {\n for (const key in markers) {\n if (Object.prototype.hasOwnProperty.call(markers, key)) {\n this.pushMarkerTo_(markers[key]);\n }\n }\n if (!nodraw) {\n this.redraw_();\n }\n }\n\n /**\n * Pushes a marker to the clusterer.\n *\n * @param marker The marker to add.\n */\n private pushMarkerTo_(\n marker: google.maps.Marker & { isAdded?: boolean }\n ): void {\n // If the marker is draggable add a listener so we can update the clusters on the dragend:\n if (marker.getDraggable()) {\n google.maps.event.addListener(marker, \"dragend\", () => {\n if (this.ready_) {\n marker.isAdded = false;\n this.repaint();\n }\n });\n }\n marker.isAdded = false;\n this.markers_.push(marker);\n }\n\n /**\n * Removes a marker from the cluster. The clusters are redrawn unless\n * `nodraw` is set to `true`. Returns `true` if the\n * marker was removed from the clusterer.\n *\n * @param marker The marker to remove.\n * @param nodraw Set to `true` to prevent redrawing.\n * @return True if the marker was removed from the clusterer.\n */\n removeMarker(marker: google.maps.Marker, nodraw?: boolean): boolean {\n const removed = this.removeMarker_(marker);\n\n if (!nodraw && removed) {\n this.repaint();\n }\n\n return removed;\n }\n\n /**\n * Removes an array of markers from the cluster. The clusters are redrawn unless\n * `nodraw` is set to `true`. Returns `true` if markers were removed from the clusterer.\n *\n * @param markers The markers to remove.\n * @param nodraw Set to `true` to prevent redrawing.\n * @return True if markers were removed from the clusterer.\n */\n removeMarkers(markers: google.maps.Marker[], nodraw?: boolean): boolean {\n let removed = false;\n\n for (let i = 0; i < markers.length; i++) {\n const r = this.removeMarker_(markers[i]);\n removed = removed || r;\n }\n\n if (!nodraw && removed) {\n this.repaint();\n }\n\n return removed;\n }\n\n /**\n * Removes a marker and returns true if removed, false if not.\n *\n * @param marker The marker to remove\n * @return Whether the marker was removed or not\n */\n private removeMarker_(marker: google.maps.Marker): boolean {\n let index = -1;\n if (this.markers_.indexOf) {\n index = this.markers_.indexOf(marker);\n } else {\n for (let i = 0; i < this.markers_.length; i++) {\n if (marker === this.markers_[i]) {\n index = i;\n break;\n }\n }\n }\n\n if (index === -1) {\n // Marker is not in our list of markers, so do nothing:\n return false;\n }\n\n marker.setMap(null);\n this.markers_.splice(index, 1); // Remove the marker from the list of managed markers\n return true;\n }\n\n /**\n * Removes all clusters and markers from the map and also removes all markers\n * managed by the clusterer.\n */\n clearMarkers(): void {\n this.resetViewport_(true);\n this.markers_ = [];\n }\n\n /**\n * Recalculates and redraws all the marker clusters from scratch.\n * Call this after changing any properties.\n */\n repaint(): void {\n const oldClusters = this.clusters_.slice();\n this.clusters_ = [];\n this.resetViewport_(false);\n this.redraw_();\n\n // Remove the old clusters.\n // Do it in a timeout to prevent blinking effect.\n setTimeout(function() {\n for (let i = 0; i < oldClusters.length; i++) {\n oldClusters[i].remove();\n }\n }, 0);\n }\n\n /**\n * Returns the current bounds extended by the grid size.\n *\n * @param bounds The bounds to extend.\n * @return The extended bounds.\n * @ignore\n */\n getExtendedBounds(\n bounds: google.maps.LatLngBounds\n ): google.maps.LatLngBounds {\n const projection = this.getProjection();\n\n // Turn the bounds into latlng.\n const tr = new google.maps.LatLng(\n bounds.getNorthEast().lat(),\n bounds.getNorthEast().lng()\n );\n const bl = new google.maps.LatLng(\n bounds.getSouthWest().lat(),\n bounds.getSouthWest().lng()\n );\n\n // Convert the points to pixels and the extend out by the grid size.\n const trPix = projection.fromLatLngToDivPixel(tr);\n trPix.x += this.gridSize_;\n trPix.y -= this.gridSize_;\n\n const blPix = projection.fromLatLngToDivPixel(bl);\n blPix.x -= this.gridSize_;\n blPix.y += this.gridSize_;\n\n // Convert the pixel points back to LatLng\n const ne = projection.fromDivPixelToLatLng(trPix);\n const sw = projection.fromDivPixelToLatLng(blPix);\n\n // Extend the bounds to contain the new bounds.\n bounds.extend(ne);\n bounds.extend(sw);\n\n return bounds;\n }\n\n /**\n * Redraws all the clusters.\n */\n private redraw_(): void {\n this.createClusters_(0);\n }\n\n /**\n * Removes all clusters from the map. The markers are also removed from the map\n * if `hide` is set to `true`.\n *\n * @param hide Set to `true` to also remove the markers from the map.\n */\n private resetViewport_(hide?: boolean): void {\n // Remove all the clusters\n for (let i = 0; i < this.clusters_.length; i++) {\n this.clusters_[i].remove();\n }\n this.clusters_ = [];\n\n // Reset the markers to not be added and to be removed from the map.\n for (let i = 0; i < this.markers_.length; i++) {\n const marker = this.markers_[i];\n marker.isAdded = false;\n if (hide) {\n marker.setMap(null);\n }\n }\n }\n\n /**\n * Calculates the distance between two latlng locations in km.\n *\n * @param p1 The first lat lng point.\n * @param p2 The second lat lng point.\n * @return The distance between the two points in km.\n * @link http://www.movable-type.co.uk/scripts/latlong.html\n */\n private distanceBetweenPoints_(\n p1: google.maps.LatLng,\n p2: google.maps.LatLng\n ): number {\n const R = 6371; // Radius of the Earth in km\n const dLat = ((p2.lat() - p1.lat()) * Math.PI) / 180;\n const dLon = ((p2.lng() - p1.lng()) * Math.PI) / 180;\n const a =\n Math.sin(dLat / 2) * Math.sin(dLat / 2) +\n Math.cos((p1.lat() * Math.PI) / 180) *\n Math.cos((p2.lat() * Math.PI) / 180) *\n Math.sin(dLon / 2) *\n Math.sin(dLon / 2);\n const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n return R * c;\n }\n\n /**\n * Determines if a marker is contained in a bounds.\n *\n * @param marker The marker to check.\n * @param bounds The bounds to check against.\n * @return True if the marker is in the bounds.\n */\n private isMarkerInBounds_(\n marker: google.maps.Marker,\n bounds: google.maps.LatLngBounds\n ): boolean {\n return bounds.contains(marker.getPosition());\n }\n\n /**\n * Adds a marker to a cluster, or creates a new cluster.\n *\n * @param marker The marker to add.\n */\n private addToClosestCluster_(marker: google.maps.Marker): void {\n let distance = 40000; // Some large number\n let clusterToAddTo = null;\n for (let i = 0; i < this.clusters_.length; i++) {\n const cluster = this.clusters_[i];\n const center = cluster.getCenter();\n if (center) {\n const d = this.distanceBetweenPoints_(center, marker.getPosition());\n if (d < distance) {\n distance = d;\n clusterToAddTo = cluster;\n }\n }\n }\n\n if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) {\n clusterToAddTo.addMarker(marker);\n } else {\n const cluster = new Cluster(this);\n cluster.addMarker(marker);\n this.clusters_.push(cluster);\n }\n }\n\n /**\n * Creates the clusters. This is done in batches to avoid timeout errors\n * in some browsers when there is a huge number of markers.\n *\n * @param iFirst The index of the first marker in the batch of\n * markers to be added to clusters.\n */\n private createClusters_(iFirst: number): void {\n if (!this.ready_) {\n return;\n }\n\n // Cancel previous batch processing if we're working on the first batch:\n if (iFirst === 0) {\n google.maps.event.trigger(this, \"clusteringbegin\", this);\n\n if (typeof this.timerRefStatic !== \"undefined\") {\n clearTimeout(this.timerRefStatic);\n delete this.timerRefStatic;\n }\n }\n\n // Get our current map view bounds.\n // Create a new bounds object so we don't affect the map.\n //\n // See Comments 9 & 11 on Issue 3651 relating to this workaround for a Google Maps bug:\n let mapBounds: google.maps.LatLngBounds;\n\n if (this.getMap().getZoom() > 3) {\n mapBounds = new google.maps.LatLngBounds(\n (this.getMap() as google.maps.Map).getBounds().getSouthWest(),\n (this.getMap() as google.maps.Map).getBounds().getNorthEast()\n );\n } else {\n mapBounds = new google.maps.LatLngBounds(\n new google.maps.LatLng(85.02070771743472, -178.48388434375),\n new google.maps.LatLng(-85.08136444384544, 178.00048865625)\n );\n }\n const bounds = this.getExtendedBounds(mapBounds);\n\n const iLast = Math.min(iFirst + this.batchSize_, this.markers_.length);\n\n for (let i = iFirst; i < iLast; i++) {\n const marker = this.markers_[i];\n if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) {\n if (\n !this.ignoreHidden_ ||\n (this.ignoreHidden_ && marker.getVisible())\n ) {\n this.addToClosestCluster_(marker);\n }\n }\n }\n\n if (iLast < this.markers_.length) {\n this.timerRefStatic = window.setTimeout(() => {\n this.createClusters_(iLast);\n }, 0);\n } else {\n delete this.timerRefStatic;\n google.maps.event.trigger(this, \"clusteringend\", this);\n }\n }\n\n /**\n * The default function for determining the label text and style\n * for a cluster icon.\n *\n * @param markers The array of markers represented by the cluster.\n * @param numStyles The number of marker styles available.\n * @return The information resource for the cluster.\n */\n static CALCULATOR(\n markers: google.maps.Marker[],\n numStyles: number\n ): ClusterIconInfo {\n let index = 0;\n const count: number = markers.length;\n\n let dv = count;\n while (dv !== 0) {\n dv = Math.floor(dv / 10);\n index++;\n }\n\n index = Math.min(index, numStyles);\n return {\n text: count.toString(),\n index: index,\n title: \"\"\n };\n }\n}\n"],"names":["OverlayViewSafe","type1","type2","property","prototype","extend","google","maps","OverlayView","ClusterIcon","cluster_","styles_","_this","getMarkerClusterer","getClusterClass","setMap","getMap","cMouseDownInCluster","cDraggingMapByCluster","mc","this","version","split","major","minor","gmVersion","parseInt","div_","document","createElement","className","className_","visible_","show","getPanes","overlayMouseTarget","appendChild","boundsChangedListener_","event","addListener","addDomListener","e","stopPropagation","trigger","_this2","getZoomOnClick","mz","getMaxZoom","theBounds","getBounds","fitBounds","setTimeout","getZoom","setZoom","cancelBubble","parentNode","hide","removeListener","clearInstanceListeners","removeChild","pos","getPosFromLatLng_","center_","style","top","y","left","x","display","ariaLabel","ariaLabelFn","sums_","text","bp","backgroundPosition_","spriteH","replace","spriteV","cssText","createCss_","imgDimensions","getEnableRetinaIcons","width_","height_","Y1","X1","Y2","X2","imgStyle","join","divStyle","anchorText_","textColor_","textSize_","fontFamily_","fontWeight_","fontStyle_","textDecoration_","innerHTML","url_","title","getTitle","sums","index","Math","max","min","length","url","height","width","anchorText","anchorIcon_","anchorIcon","floor","textColor","textSize","textDecoration","fontWeight","fontStyle","fontFamily","backgroundPosition","center","getZIndex","latlng","getProjection","fromLatLngToDivPixel","Cluster","markerClusterer_","getMinimumClusterSize","getAverageCenter","getStyles","markers_","map_","bounds","LatLngBounds","markers","getMarkers","i","getPosition","clusterIcon_","marker","isMarkerAlreadyAdded_","averageCenter_","l","lat","lng","LatLng","calculateBounds_","isAdded","push","mCount","minClusterSize_","updateIcon_","bounds_","contains","getExtendedBounds","numStyles","getCalculator","setCenter","useStyle","indexOf","getOption","options","prop","def","undefined","MarkerClusterer","map","zIndex","Marker","MAX_ZINDEX","gridSize","minimumClusterSize","maxZoom","styles","imagePath","IMAGE_PATH","imageExtension","IMAGE_EXTENSION","imageSizes","IMAGE_SIZES","calculator","CALCULATOR","batchSize","BATCH_SIZE","batchSizeIE","BATCH_SIZE_IE","clusterClass","navigator","userAgent","toLowerCase","batchSize_","batchSizeIE_","setupStyles_","addMarkers","activeMap_","ready_","repaint","prevZoom_","listeners_","minZoom","mapTypes","getMapTypeId","zoom","resetViewport_","redraw_","clusters_","remove","imageSizes_","size","imagePath_","imageExtension_","getVisible","getIgnoreHidden","gridSize_","maxZoom_","zIndex_","title_","zoomOnClick_","zoomOnClick","averageCenter","ignoreHidden_","ignoreHidden","enableRetinaIcons_","enableRetinaIcons","calculator_","clusterClass_","nodraw","pushMarkerTo_","key","Object","hasOwnProperty","call","getDraggable","_this3","removed","removeMarker_","r","splice","oldClusters","slice","projection","tr","getNorthEast","bl","getSouthWest","trPix","blPix","ne","fromDivPixelToLatLng","sw","createClusters_","p1","p2","dLat","PI","dLon","a","sin","cos","atan2","sqrt","distance","clusterToAddTo","cluster","getCenter","d","distanceBetweenPoints_","isMarkerInClusterBounds","addMarker","iFirst","mapBounds","timerRefStatic","clearTimeout","iLast","isMarkerInBounds_","addToClosestCluster_","window","_this4","count","dv","toString"],"mappings":"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"}