ФRuŠKać

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

//www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

MarkerClusterer

constructor
MarkerClusterer()

Option name Type Description
map google.maps.Map

The Google map to attach to.

opt_markers Array.<google.maps.Marker>

Optional markers to add to the cluster.

opt_options Object

support the following options: 'gridSize': (number) The grid size of a cluster in pixels.
'maxZoom': (number) The maximum zoom level that a marker can be part of a
cluster.
'zoomOnClick': (boolean) Whether the default behaviour of clicking on a
cluster is to zoom into it.
'averageCenter': (boolean) Wether the center of each cluster should be
the average of all markers in the cluster.
'minimumClusterSize': (number) The minimum number of markers to be in a
cluster before the markers are hidden and a count
is shown.
'styles': (object) An object that has style properties:
'url': (string) The image url.
'height': (number) The image height.
'width': (number) The image width.
'anchor': (Array) The anchor position of the label text.
'textColor': (string) The text color.
'textSize': (number) The text size.
'backgroundPosition': (string) The position of the backgound x, y.

A Marker Clusterer that clusters markers.

function MarkerClusterer(map, opt_markers, opt_options) {
    // MarkerClusterer implements google.maps.OverlayView interface. We use the
    // extend function to extend MarkerClusterer with google.maps.OverlayView
    // because it might not always be available when the code is defined so we
    // look for it at the last possible moment. If it doesn't exist now then
    // there is no point going ahead :)
    this.extend(MarkerClusterer, google.maps.OverlayView);
    this.map_ = map;

clusters_

property
this.clusters_

this.clusters_ = [];

this.sizes = [53, 56, 66, 78, 90];

fitMapToMarkers

method
MarkerClusterer.prototype.fitMapToMarkers()

Fit the map to the bounds of the markers in the clusterer.

MarkerClusterer.prototype.fitMapToMarkers = function () {
    var markers = this.getMarkers();
    var bounds = new google.maps.LatLngBounds();
    for (var i = 0, marker; marker = markers[i]; i++) {
        bounds.extend(marker.getPosition());
    }

    this.map_.fitBounds(bounds);
};

setStyles

method
MarkerClusterer.prototype.setStyles()

Option name Type Description
styles Object

The style to set.

Sets the styles.

MarkerClusterer.prototype.setStyles = function (styles) {
    this.styles_ = styles;
};

getStyles

method
MarkerClusterer.prototype.getStyles()

Gets the styles.

MarkerClusterer.prototype.getStyles = function () {
    return this.styles_;
};

isZoomOnClick

method
MarkerClusterer.prototype.isZoomOnClick()

Whether zoom on click is set.

MarkerClusterer.prototype.isZoomOnClick = function () {
    return this.zoomOnClick_;
};

isAverageCenter

method
MarkerClusterer.prototype.isAverageCenter()

Whether average center is set.

MarkerClusterer.prototype.isAverageCenter = function () {
    return this.averageCenter_;
};

getMarkers

method
MarkerClusterer.prototype.getMarkers()

Returns the array of markers in the clusterer.

MarkerClusterer.prototype.getMarkers = function () {
    return this.markers_;
};

getTotalMarkers

method
MarkerClusterer.prototype.getTotalMarkers()

Returns the number of markers in the clusterer

MarkerClusterer.prototype.getTotalMarkers = function () {
    return this.markers_.length;
};

setMaxZoom

method
MarkerClusterer.prototype.setMaxZoom()

Option name Type Description
maxZoom number

The max zoom level.

Sets the max zoom for the clusterer.

MarkerClusterer.prototype.setMaxZoom = function (maxZoom) {
    this.maxZoom_ = maxZoom;
};

getMaxZoom

method
MarkerClusterer.prototype.getMaxZoom()

Gets the max zoom for the clusterer.

MarkerClusterer.prototype.getMaxZoom = function () {
    return this.maxZoom_;
};

setCalculator

method
MarkerClusterer.prototype.setCalculator()

Option name Type Description
calculator function(Array, number)

The function to set as the calculator. The function should return a object properties:
'text' (string) and 'index' (number).

Set the calculator function.

MarkerClusterer.prototype.setCalculator = function (calculator) {
    this.calculator_ = calculator;
};

getCalculator

method
MarkerClusterer.prototype.getCalculator()

Get the calculator function.

MarkerClusterer.prototype.getCalculator = function () {
    return this.calculator_;
};

addMarkers

method
MarkerClusterer.prototype.addMarkers()

Option name Type Description
markers Array.<google.maps.Marker>

The markers to add.

opt_nodraw boolean

Whether to redraw the clusters.

Add an array of markers to the clusterer.

MarkerClusterer.prototype.addMarkers = function (markers, opt_nodraw) {
    for (var i = 0, marker; marker = markers[i]; i++) {
        this.pushMarkerTo_(marker);
    }
    if (!opt_nodraw) {
        this.redraw();
    }
};

addMarker

method
MarkerClusterer.prototype.addMarker()

Option name Type Description
marker google.maps.Marker

The marker to add.

opt_nodraw boolean

Whether to redraw the clusters.

Adds a marker to the clusterer and redraws if needed.

MarkerClusterer.prototype.addMarker = function (marker, opt_nodraw) {
    this.pushMarkerTo_(marker);
    if (!opt_nodraw) {
        this.redraw();
    }
};

removeMarker

method
MarkerClusterer.prototype.removeMarker()

Option name Type Description
marker google.maps.Marker

The marker to remove.

opt_nodraw boolean

Optional boolean to force no redraw.

return boolean

True if the marker was removed.

Remove a marker from the cluster.

MarkerClusterer.prototype.removeMarker = function (marker, opt_nodraw) {
    var removed = this.removeMarker_(marker);

    if (!opt_nodraw && removed) {
        this.resetViewport();
        this.redraw();
        return true;
    } else {
        return false;
    }
};

removeMarkers

method
MarkerClusterer.prototype.removeMarkers()

Option name Type Description
markers Array.<google.maps.Marker>

The markers to remove.

opt_nodraw boolean

Optional boolean to force no redraw.

Removes an array of markers from the cluster.

MarkerClusterer.prototype.removeMarkers = function (markers, opt_nodraw) {
    var removed = false;

    for (var i = 0, marker; marker = markers[i]; i++) {
        var r = this.removeMarker_(marker);
        removed = removed || r;
    }

    if (!opt_nodraw && removed) {
        this.resetViewport();
        this.redraw();
        return true;
    }
};

getTotalClusters

method
MarkerClusterer.prototype.getTotalClusters()

Returns the number of clusters in the clusterer.

MarkerClusterer.prototype.getTotalClusters = function () {
    return this.clusters_.length;
};

getMap

method
MarkerClusterer.prototype.getMap()

Returns the google map that the clusterer is associated with.

MarkerClusterer.prototype.getMap = function () {
    return this.map_;
};

setMap

method
MarkerClusterer.prototype.setMap()

Option name Type Description
map google.maps.Map

The map.

Sets the google map that the clusterer is associated with.

MarkerClusterer.prototype.setMap = function (map) {
    this.map_ = map;
};

getGridSize

method
MarkerClusterer.prototype.getGridSize()

Returns the size of the grid.

MarkerClusterer.prototype.getGridSize = function () {
    return this.gridSize_;
};

setGridSize

method
MarkerClusterer.prototype.setGridSize()

Option name Type Description
size number

The grid size.

Sets the size of the grid.

MarkerClusterer.prototype.setGridSize = function (size) {
    this.gridSize_ = size;
};

getMinClusterSize

method
MarkerClusterer.prototype.getMinClusterSize()

Returns the min cluster size.

MarkerClusterer.prototype.getMinClusterSize = function () {
    return this.minClusterSize_;
};

setMinClusterSize

method
MarkerClusterer.prototype.setMinClusterSize()

Option name Type Description
size number

The grid size.

Sets the min cluster size.

MarkerClusterer.prototype.setMinClusterSize = function (size) {
    this.minClusterSize_ = size;
};

getExtendedBounds

method
MarkerClusterer.prototype.getExtendedBounds()

Option name Type Description
bounds google.maps.LatLngBounds

The bounds to extend.

return google.maps.LatLngBounds

The extended bounds.

Extends a bounds object by the grid size.

MarkerClusterer.prototype.getExtendedBounds = function (bounds) {
    var projection = this.getProjection();

    // Turn the bounds into latlng.
    var tr = new google.maps.LatLng(bounds.getNorthEast().lat(),
        bounds.getNorthEast().lng());
    var bl = new google.maps.LatLng(bounds.getSouthWest().lat(),
        bounds.getSouthWest().lng());

    // Convert the points to pixels and the extend out by the grid size.
    var trPix = projection.fromLatLngToDivPixel(tr);
    trPix.x += this.gridSize_;
    trPix.y -= this.gridSize_;

    var blPix = projection.fromLatLngToDivPixel(bl);
    blPix.x -= this.gridSize_;
    blPix.y += this.gridSize_;

    // Convert the pixel points back to LatLng
    var ne = projection.fromDivPixelToLatLng(trPix);
    var sw = projection.fromDivPixelToLatLng(blPix);

    // Extend the bounds to contain the new bounds.
    bounds.extend(ne);
    bounds.extend(sw);

    return bounds;
};

clearMarkers

method
MarkerClusterer.prototype.clearMarkers()

Clears all clusters and markers from the clusterer.

MarkerClusterer.prototype.clearMarkers = function () {
    this.resetViewport(true);

    // Set the markers a empty array.
    this.markers_ = [];
};

resetViewport

method
MarkerClusterer.prototype.resetViewport()

Option name Type Description
opt_hide boolean

To also hide the marker.

Clears all existing clusters and recreates them.

MarkerClusterer.prototype.resetViewport = function (opt_hide) {
    // Remove all the clusters
    for (var i = 0, cluster; cluster = this.clusters_[i]; i++) {
        cluster.remove();
    }

    // Reset the markers to not be added and to be invisible.
    for (var i = 0, marker; marker = this.markers_[i]; i++) {
        marker.isAdded = false;
        if (opt_hide) {
            marker.setClustered(true);
        }
    }

    this.clusters_ = [];
};

repaint

method
MarkerClusterer.prototype.repaint()

MarkerClusterer.prototype.repaint = function () {
    var oldClusters = this.clusters_.slice();
    this.clusters_.length = 0;
    this.resetViewport();
    this.redraw();

    // Remove the old clusters.
    // Do it in a timeout so the other clusters have been drawn first.
    window.setTimeout(function () {
        for (var i = 0, cluster; cluster = oldClusters[i]; i++) {
            cluster.remove();
        }
    }, 0);
};

redraw

method
MarkerClusterer.prototype.redraw()

Redraws the clusters.

MarkerClusterer.prototype.redraw = function () {
    this.createClusters_();
};

isMarkerAlreadyAdded

method
Cluster.prototype.isMarkerAlreadyAdded()

Option name Type Description
marker google.maps.Marker

The marker to check.

return boolean

True if the marker is already added.

Determins if a marker is already added to the cluster.

Cluster.prototype.isMarkerAlreadyAdded = function (marker) {
    if (this.markers_.indexOf) {
        return this.markers_.indexOf(marker) != -1;
    } else {
        for (var i = 0, m; m = this.markers_[i]; i++) {
            if (m == marker) {
                return true;
            }
        }
    }
    return false;
};

addMarker

method
Cluster.prototype.addMarker()

Option name Type Description
marker google.maps.Marker

The marker to add.

return boolean

True if the marker was added.

Add a marker the cluster.

Cluster.prototype.addMarker = function (marker) {
    if (this.isMarkerAlreadyAdded(marker)) {
        return false;
    }

    if (!this.center_) {
        this.center_ = marker.getPosition();
        this.calculateBounds_();
    } else {
        if (this.averageCenter_) {
            var l = this.markers_.length + 1;
            var lat = (this.center_.lat() * (l - 1) + marker.getPosition().lat()) / l;
            var lng = (this.center_.lng() * (l - 1) + marker.getPosition().lng()) / l;
            this.center_ = new google.maps.LatLng(lat, lng);
            this.calculateBounds_();
        }
    }

    marker.isAdded = true;
    this.markers_.push(marker);

    var len = this.markers_.length;
    if (len < this.minClusterSize_) {
        // Min cluster size not reached so show the marker.
        marker.setClustered(false);
    }

    if (len == this.minClusterSize_) {
        // Hide the markers that were showing.
        for (var i = 0; i < len; i++) {
            this.markers_[i].setClustered(true);
        }
    }

    if (len >= this.minClusterSize_) {
        marker.setClustered(true);
    }

    this.updateIcon();
    return true;
};

getMarkerClusterer

method
Cluster.prototype.getMarkerClusterer()

Returns the marker clusterer that the cluster is associated with.

Cluster.prototype.getMarkerClusterer = function () {
    return this.markerClusterer_;
};

getBounds

method
Cluster.prototype.getBounds()

Returns the bounds of the cluster.

Cluster.prototype.getBounds = function () {
    var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
    var markers = this.getMarkers();
    for (var i = 0, marker; marker = markers[i]; i++) {
        bounds.extend(marker.getPosition());
    }
    return bounds;
};

remove

method
Cluster.prototype.remove()

Removes the cluster

Cluster.prototype.remove = function () {
    this.clusterIcon_.remove();
    this.markers_.length = 0;
    delete this.markers_;
};

getSize

method
Cluster.prototype.getSize()

Returns the center of the cluster.

Cluster.prototype.getSize = function () {
    return this.markers_.length;
};

getMarkers

method
Cluster.prototype.getMarkers()

Returns the center of the cluster.

Cluster.prototype.getMarkers = function () {
    return this.markers_;
};

getCenter

method
Cluster.prototype.getCenter()

Returns the center of the cluster.

Cluster.prototype.getCenter = function () {
    return this.center_;
};

isMarkerInClusterBounds

method
Cluster.prototype.isMarkerInClusterBounds()

Option name Type Description
marker google.maps.Marker

The marker to check.

return boolean

True if the marker lies in the bounds.

Determines if a marker lies in the clusters bounds.

Cluster.prototype.isMarkerInClusterBounds = function (marker) {
    return this.bounds_.contains(marker.getPosition());
};

getMap

method
Cluster.prototype.getMap()

Returns the map that the cluster is associated with.

Cluster.prototype.getMap = function () {
    return this.map_;
};

updateIcon

method
Cluster.prototype.updateIcon()

Updates the cluster icon

Cluster.prototype.updateIcon = function () {
    var zoom = this.map_.getZoom();
    var mz = this.markerClusterer_.getMaxZoom();

    if (mz && zoom > mz) {
        // The zoom is greater than our max zoom so show all the markers in cluster.
        for (var i = 0, marker; marker = this.markers_[i]; i++) {
            marker.setClustered(false);
        }
        return;
    }

    if (this.markers_.length < this.minClusterSize_) {
        // Min cluster size not yet reached.
        this.clusterIcon_.hide();
        return;
    }

    var numStyles = this.markerClusterer_.getStyles().length;
    var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles);
    this.clusterIcon_.setCenter(this.center_);
    this.clusterIcon_.setSums(sums);
    this.clusterIcon_.show();
};

triggerClusterClick

method
ClusterIcon.prototype.triggerClusterClick()

Triggers the clusterclick event and zoom's if the option is set.

ClusterIcon.prototype.triggerClusterClick = function () {
    var markerClusterer = this.cluster_.getMarkerClusterer();

    // Trigger the clusterclick event.
    google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster_);

    if (markerClusterer.isZoomOnClick()) {
        // Zoom into the cluster.
        this.map_.fitBounds(this.cluster_.getBounds());
    }
};

hide

method
ClusterIcon.prototype.hide()

Hide the icon.

ClusterIcon.prototype.hide = function () {
    if (this.div_) {
        this.div_.style.display = 'none';
    }
    this.visible_ = false;
};

show

method
ClusterIcon.prototype.show()

Position and show the icon.

ClusterIcon.prototype.show = function () {
    if (this.div_) {
        var pos = this.getPosFromLatLng_(this.center_);
        this.div_.style.cssText = this.createCss(pos);
        this.div_.style.display = '';
    }
    this.visible_ = true;
};

remove

method
ClusterIcon.prototype.remove()

Remove the icon from the map

ClusterIcon.prototype.remove = function () {
    this.setMap(null);
};

setSums

method
ClusterIcon.prototype.setSums()

Option name Type Description
sums Object

The sums containing: 'text': (string) The text to display in the icon.
'index': (number) The style index of the icon.

Set the sums of the icon.

ClusterIcon.prototype.setSums = function (sums) {
    this.sums_ = sums;
    this.text_ = sums.text;
    this.index_ = sums.index;
    if (this.div_) {
        this.div_.innerHTML = sums.text;
    }

    this.useStyle();
};

useStyle

method
ClusterIcon.prototype.useStyle()

Sets the icon to the the styles.

ClusterIcon.prototype.useStyle = function () {
    var index = Math.max(0, this.sums_.index - 1);
    index = Math.min(this.styles_.length - 1, index);
    var style = this.styles_[index];
    this.url_ = style['url'];
    this.height_ = style['height'];
    this.width_ = style['width'];
    this.textColor_ = style['textColor'];
    this.anchor_ = style['anchor'];
    this.textSize_ = style['textSize'];
    this.backgroundPosition_ = style['backgroundPosition'];
};

setCenter

method
ClusterIcon.prototype.setCenter()

Option name Type Description
center google.maps.LatLng

The latlng to set as the center.

Sets the center of the icon.

ClusterIcon.prototype.setCenter = function (center) {
    this.center_ = center;
};

createCss

method
ClusterIcon.prototype.createCss()

Option name Type Description
pos google.maps.Point

The position.

return string

The css style text.

Create the css text based on the position of the icon.

ClusterIcon.prototype.createCss = function (pos) {
    var style = [];
    //style.push('background-image:url(' + this.url_ + ');');
    //var backgroundPosition = this.backgroundPosition_ ? this.backgroundPosition_ : '0 0';
    //style.push('background-position:' + backgroundPosition + ';');

    if (typeof this.anchor_ === 'object') {
        if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 &&
            this.anchor_[0] < this.height_) {
            style.push('height:' + (this.height_ - this.anchor_[0]) +
                'px; padding-top:' + this.anchor_[0] + 'px;');
        } else {
            style.push('height:' + this.height_ + 'px; line-height:' + this.height_ +
                'px;');
        }
        if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 &&
            this.anchor_[1] < this.width_) {
            style.push('width:' + (this.width_ - this.anchor_[1]) +
                'px; padding-left:' + this.anchor_[1] + 'px;');
        } else {
            style.push('width:' + this.width_ + 'px; text-align:center;');
        }
    } else {
        style.push('height:' + this.height_ + 'px; line-height:' +
            this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;');
    }

    var txtColor = this.textColor_ ? this.textColor_ : 'black';
    var txtSize = this.textSize_ ? this.textSize_ : 11;

    style.push('cursor:pointer; top:' + pos.y + 'px; left:' +
        pos.x + 'px; color:' + txtColor + '; position:absolute; font-size:' +
        txtSize + 'px; font-family:Arial,sans-serif; font-weight:bold');
    return style.join('');
};


// Export Symbols for Closure
// If you are not going to compile with closure then you can remove the
// code below.
window['MarkerClusterer'] = MarkerClusterer;
MarkerClusterer.prototype['addMarker'] = MarkerClusterer.prototype.addMarker;
MarkerClusterer.prototype['addMarkers'] = MarkerClusterer.prototype.addMarkers;
MarkerClusterer.prototype['clearMarkers'] =
    MarkerClusterer.prototype.clearMarkers;
MarkerClusterer.prototype['fitMapToMarkers'] =
    MarkerClusterer.prototype.fitMapToMarkers;
MarkerClusterer.prototype['getCalculator'] =
    MarkerClusterer.prototype.getCalculator;
MarkerClusterer.prototype['getGridSize'] =
    MarkerClusterer.prototype.getGridSize;
MarkerClusterer.prototype['getExtendedBounds'] =
    MarkerClusterer.prototype.getExtendedBounds;
MarkerClusterer.prototype['getMap'] = MarkerClusterer.prototype.getMap;
MarkerClusterer.prototype['getMarkers'] = MarkerClusterer.prototype.getMarkers;
MarkerClusterer.prototype['getMaxZoom'] = MarkerClusterer.prototype.getMaxZoom;
MarkerClusterer.prototype['getStyles'] = MarkerClusterer.prototype.getStyles;
MarkerClusterer.prototype['getTotalClusters'] =
    MarkerClusterer.prototype.getTotalClusters;
MarkerClusterer.prototype['getTotalMarkers'] =
    MarkerClusterer.prototype.getTotalMarkers;
MarkerClusterer.prototype['redraw'] = MarkerClusterer.prototype.redraw;
MarkerClusterer.prototype['removeMarker'] =
    MarkerClusterer.prototype.removeMarker;
MarkerClusterer.prototype['removeMarkers'] =
    MarkerClusterer.prototype.removeMarkers;
MarkerClusterer.prototype['resetViewport'] =
    MarkerClusterer.prototype.resetViewport;
MarkerClusterer.prototype['repaint'] =
    MarkerClusterer.prototype.repaint;
MarkerClusterer.prototype['setCalculator'] =
    MarkerClusterer.prototype.setCalculator;
MarkerClusterer.prototype['setGridSize'] =
    MarkerClusterer.prototype.setGridSize;
MarkerClusterer.prototype['setMaxZoom'] =
    MarkerClusterer.prototype.setMaxZoom;
MarkerClusterer.prototype['onAdd'] = MarkerClusterer.prototype.onAdd;
MarkerClusterer.prototype['draw'] = MarkerClusterer.prototype.draw;

Cluster.prototype['getCenter'] = Cluster.prototype.getCenter;
Cluster.prototype['getSize'] = Cluster.prototype.getSize;
Cluster.prototype['getMarkers'] = Cluster.prototype.getMarkers;

ClusterIcon.prototype['onAdd'] = ClusterIcon.prototype.onAdd;
ClusterIcon.prototype['draw'] = ClusterIcon.prototype.draw;
ClusterIcon.prototype['onRemove'] = ClusterIcon.prototype.onRemove;