summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gmap.install15
-rw-r--r--gmap.module17
-rw-r--r--gmap_settings_ui.inc187
-rw-r--r--js/clusterer_marker.js41
-rw-r--r--js/clustermarker_marker.js50
-rw-r--r--js/markermanager_marker.js49
-rwxr-xr-xthirdparty/README.txt18
7 files changed, 348 insertions, 29 deletions
diff --git a/gmap.install b/gmap.install
index c6d167b..8fbcba0 100644
--- a/gmap.install
+++ b/gmap.install
@@ -85,3 +85,18 @@ function gmap_update_5002() {
variable_del('gmap_load_zoom_plugin');
return $ret;
}
+
+/**
+ * Mini-update -- slight tweak to marker manager settings format.
+ */
+function gmap_update_5003() {
+ $ret = array();
+ $val = variable_get('gmap_markermanager', array());
+ if (isset($val['clusterer']) && isset($val['clusterer']['clusterer_file']) && !empty($val['clusterer']['clusterer_file'])) {
+ $val['clusterer']['filename'] = $val['clusterer']['clusterer_file'];
+ unset($val['clusterer']['clusterer_file']);
+ variable_set('gmap_markermanager', $val);
+ }
+ drupal_set_message(t('Border Padding on marker managers is now defaulting to the recommended 256 pixels, rather than 0 pixels. You may wish to change your marker manager settings on the <a href="@url">GMap settings page</a> to match.', array('@url' => url('admin/settings/gmap'))));
+ return $ret;
+}
diff --git a/gmap.module b/gmap.module
index 7d0db6e..2c41b30 100644
--- a/gmap.module
+++ b/gmap.module
@@ -248,21 +248,24 @@ function _gmap_doheader() {
drupal_add_css($gmap_path .'/gmap.css');
drupal_add_js($gmap_path .'/js/gmap.js');
$mm = variable_get('gmap_mm_type', 'gmap');
+ $mms = variable_get('gmap_markermanager', array());
+ if (empty($mms[$mm])) {
+ $mms[$mm] = array();
+ }
// If you really really want to override the marker manager, implement
// this, take $mm by ref, and have fun. --Bdragon
if (function_exists('_gmap_markermanager_override')) {
- _gmap_markermanager_override($mm);
+ _gmap_markermanager_override($mm, $mms);
}
- if ($mm=='clusterer') {
+ if ($mm == 'clusterer' || $mm == 'clustermarker') {
+ // Needed for access to clusterer marker.
drupal_add_js($gmap_path .'/js/icon.js');
- drupal_add_js($gmap_path .'/thirdparty/Clusterer2.js');
+ }
+ if (isset($mms[$mm]['filename'])) {
+ drupal_add_js($gmap_path .'/thirdparty/'. $mms[$mm]['filename']);
}
drupal_add_js($gmap_path .'/js/marker.js');
drupal_add_js($gmap_path .'/js/'. $mm .'_marker.js');
- $mms = variable_get('gmap_markermanager', array());
- if (empty($mms[$mm])) {
- $mms[$mm] = array();
- }
drupal_add_js(array('gmap_markermanager' => $mms[$mm]), 'setting');
// @@@
drupal_add_js($gmap_path .'/js/poly.js');
diff --git a/gmap_settings_ui.inc b/gmap_settings_ui.inc
index cccf2fc..e1d99b5 100644
--- a/gmap_settings_ui.inc
+++ b/gmap_settings_ui.inc
@@ -272,30 +272,56 @@ function _gmap_admin_settings() {
if (!isset($opts['gmarkermanager']) || !is_array($opts['gmarkermanager'])) {
$opts['gmarkermanager'] = array();
}
+ if (!isset($opts['markermanager']) || !is_array($opts['markermanager'])) {
+ $opts['markermanager'] = array();
+ }
if (!isset($opts['clusterer']) || !is_array($opts['clusterer'])) {
$opts['clusterer'] = array();
}
+ if (!isset($opts['clustermarker']) || !is_array($opts['clustermarker'])) {
+ $opts['clustermarker'] = array();
+ }
$opts['gmap'] = array_merge(array(
// None.
), $opts['gmap']);
$opts['gmarkermanager'] = array_merge(array(
- 'borderPadding' => 0,
+ 'borderPadding' => 256,
'maxZoom' => 4,
'trackMarkers' => FALSE,
'markerMinZoom' => 4,
'markerMaxZoom' => 0,
), $opts['gmarkermanager']);
+ $opts['markermanager'] = array_merge(array(
+ 'filename' => 'markermanager_packed.js',
+ 'borderPadding' => 256,
+ 'maxZoom' => 4,
+ 'trackMarkers' => FALSE,
+ 'markerMinZoom' => 4,
+ 'markerMaxZoom' => 0,
+ ), $opts['markermanager']);
+
$opts['clusterer'] = array_merge(array(
- 'clusterer_file' => 'Clusterer2.js',
+ 'filename' => 'Clusterer2.js',
'marker' => 'cluster',
'max_nocluster' => 150,
'cluster_min' => 5,
'max_lines' => 10,
+ 'popup_mode' => 'orig',
), $opts['clusterer']);
+ $opts['clustermarker'] = array_merge(array(
+ 'filename' => 'ClusterMarker.js',
+ 'borderPadding' => 256,
+ 'clusteringEnabled' => TRUE,
+ 'clusterMarkerIcon' => '',
+ 'clusterMarkerTitle' => '',
+ 'fitMapMaxZoom' => 0,
+ 'intersectPadding' => 0,
+ ), $opts['clustermarker']);
+
$form['gmap_markermanager'] = array(
'#type' => 'fieldset',
'#title' => t('Marker manager'),
@@ -308,30 +334,37 @@ function _gmap_admin_settings() {
'#options' => array(
'gmap' => t('No manager (use GMap API directly)'),
'gmarkermanager' => t("Google's GMarkerManager"),
+ 'markermanager' => t('Gmaps Utility Library MarkerManager'),
'clusterer' => t("Jef Poskanzer's Clusterer"),
+ 'clustermarker' => t("Martin Pearman's ClusterMarker"),
),
'#default_value' => variable_get('gmap_mm_type', 'gmap'),
'#description' => t('If you are planning on using many markers on a single map, you may want to consider using a marker manager to speed up map rendering.'),
);
+ // No Manager
$form['gmap_markermanager']['gmap'] = array(
'#type' => 'fieldset',
'#title' => t('Unmanaged marker settings'),
'#description' => t('There are no settings for unmanaged markers.'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
);
+ // Google Maps API GMarkerManager
$form['gmap_markermanager']['gmarkermanager'] = array(
'#type' => 'fieldset',
'#title' => t('GMarkerManager settings'),
'#description' => t('GMarkerManager is a new part of the official Google Maps API that provides a marker manager.'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
);
-
$form['gmap_markermanager']['gmarkermanager']['borderPadding'] = array(
'#type' => 'textfield',
'#title' => t('Border padding'),
'#description' => t('Markers located less than this number of pixels from the viewport will be added to the map by the manager (even if they would be fully invisible.)'),
- '#size' => 3,
- '#maxlength' => 3,
+ '#size' => 4,
+ '#maxlength' => 4,
'#default_value' => $opts['gmarkermanager']['borderPadding'],
);
$form['gmap_markermanager']['gmarkermanager']['maxZoom'] = array(
@@ -360,7 +393,6 @@ function _gmap_admin_settings() {
'#default_value' => $opts['gmarkermanager']['markerMinZoom'],
'#parents' => array('gmap_markermanager', 'gmarkermanager', 'markerMinZoom'),
);
-
$form['gmap_markermanager']['gmarkermanager']['defaults']['markerMaxZoom'] = array(
'#type' => 'select',
'#title' => t('Maximum zoom'),
@@ -370,16 +402,77 @@ function _gmap_admin_settings() {
'#parents' => array('gmap_markermanager', 'gmarkermanager', 'markerMaxZoom'),
);
+ // Gmaps Utility Library -- MarkerManager
+ $form['gmap_markermanager']['markermanager'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('MarkerManager settings'),
+ '#description' => t('MarkerManager is the improved open source version of GMarkerManager. To use, you must download it from <a href="@url">here</a> and place it in the <em>thirdparty</em> folder.', array('@url' => 'http://gmaps-utility-library.googlecode.com/svn/trunk/markermanager/release/src/')),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ );
+ $form['gmap_markermanager']['markermanager']['filename'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Filename'),
+ '#description' => t('Name of downloaded file in the thirdparty folder. Default: %default', array('%default' => 'markermanager_packed.js')),
+ '#default_value' => $opts['markermanager']['filename'],
+ );
+ $form['gmap_markermanager']['markermanager']['borderPadding'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Border padding'),
+ '#description' => t('Markers located less than this number of pixels from the viewport will be added to the map by the manager (even if they would be fully invisible.)'),
+ '#size' => 4,
+ '#maxlength' => 4,
+ '#default_value' => $opts['markermanager']['borderPadding'],
+ );
+ $form['gmap_markermanager']['markermanager']['maxZoom'] = array(
+ '#type' => 'select',
+ '#title' => t('Maximum zoom'),
+ '#options' => drupal_map_assoc(range(0, 17)),
+ '#description' => t('At the specified zoom level and above, the marker manager will disable itself for additional speed.'),
+ '#default_value' => $opts['markermanager']['maxZoom'],
+ );
+ $form['gmap_markermanager']['markermanager']['trackMarkers'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Track markers'),
+ '#description' => t('If enabled, the marker manager will track marker movements. Leave off unless you need to move markers around with setPoint.'),
+ '#default_value' => $opts['markermanager']['trackMarkers'],
+ );
+ $form['gmap_markermanager']['markermanager']['defaults'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Marker defaults'),
+ '#description' => t('Default marker-specific settings for MarkerManager. Markers will appear when the current zoom level is between minZoom and maxZoom.'),
+ );
+ $form['gmap_markermanager']['markermanager']['defaults']['markerMinZoom'] = array(
+ '#type' => 'select',
+ '#title' => t('Minimum zoom'),
+ '#options' => drupal_map_assoc(range(0, 17)),
+ '#description' => t('At the specified zoom level and above (i.e. zooming in,) the marker will be shown. Choose 0 to show markers at all zoom levels by default.'),
+ '#default_value' => $opts['markermanager']['markerMinZoom'],
+ '#parents' => array('gmap_markermanager', 'markermanager', 'markerMinZoom'),
+ );
+
+ $form['gmap_markermanager']['markermanager']['defaults']['markerMaxZoom'] = array(
+ '#type' => 'select',
+ '#title' => t('Maximum zoom'),
+ '#options' => drupal_map_assoc(range(0, 17)),
+ '#description' => t('At the specified zoom level and above (i.e. zooming in,) the marker will be hidden. Choose 0 to disable by default.'),
+ '#default_value' => $opts['markermanager']['markerMaxZoom'],
+ '#parents' => array('gmap_markermanager', 'markermanager', 'markerMaxZoom'),
+ );
+
+ // Jef Poskanzer's Clusterer
$form['gmap_markermanager']['clusterer'] = array(
'#type' => 'fieldset',
'#title' => t('Clusterer settings'),
'#description' => t("Clusterer is a marker manager written by Jef Poskanzer of acme.com. To use, you must place Clusterer2.js (available ".'<a href="@url">here</a>) into the "thirdparty" folder.', array('@url' => 'http://acme.com/javascript/Clusterer2.js')),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
);
- $form['gmap_markermanager']['clusterer']['clusterer_file'] = array(
+ $form['gmap_markermanager']['clusterer']['filename'] = array(
'#type' => 'textfield',
- '#title' => t('Clusterer filename'),
- '#description' => t('Set the name of the Clusterer file in the thirdparty folder.'),
- '#default_value' => $opts['clusterer']['clusterer_file'],
+ '#title' => t('Filename'),
+ '#description' => t('Name of downloaded file in the thirdparty folder. Default: %default', array('%default' => 'Clusterer2.js')),
+ '#default_value' => $opts['clusterer']['filename'],
);
$form['gmap_markermanager']['clusterer']['marker'] = array(
'#type' => 'gmap_markerchooser',
@@ -392,8 +485,8 @@ function _gmap_admin_settings() {
'#title' => t('Activate on'),
'#field_suffix' => t('or more markers'),
'#description' => t("Clustering is enabled when more than the specified number of markers are visible at the same time."),
- '#size' => 3,
- '#maxlength' => 3,
+ '#size' => 4,
+ '#maxlength' => 4,
'#default_value' => $opts['clusterer']['max_nocluster'],
);
$form['gmap_markermanager']['clusterer']['cluster_min'] = array(
@@ -401,8 +494,8 @@ function _gmap_admin_settings() {
'#title' => t('Cluster on'),
'#field_suffix' => t('or more markers'),
'#description' => t("Minimal number of markers per cluster"),
- '#size' => 2,
- '#maxlength' => 2,
+ '#size' => 3,
+ '#maxlength' => 3,
'#default_value' => $opts['clusterer']['cluster_min'],
);
$form['gmap_markermanager']['clusterer']['max_lines'] = array(
@@ -411,10 +504,72 @@ function _gmap_admin_settings() {
'#field_prefix' => t('at most'),
'#field_suffix' => t('lines'),
'#description' => t("Maximum number of lines per info box"),
- '#size' => 2,
- '#maxlength' => 2,
+ '#size' => 3,
+ '#maxlength' => 3,
'#default_value' => $opts['clusterer']['max_lines'],
);
+ $form['gmap_markermanager']['clusterer']['popup_mode'] = array(
+ '#type' => 'radios',
+ '#title' => t('Popup mode'),
+ '#options' => array('orig' => t('Original'), 'zoom' => t('Zoom to Cluster')),
+ '#default_value' => $opts['clusterer']['popup_mode'],
+ );
+
+ // Martin Pearman's ClusterMarker
+ $form['gmap_markermanager']['clustermarker'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('ClusterMarker settings'),
+ '#description' => t('ClusterMarker is a marker manager written by Martin Pearman. To use, you must download it from <a href="@url">here</a> and extract the appropriate file to the <em>thirdparty</em> folder.', array('@url' => 'http://googlemapsapi.martinpearman.co.uk/downloads.php?cat_id=1')),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ );
+ $form['gmap_markermanager']['clustermarker']['filename'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Filename'),
+ '#description' => t('Name of downloaded file in the thirdparty folder. Default: %default', array('%default' => 'ClusterMarker.js')),
+ '#default_value' => $opts['clustermarker']['filename'],
+ );
+ $form['gmap_markermanager']['clustermarker']['borderPadding'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Border padding'),
+ '#description' => t('Markers located less than this number of pixels from the viewport will be added to the map by the manager (even if they would be fully invisible.)'),
+ '#size' => 4,
+ '#maxlength' => 4,
+ '#default_value' => $opts['clustermarker']['borderPadding'],
+ );
+ $form['gmap_markermanager']['clustermarker']['clusteringEnabled'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Clustering enabled'),
+ '#description' => t('Whether to use clustering or not.'),
+ '#default_value' => $opts['clustermarker']['clusteringEnabled'],
+ );
+ $form['gmap_markermanager']['clustermarker']['clusterMarkerIcon'] = array(
+ '#type' => 'gmap_markerchooser',
+ '#title' => t('Marker for clusters'),
+ '#description' => t('The marker to use when creating a cluster.'),
+ '#default_value' => $opts['clustermarker']['clusterMarkerIcon'],
+ );
+ $form['gmap_markermanager']['clustermarker']['clusterMarkerTitle'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Title for clusters'),
+ '#description' => t("The title to use for clusters. %count will be replaced with the number of markers in the cluster."),
+ '#default_value' => $opts['clustermarker']['clusterMarkerTitle'],
+ );
+ $form['gmap_markermanager']['clustermarker']['fitMapMaxZoom'] = array(
+ '#type' => 'select',
+ '#title' => t('Maximum zoom for zooming to cluster'),
+ '#options' => drupal_map_assoc(range(0, 17)),
+ '#description' => t('When clicking on a cluster, the map will not zoom in farther than this.'),
+ '#default_value' => $opts['clustermarker']['fitMapMaxZoom'],
+ );
+ $form['gmap_markermanager']['clustermarker']['intersectPadding'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Intersect Padding'),
+ '#description' => t('Number of pixels to pad marker icons by when determining whether they intersect.'),
+ '#default_value' => $opts['clustermarker']['intersectPadding'],
+ '#size' => 4,
+ '#maxlength' => 4,
+ );
$form['#validate']['gmap_admin_settings_validate'] = array();
diff --git a/js/clusterer_marker.js b/js/clusterer_marker.js
index 6a3a48b..bb300a2 100644
--- a/js/clusterer_marker.js
+++ b/js/clusterer_marker.js
@@ -20,15 +20,15 @@ Drupal.gmap.addHandler('gmap', function (elem) {
obj.clusterer = new Clusterer(obj.map);
var s = Drupal.settings.gmap_markermanager;
if (s) {
- obj.clusterer.SetMaxVisibleMarkers(s.max_nocluster);
- obj.clusterer.SetMinMarkersPerCluster(s.cluster_min);
- obj.clusterer.SetMaxLinesPerInfoBox(s.max_lines);
+ obj.clusterer.SetMaxVisibleMarkers(+s.max_nocluster);
+ obj.clusterer.SetMinMarkersPerCluster(+s.cluster_min);
+ obj.clusterer.SetMaxLinesPerInfoBox(+s.max_lines);
}
});
obj.bind('iconsready', function () {
var s = Drupal.settings.gmap_markermanager;
- if (s) {
+ if (s.marker.length) {
obj.clusterer.SetIcon(Drupal.gmap.getIcon(s.marker, 0));
}
});
@@ -53,3 +53,36 @@ Drupal.gmap.addHandler('gmap', function (elem) {
obj.map.clearOverlays();
});
});
+
+////////////////// Clusterer overrides section //////////////////
+
+// Store original implementations of overridden functions
+Clusterer.origFunctions = {};
+
+// Alternate popup code from: http://drupal.org/node/155104#comment-574696
+Clusterer.origFunctions.PopUp = Clusterer.PopUp;
+Clusterer.PopUp = function (cluster) {
+ var mode = Drupal.settings.gmap_markermanager.popup_mode;
+ if (mode === 'orig') {
+ return Clusterer.origFunctions.PopUp(cluster);
+ }
+ else if (mode === 'zoom') {
+ var bounds = new GLatLngBounds();
+ for (var k in cluster.markers)
+ bounds.extend(cluster.markers[k].getPoint());
+
+ var sw = bounds.getSouthWest();
+ var ne = bounds.getNorthEast();
+ var rect = [
+ sw,
+ new GLatLng(sw.lat(), ne.lng()),
+ ne,
+ new GLatLng(ne.lat(), sw.lng()),
+ sw
+ ];
+
+ var center = bounds.getCenter();
+ var zoom = cluster.clusterer.map.getBoundsZoomLevel(bounds);
+ cluster.clusterer.map.setCenter(new GLatLng(+center.lat(), +center.lng()), +zoom);
+ }
+};
diff --git a/js/clustermarker_marker.js b/js/clustermarker_marker.js
new file mode 100644
index 0000000..25798b2
--- /dev/null
+++ b/js/clustermarker_marker.js
@@ -0,0 +1,50 @@
+/* $Id$ */
+
+/**
+ * @file
+ * GMap Markers
+ * Martin Pearman's ClusterMarker version
+ */
+
+/*global ClusterMarker, Drupal, GMarker */
+
+// Replace to override marker creation
+Drupal.gmap.factory.marker = function (loc, opts) {
+ return new GMarker(loc, opts);
+};
+
+Drupal.gmap.addHandler('gmap', function (elem) {
+ var obj = this;
+
+ obj.bind('init', function () {
+ obj.clustermarker = 0;
+ });
+
+ obj.bind('iconsready', function () {
+ if (!obj.clustermarker) {
+ var options = Drupal.settings.gmap_markermanager;
+ if (options.clusterMarkerIcon.length) {
+ options.clusterMarkerIcon = Drupal.gmap.getIcon(options.clusterMarkerIcon, 0);
+ }
+ else {
+ delete options.clusterMarkerIcon;
+ }
+ options.borderPadding = +options.borderPadding;
+ options.fitMapMaxZoom = +options.fitMapMaxZoom;
+ options.intersectPadding = +options.intersectPadding;
+ obj.clusterMarker = new ClusterMarker(obj.map, options);
+ }
+ });
+
+ obj.bind('addmarker', function (marker) {
+ obj.clusterMarker.addMarkers([marker.marker]);
+ });
+
+ obj.bind('delmarker', function (marker) {
+ // @@@TODO: Implement this!
+ });
+
+ obj.bind('clearmarkers', function () {
+ obj.clusterMarker.removeMarkers();
+ });
+});
diff --git a/js/markermanager_marker.js b/js/markermanager_marker.js
new file mode 100644
index 0000000..31cb732
--- /dev/null
+++ b/js/markermanager_marker.js
@@ -0,0 +1,49 @@
+/* $Id$ */
+
+/**
+ * @file
+ * GMap Markers
+ * Gmaps Utility Library MarkerManager API version
+ */
+
+/*global Drupal, GMarker, MarkerManager */
+
+// Replace to override marker creation
+Drupal.gmap.factory.marker = function (loc, opts) {
+ return new GMarker(loc, opts);
+};
+
+Drupal.gmap.addHandler('gmap', function (elem) {
+ var obj = this;
+
+ obj.bind('init', function () {
+ // Set up the markermanager.
+ obj.mm = new MarkerManager(obj.map, Drupal.settings.gmap_markermanager);
+ });
+
+ obj.bind('addmarker', function (marker) {
+ var minzoom = Drupal.settings.gmap_markermanager.markerMinZoom;
+ var maxzoom = Drupal.settings.gmap_markermanager.markerMaxZoom;
+ if (marker.minzoom) {
+ minzoom = marker.minzoom;
+ }
+ if (marker.maxzoom) {
+ maxzoom = marker.maxzoom;
+ }
+ if (maxzoom > 0) {
+ obj.mm.addMarker(marker.marker, minzoom, maxzoom);
+ }
+ else {
+ obj.mm.addMarker(marker.marker, minzoom);
+ }
+ obj.mm.refresh();
+ });
+
+ obj.bind('delmarker', function (marker) {
+ obj.mm.removeMarker(marker.marker);
+ });
+
+ obj.bind('clearmarkers', function () {
+ obj.mm.clearMarkers();
+ });
+});
diff --git a/thirdparty/README.txt b/thirdparty/README.txt
index ba24077..1079d68 100755
--- a/thirdparty/README.txt
+++ b/thirdparty/README.txt
@@ -5,7 +5,7 @@ Place downloaded third party code in this folder if instructed to by GMap.
(todo: document better)
-clusterer2.js
+Clusterer2.js
------------
Marker clusterer addon by Jef Poskanzer
This was the original "marker clustering" solution for viewing large sets of markers without
@@ -22,4 +22,18 @@ The specifics are still up in the air at this point.
http://geoserver.org/display/GEOSDOC/Google+Maps
http://chignik.berkeley.edu/google/wms236.js
http://dist.codehaus.org/geoserver/gmaps-geoserver_scripts/wms-gs-1_0_0.js
-http://dist.codehaus.org/geoserver/gmaps-geoserver_scripts/wms-gs-1_0_1.js \ No newline at end of file
+http://dist.codehaus.org/geoserver/gmaps-geoserver_scripts/wms-gs-1_0_1.js
+
+ClusterMarker.js
+----------------
+Marker clusterer addon by Martin Pearman
+This is a new "marker clustering" solution similar to Clusterer2.js.
+Website: http://googlemapsapi.martinpearman.co.uk/articles.php?cat_id=1
+Tested version: 1.3.2
+
+MarkerManager.js
+----------------
+Google Maps API Utility Library addon
+This is an improved version of GMarkerManager.
+Website: http://code.google.com/p/gmaps-utility-library/
+Tested version: 1.0