1 2 3 vq.PaCo= function() { 4 vq.Vis.call(this); 5 6 //set option variables to useful values before options are set. 7 this.height(400); // defaults 8 this.width(600); // defaults 9 this.vertical_padding(0); 10 this.horizontal_padding(0); 11 12 }; 13 14 vq.PaCo.prototype = pv.extend(vq.Vis); 15 16 vq.PaCo.prototype._setOptionDefaults = function(options) { 17 18 if (options.height != null) { 19 this.height(options.height); 20 } 21 22 if (options.width != null) { 23 this.width(options.width); 24 } 25 26 if (options.vertical_padding != null) { 27 this.vertical_padding(options.vertical_padding); 28 } 29 30 if (options.horizontal_padding != null) { 31 this.horizontal_padding(options.horizontal_padding); 32 } 33 34 if (options.container != null) { 35 this.container(options.container); 36 } 37 }; 38 39 vq.PaCo.prototype.draw = function(data) { 40 41 this._pa_co_data = new vq.models.PaCoData(data); 42 43 if (this._pa_co_data.isDataReady()) { 44 this._setOptionDefaults(this._pa_co_data); 45 this.render(); 46 } 47 48 }; 49 50 vq.PaCo.prototype.getFiltered = function() { 51 var that = this, 52 dataObj = that._pa_co_data; 53 var data = dataObj._data; 54 filtered_data = data.filter( 55 function(d) { return dataObj._dims.every(function(dim) {return d[dim] != undefined;}) && 56 dataObj._dims.every( function(dim) { return d[dim] <= that.filter[dim].max && 57 d[dim] >= that.filter[dim].min ;});}); 58 59 return filtered_data; 60 }; 61 62 vq.PaCo.prototype.getSelected = function() { 63 return this._pa_co_data.selected||null; 64 }; 65 66 vq.PaCo.prototype.render = function() { 67 var that = this; 68 var w = that.width(), 69 h = that.height(), 70 div = that.container(), 71 dataObj = that._pa_co_data, 72 cursor = [{x:0,y:0,dx:0,dy:0}], 73 cursor_label = {label:'',coordinate:'',value:0.0}; 74 75 dataObj.hovered = null; 76 77 78 var x = pv.Scale.ordinal(dataObj._dims).splitFlush(0, w), 79 y = {}, 80 c = {}; 81 82 dataObj._dims.forEach( function(t) { 83 if (dataObj._coordinates[dataObj._coordinates_map[t]].scale_type == undefined) { 84 dataObj._coordinates[dataObj._coordinates_map[t]].scale_type = 'linear'; 85 } 86 87 var config = dataObj._coordinates[dataObj._coordinates_map[t]]; 88 var min_val = config.min_value != undefined ? config.min_value : 89 pv.min(dataObj._data,function(a) { return a[t];}); 90 91 var max_val = config.max_value != undefined ? config.max_value : 92 pv.max(dataObj._data,function(a) { return a[t];}); 93 94 switch(config.scale_type) { 95 case('log'): 96 min_val = min_val > 0 ? min_val : 0.1; 97 y[t] = pv.Scale.log( min_val, max_val).range(0,h).nice(); 98 c[t] = pv.Scale.log( min_val,max_val).range("steelblue", "brown").nice(); 99 break; 100 case('linear') : 101 case('default'): 102 y[t]= pv.Scale.linear( min_val, max_val).range(0,h); 103 c[t]= pv.Scale.linear( min_val, max_val).range("steelblue", "brown"); 104 break; 105 }}); 106 107 /* Interaction state. */ 108 that.filter = pv.dict(dataObj._dims, function(t) { 109 return {min: y[t].domain()[0], max: y[t].domain()[1]}; 110 }), active = dataObj._dims[0]; 111 112 var vis = new pv.Panel() 113 .width(w) 114 .height(h) 115 .left(that.horizontal_padding()) 116 .right(that.horizontal_padding()) 117 .top(that.vertical_padding()) 118 .bottom(that.vertical_padding()) 119 .canvas(div); 120 121 // The parallel coordinates display. 122 vis.add(pv.Panel) 123 .data(dataObj._data) 124 .visible(function(d) { return dataObj._dims.every(function(t) 125 { return d[t] <= that.filter[t].max && 126 d[t] >= that.filter[t].min ;});}) 127 .add(pv.Line) 128 .data(dataObj._dims) 129 .left(function(t) { return x(t);}) 130 .bottom(function(t, d) { return y[t](d[t]);}) 131 .strokeStyle("#ddd") 132 .lineWidth(1) 133 .antialias(false); 134 135 // Rule per dimension. 136 rule = vis.add(pv.Rule) 137 .data(dataObj._dims) 138 .left(x); 139 140 // Dimension label 141 rule.anchor("top").add(pv.Label) 142 .top(-12) 143 .font("bold 16px sans-serif") 144 .text(function(d) { return d;}); 145 146 147 // The parallel coordinates display. 148 var change = vis.add(pv.Panel); 149 150 151 function filter_data() { 152 return dataObj._data.filter(function(d) { 153 return dataObj._dims.every(function(t) 154 { return d[t] <= that.filter[t].max && 155 d[t] >= that.filter[t].min ;});}); 156 } 157 158 function invert_x(d) { 159 var closest_dim=dataObj._dims[0]; 160 var distance = 999999; 161 dataObj._dims.forEach(function(dim) {var dist = Math.abs(x(dim)-d); 162 if(distance > dist) {closest_dim=dim; distance = dist; }}); 163 return closest_dim; 164 } 165 //separate click event from handle events 166 var line_panel = change.add(pv.Panel) 167 .event('click', function(c) {dataObj.selected = dataObj.hovered; highlight.render();}) 168 .events('all'); 169 170 function overLine(c,d) { 171 var cursor_label = {}; 172 dataObj.hovered = d; 173 cursor[0] = this.mouse(); 174 var dim = invert_x(cursor[0].x); 175 cursor_label.label = d[dataObj._label_column]; 176 cursor_label.coordinate = dim; 177 cursor_label.value = d[dim]; 178 var config={}; 179 config[cursor_label['label']] = function(cursor) { return cursor.coordinate + ' ' + cursor.value;}; 180 pv.Behavior.flextip( { 181 include_header : false, 182 include_footer : false, 183 timeout : 20, 184 param_data:true, 185 on_mark:false, 186 close_timeout : 'inf', 187 data_config : config 188 } ).call(this.parent,cursor_label); 189 dataObj.highlight.render(); 190 } 191 192 193 var line = line_panel.add(pv.Panel) 194 .data(filter_data) 195 196 .add(pv.Line) 197 .data(dataObj._dims) 198 .events('painted') 199 .event('click', function(c) {dataObj.selected = dataObj.hovered; highlight.render();}) 200 .event('mouseover', overLine) 201 .left(function(t, d) { return x(t);}) 202 .bottom(function(t, d) { return y[t](d[t]);}) 203 .strokeStyle(function(t, d) { return c[active](d[active]);}) 204 .lineWidth(1); 205 206 207 208 var highlight = change.add(pv.Panel) 209 .data(function() {return [dataObj.hovered,dataObj.selected];}) 210 .event('click', function(c) {dataObj.selected = dataObj.hovered; highlight.render();}) 211 .visible(function(d) { return d != undefined;}); 212 highlight.add(pv.Line) 213 .data(dataObj._dims) 214 .events('painted') 215 .event('mouseover',overLine) 216 .strokeStyle(function(t,d) {return this.parent.index == 1 ? 'red' : 'yellow';}) 217 .left(function(t, d) { return x(t);}) 218 .lineWidth(2.5) 219 .bottom(function(t, d) { return y[t](d[t]);}); 220 221 dataObj.highlight = highlight; 222 223 var handle = change.add(pv.Panel); 224 225 // Updater for slider and resizer. 226 function update(d) { 227 var t = d.dim; 228 that.filter[t].min = Math.max(y[t].domain()[0], y[t].invert(h - d.y - d.dy)); 229 that.filter[t].max = Math.min(y[t].domain()[1], y[t].invert(h - d.y)); 230 active = t; 231 change.render(); 232 return false; 233 } 234 235 // Updater for slider and resizer. 236 function selectAll(d) { 237 if (d.dy < 3) { 238 var t = d.dim; 239 that.filter[t].min = Math.max(y[t].domain()[0], y[t].invert(0)); 240 that.filter[t].max = Math.min(y[t].domain()[1], y[t].invert(h)); 241 d.y = 0; d.dy = h; 242 active = t; 243 change.render(); 244 } 245 return false; 246 } 247 248 /* Handle select and drag */ 249 handle 250 .data(dataObj._dims.map(function(dim) { return {y:0, dy:h, dim:dim}; })) 251 .left(function(t) { return x(t.dim) - 15;}) 252 .width(30) 253 .fillStyle("rgba(0,0,0,.1)") 254 .strokeStyle("rgba(0,0,0,.5)") 255 .lineWidth(".2px") 256 .events('all') 257 .cursor("crosshair") 258 .event("mousedown", pv.Behavior.select()) 259 .event("select", update) 260 .event("selectend", selectAll) 261 .add(pv.Bar) 262 .left(10) 263 .top(function(d) { return d.y;}) 264 .width(10) 265 .height(function(d) { return d.dy;}) 266 .fillStyle(function(t) { return t.dim == active 267 ? c[t.dim]((that.filter[t.dim].max + that.filter[t.dim].min) / 2) 268 : "hsla(0,0,50%,.5)";}) 269 .strokeStyle("white") 270 .cursor("move") 271 .event("mousedown", pv.Behavior.drag()) 272 .event("dragstart", update) 273 .event("drag", update); 274 275 handle.anchor("bottom").add(pv.Label) 276 .textBaseline("top") 277 .text(function(d) { return that.filter[d.dim].min.toPrecision(3);}); 278 279 handle.anchor("top").add(pv.Label) 280 .textBaseline("bottom") 281 .text(function(d) { return that.filter[d.dim].max.toPrecision(3);}); 282 283 vis.render(); 284 }; 285 286 vq.models.PaCoData = function(data) { 287 288 vq.models.VisData.call(this,data); 289 this.setDataModel(); 290 if (this.getDataType() == 'vq.models.PaCoData') { 291 this._build_data(this.getContents()); 292 } else { 293 console.warn('Unrecognized JSON object. Expected vq.models.PaCoData object.'); 294 } 295 }; 296 297 vq.models.PaCoData.prototype = pv.extend(vq.models.VisData); 298 299 300 /** 301 * @private Builds the data structure up. 302 * @param data Contents of JSON data structure 303 */ 304 vq.models.PaCoData.prototype._build_data = function(data) { 305 var that = this; 306 307 this._processData(data); 308 309 if (this._coordinates != null) { 310 this._dims=this._coordinates.map(function(coord) { return coord.id;}); 311 this._coordinates_map = pv.numerate(that._coordinates,function(d) {return d.id;}); 312 this.setDataReady(true); 313 } else { 314 console.error('Coordinate configuration not passed to constructor!'); 315 } 316 }; 317 318 vq.models.PaCoData.prototype.setDataModel = function () { 319 this._dataModel = [ 320 {label: 'width', id: 'PLOT.width', cast : Number, defaultValue: 700}, 321 {label: 'height', id: 'PLOT.height', cast : Number, defaultValue: 300}, 322 {label : 'container', id:'PLOT.container', optional : true}, 323 {label: 'vertical_padding', id: 'PLOT.vertical_padding', cast : Number, defaultValue: 20}, 324 {label: 'horizontal_padding', id: 'PLOT.horizontal_padding',cast : Number, defaultValue:30}, 325 {label : '_data', id: 'data_array', defaultValue : [] }, 326 {label : '_identifier_column', id: 'CONFIGURATION.identifier_column ',cast : String, defaultValue : ''}, 327 {label : '_label_column', id: 'CONFIGURATION.label_column',cast : String, defaultValue : ''}, 328 {label : '_coordinates', id: 'CONFIGURATION.COORD_COLUMN_ARRAY', defaultValue : []}, 329 {label : 'tooltipItems', id: 'tooltip_items', defaultValue : { 330 Name : 'nodeName', 331 Parent : 'parentNode.nodeName' 332 } }, 333 {label : '_notifier', id: 'notifier', cast : Function, defaultValue : function() {return null;}} 334 ]; 335 };