Vishful thinking…

Creating a LEAN and FAST custom marker overlay for Google Maps

Posted in Uncategorized by viswaug on December 18, 2008

Before I start, I need to say that the link to the code below was provided to me by Pamela Fox. The sample below illustrates how to create a light weight custom marker GOverlay for Google maps that is REALLY FAST. And it allows for a multitude of customizations as you see fit. It was just too good not to share. Please find the the tweak I made to the code to allow for it to do a little bit of thematic mapping in just minutes. The demo below also allows you to check out the performance of the custom overlay with large number of markers loaded.

CustomGOverlay

Here are the reasons provided by Pamela Fox for the default marker overlay in Google Maps to be slower than the custom marker overlay below.

The default marker class (GMarker) is designed so that it works well across-browsers, has hot spot shape, can be draggable, has a shadow, prints well in any browser, etc. It is not optimized for performance.

Here is the custom marker overlay code sample.

   1: /* A Bar is a simple overlay that outlines a lat/lng bounds on the
   2:  * map. It has a border of the given weight and color and can optionally
   3:  * have a semi-transparent background color.
   4:  * @param latlng {GLatLng} Point to place bar at.
   5:  * @param opts {Object Literal} Passes configuration options - 
   6:  *   weight, color, height, width, text, and offset.
   7:  */
   8: function MarkerLight(latlng, opts) {
   9:   this.latlng = latlng;
  10:  
  11:   if (!opts) opts = {};
  12:  
  13:   this.height_ = opts.height || 32;
  14:   this.width_ = opts.width || 32;
  15:   this.image_ = opts.image;
  16:   this.imageOver_ = opts.imageOver;
  17:   this.clicked_ = 0;
  18: }
  19:  
  20: /* MarkerLight extends GOverlay class from the Google Maps API
  21:  */
  22: MarkerLight.prototype = new GOverlay();
  23:  
  24: /* Creates the DIV representing this MarkerLight.
  25:  * @param map {GMap2} Map that bar overlay is added to.
  26:  */
  27: MarkerLight.prototype.initialize = function(map) {
  28:   var me = this;
  29:  
  30:   // Create the DIV representing our MarkerLight
  31:   var div = document.createElement("div");
  32:   div.style.border = "1px solid white";
  33:   div.style.position = "absolute";
  34:   div.style.paddingLeft = "0px";
  35:   div.style.cursor = 'pointer';
  36:  
  37:   var img = document.createElement("img");
  38:   img.src = me.image_;
  39:   img.style.width = me.width_ + "px";
  40:   img.style.height = me.height_ + "px";
  41:   div.appendChild(img);  
  42:  
  43:   GEvent.addDomListener(div, "click", function(event) {
  44:     me.clicked_ = 1;
  45:     GEvent.trigger(me, "click");
  46:   });
  47:  
  48:   map.getPane(G_MAP_MARKER_PANE).appendChild(div);
  49:  
  50:   this.map_ = map;
  51:   this.div_ = div;
  52: };
  53:  
  54: /* Remove the main DIV from the map pane
  55:  */
  56: MarkerLight.prototype.remove = function() {
  57:   this.div_.parentNode.removeChild(this.div_);
  58: };
  59:  
  60: /* Copy our data to a new MarkerLight
  61:  * @return {MarkerLight} Copy of bar
  62:  */
  63: MarkerLight.prototype.copy = function() {
  64:   var opts = {};
  65:   opts.color = this.color_;
  66:   opts.height = this.height_;
  67:   opts.width = this.width_;
  68:   opts.image = this.image_;
  69:   opts.imageOver = this.image_;
  70:   return new MarkerLight(this.latlng, opts);
  71: };
  72:  
  73: /* Redraw the MarkerLight based on the current projection and zoom level
  74:  * @param force {boolean} Helps decide whether to redraw overlay
  75:  */
  76: MarkerLight.prototype.redraw = function(force) {
  77:  
  78:   // We only need to redraw if the coordinate system has changed
  79:   if (!force) return;
  80:  
  81:   // Calculate the DIV coordinates of two opposite corners 
  82:   // of our bounds to get the size and position of our MarkerLight
  83:   var divPixel = this.map_.fromLatLngToDivPixel(this.latlng);
  84:  
  85:   // Now position our DIV based on the DIV coordinates of our bounds
  86:   this.div_.style.width = this.width_ + "px";
  87:   this.div_.style.left = (divPixel.x) + "px"
  88:   this.div_.style.height = (this.height_) + "px";
  89:   this.div_.style.top = (divPixel.y) - this.height_ + "px";
  90: };
  91:  
  92: MarkerLight.prototype.getZIndex = function(m) {
  93:   return GOverlay.getZIndex(marker.getPoint().lat())-m.clicked*10000;
  94: }
  95:  
  96: MarkerLight.prototype.getPoint = function() {
  97:   return this.latlng;
  98: };
  99:  
 100: MarkerLight.prototype.setStyle = function(style) {
 101:   for (s in style) {
 102:     this.div_.style[s] = style[s];
 103:   }
 104: };
 105:  
 106: MarkerLight.prototype.setImage = function(image) {
 107:   this.div_.style.background = 'url("' + image + '")';
 108: }
 109: