Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MarkerClustererPlus: set icon color/url independent of size

I'm plotting a few hundred thousand points via MarkerClustererPlus, and I want to set groups of cluster icons (colour) based on some exteral property (not based on number of markers represented).

The only way I can think to do this is by creating multiple MarkerClusterer objects and passing in a different options object, but I feel like I'll take a big performance hit doing that. Is there a better way?

Marker Clusterer Plus with differently sized icons scaled to fit.

Given the image above, I'd like 139, 24, and 5 to be yellow and 213, 25, 30, and 2 to be red; and if possible, update their styles/options via setOptions:
mc.group[0].setOptions({"url": imgPath +lookupThreshold(severity)+ '.svg' });
mc.group[1].setOptions({"url": imgPath +lookupThreshold(severity)+ '.svg' });

P.S. If anyone is interested, I tweaked the lib so the cluster icon scales to its size by supplying an svg image and increasing the width & height in the options object:

var mcOptions = {
  "styles": [{
    "height": 19,
    "url": img/map/clusters/",
    "width": 19
  },{
    "height": 24,
    "url": img/map/clusters/",
    "width": 24
  }, {…}]
};
for ( var s = mcOptions.styles.length-1; s >= 0; s-- )
{ mcOptions.styles[s].url += lookupThreshold(severity) + '.svg'; }
// lookupThreshold switches severity and returns a string: red, orange, …

Then added the following to markerclusterer.js:

line 275: this.backgroundSize_ = style.backgroundSize || "contain";
line 300: style.push('background-size:' + this.backgroundSize_ + ';');

Works in Ffx 19.0.2, Chrome 26.x, Chrome Canary 28.x, Safari 6.0.2, IE 9.0.8 (but not Opera 12.15).

EDIT It seems there is not much of a performance hit from creating multiple instances of MarkerClusterer; however, it appears that the properties/options object passed to MC is shared amoung the instances of MCs.

Solved I had to modify the MarkerClustererPlus library near line 665 to clone opt_options (the lib was using a reference, which caused all previous opt_options to be overwritten with the newest/last one passed).

like image 402
Jakob Jingleheimer Avatar asked Nov 04 '22 00:11

Jakob Jingleheimer


1 Answers

So it turns out the trouble was coming from the MarkerClustererPlus lib itself:

656:  function MarkerClusterer(map, opt_markers, opt_options) {
…
665:    opt_options = opt_options || {};

Line 665 creates a reference to the existing object, instead of a new copy. I couldn't use MarkerClusterer.prototype.extend from line 1539 because it does not make a deep copy (and it extends only an object's prototype).

So, I wrote my own deep-copy function (jsfiddle), which I made globally available (rather than add it to the prototypes of both Array and Object):

function deepCopy(obj) {  
  this.cloneArr = function (arr) {
    var newArr = [];
    for ( var i = arr.length-1; i >= 0; i-- ) newArr[i] = this.evalObj( arr[i] );
    return newArr;
  };
  this.cloneObj = function(obj) {
    var newObj = {};
    for ( var prop in obj ) newObj[prop] = this.evalObj( obj[prop] );
    return newObj;
  };
  this.evalObj = function(obj) {
    switch ( typeof obj ) {
      case 'object':
        if ( Array.isArray( obj ) ) return this.cloneArr( obj );
        if ( obj instanceof Date === false ) return this.cloneObj( obj );
        // pass thru dates, strings, numbers, booleans, and functions
      default: return obj; // primitive
    }
  };
  return this.evalObj(obj);
}

I then altered MarkerClustererPlus.js to the following:

656:  function MarkerClusterer(map, opt_markers, opt_optionsG) {
…
665:    var opt_options = deepCopy( opt_optionsG ) || {};

I tested having 5 instances of MarkerClustererPlus (each with 5000 markers, 25000 total), and there was no decernable performance impact compared to having a single MC+ instance.

Screenshot of multiple instances of MarkerClustererPlus

like image 64
Jakob Jingleheimer Avatar answered Nov 11 '22 06:11

Jakob Jingleheimer