diff --git a/lib/galaxy/visualization/data_providers/basic.py b/lib/galaxy/visualization/data_providers/basic.py index ef4c630ceb6..56fd740f4c7 100644 --- a/lib/galaxy/visualization/data_providers/basic.py +++ b/lib/galaxy/visualization/data_providers/basic.py @@ -105,7 +105,12 @@ class ColumnDataProvider( BaseDataProvider ): # set up the response, column lists response = {} response[ 'data' ] = data = [ [] for column in columns ] - response[ 'meta' ] = meta = [ { 'min': None, 'max': None } for column in columns ] + response[ 'meta' ] = meta = [{ + 'min' : None, + 'max' : None, + 'count' : 0, + 'sum' : 0 + } for column in columns ] column_types = [ self.original_dataset.metadata.column_types[ column ] for column in columns ] @@ -135,18 +140,39 @@ class ColumnDataProvider( BaseDataProvider ): #NOTE: this will return None/null for abberrant column values (including bad indeces) for index, column in enumerate( columns ): column_val = None + column_type = column_types[ index ] if column < fields_len: - column_val = cast_val( fields[ column ], column_types[ index ] ) + column_val = cast_val( fields[ column ], column_type ) if column_val != None: - if( meta[ index ][ 'min' ] == None - or column_val < meta[ index ][ 'min' ] ): - meta[ index ][ 'min' ] = column_val - if( meta[ index ][ 'max' ] == None - or column_val > meta[ index ][ 'max' ] ): - meta[ index ][ 'max' ] = column_val + + # if numeric, maintain min, max, sum + if( column_type == 'float' or column_type == 'int' ): + if( ( meta[ index ][ 'min' ] == None ) or ( column_val < meta[ index ][ 'min' ] ) ): + meta[ index ][ 'min' ] = column_val + + if( ( meta[ index ][ 'max' ] == None ) or ( column_val > meta[ index ][ 'max' ] ) ): + meta[ index ][ 'max' ] = column_val + + meta[ index ][ 'sum' ] += column_val + + # maintain a count - for other stats + meta[ index ][ 'count' ] += 1 data[ index ].append( column_val ) response[ 'endpoint' ] = dict( last_line=( count - 1 ), file_ptr=f.tell() ) f.close() + for index, meta in enumerate( response[ 'meta' ] ): + count = meta[ 'count' ] + meta[ 'mean' ] = float( meta[ 'sum' ] ) / count + + sorted_data = sorted( response[ 'data' ][ index ] ) + # even data count - + middle_index = count / 2 - 1 + if count % 2 == 0: + meta[ 'median' ] = sum( sorted_data[ middle_index : ( middle_index + 1 ) ] ) / 2.0 + + else: + meta[ 'median' ] = sorted_data[ middle_index ] + return response diff --git a/static/scripts/libs/jquery/jquery-ui-1.8.23.custom.min.js b/static/scripts/libs/jquery/jquery-ui-1.8.23.custom.min.js index 9918c0b82b1..dfa1292e282 100644 --- a/static/scripts/libs/jquery/jquery-ui-1.8.23.custom.min.js +++ b/static/scripts/libs/jquery/jquery-ui-1.8.23.custom.min.js @@ -22,4 +22,4 @@ * https://github.com/jquery/jquery-ui * Includes: jquery.ui.slider.js * Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */ -(function(a,b){var c=5;a.widget("ui.slider",a.ui.mouse,{widgetEventPrefix:"slide",options:{animate:!1,distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null},_create:function(){var b=this,d=this.options,e=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),f="",g=d.values&&d.values.length||1,h=[];this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget"+" ui-widget-content"+" ui-corner-all"+(d.disabled?" ui-slider-disabled ui-disabled":"")),this.range=a([]),d.range&&(d.range===!0&&(d.values||(d.values=[this._valueMin(),this._valueMin()]),d.values.length&&d.values.length!==2&&(d.values=[d.values[0],d.values[0]])),this.range=a("
").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(d.range==="min"||d.range==="max"?" ui-slider-range-"+d.range:"")));for(var i=e.length;i\n Use the following controls to how the chart is displayed.\n The slide controls can be moved by the mouse or, if the \'handle\' is in focus, your keyboard\'s arrow keys.\n Move the focus between controls by using the tab or shift+tab keys on your keyboard.\n Use the \'Draw\' button to render (or re-render) the chart with the current settings.\n
\n \n \n\n \n\n\n Uncheck this to disable the animations used on the graph\n
\nBy column:
\n| X | Y | \n ';c=l.stats;c=e.each.call(l,c,{hash:{},inverse:m.noop,fn:m.program(1,d,j)});if(c||c===0){h+=c}h+="\n
|---|
360){return w}else{v=x;return w}};w.separation=function(x){if(typeof x==="undefined"){return s}else{s=x;return w}};w.links=function(x){return m.layout.tree().links(x)};w.nodes=function(A,y){var z=r.call(w,A,y),x=[],C=0,B=0;z.forEach(function(D){var E=D.data;E.depth=D.depth;C=E.depth>C?E.depth:C;x.push(E)});x.forEach(function(D){if(!D.children){B+=1;D.depth=C}});u=q==="Circular"?v/B:u;t=0;o(x[0],C,u,null);return x};function o(B,D,A,z){var y=B.children,x=0;var C=B.dist||p;C=C>1?1:C;B.dist=C;if(z!==null){B.y0=z.y0+C*s}else{B.y0=n}if(!y){B.x0=t++*A}else{y.forEach(function(E){E.parent=B;x+=o(E,D,A,B)});B.x0=x/y.length}B.x=B.x0;B.y=B.y0;return B.x0}return w}var b=f.Visualization.extend({defaults:{layout:"Linear",separation:250,leafHeight:18,type:"phyloviz",title:"Title",scaleFactor:1,translate:[0,0],fontSize:12,selectedNode:null,nodeAttrChangedTime:0},initialize:function(n){this.set("dataset",new g.Dataset({id:n.dataset_id}))},root:{},toggle:function(n){if(typeof n==="undefined"){return}if(n.children){n._children=n.children;n.children=null}else{n.children=n._children;n._children=null}},toggleAll:function(n){if(n.children&&n.children.length!==0){n.children.forEach(this.toggleAll);toggle(n)}},getData:function(){return this.root},save:function(){var n=this.root;o(n);this.set("root",n);function o(q){delete q.parent;if(q._selected){delete q._selected}if(q.children){q.children.forEach(o)}if(q._children){q._children.forEach(o)}}var p=jQuery.extend(true,{},this.attributes);p.selectedNode=null;show_message("Saving to Galaxy","progress");return $.ajax({url:this.url(),type:"POST",dataType:"json",data:{vis_json:JSON.stringify(p)},success:function(q){var r=q.url.split("id=")[1].split("&")[0],s="/visualization?id="+r;window.history.pushState({},"",s+window.location.hash);hide_modal()}})}});var d=Backbone.View.extend({defaults:{nodeRadius:4.5},stdInit:function(o){var n=this;n.model.on("change:separation change:leafHeight change:fontSize change:nodeAttrChangedTime",n.updateAndRender,n);n.vis=o.vis;n.i=0;n.maxDepth=-1;n.width=o.width;n.height=o.height},updateAndRender:function(p){var o=m.select(".vis"),n=this;p=p||n.model.root;n.renderNodes(p);n.renderLinks(p);n.addTooltips()},renderLinks:function(n){var w=this;var o=w.diagonal;var p=w.duration;var r=w.layoutMode;var t=w.vis.selectAll("g.completeLink").data(w.tree.links(w.nodes),function(x){return x.target.id});var v=function(x){x.pos0=x.source.y0+" "+x.source.x0;x.pos1=x.source.y0+" "+x.target.x0;x.pos2=x.target.y0+" "+x.target.x0};var u=t.enter().insert("svg:g","g.node").attr("class","completeLink");u.append("svg:path").attr("class","link").attr("d",function(x){v(x);return"M "+x.pos0+" L "+x.pos1});var s=t.transition().duration(500);s.select("path.link").attr("d",function(x){v(x);return"M "+x.pos0+" L "+x.pos1+" L "+x.pos2});var q=t.exit().remove()},selectNode:function(o){var n=this;m.selectAll("g.node").classed("selectedHighlight",function(p){if(o.id===p.id){if(o._selected){delete o._selected;return false}else{o._selected=true;return true}}return false});n.model.set("selectedNode",o);$("#phyloVizSelectedNodeName").val(o.name);$("#phyloVizSelectedNodeDist").val(o.dist);$("#phyloVizSelectedNodeAnnotation").val(o.annotation||"")},addTooltips:function(){$(".bs-tooltip").remove();$(".node").attr("data-original-title",function(){var o=this.__data__,n=o.annotation||"None";return o?(o.name?o.name+" \n Use the following controls to how the chart is displayed.\n The slide controls can be moved by the mouse or, if the 'handle' is in focus, your keyboard's arrow keys.\n Move the focus between controls by using the tab or shift+tab keys on your keyboard.\n Use the 'Draw' button to render (or re-render) the chart with the current settings.\n \n Change the maximum number of data points displayable on this graph (higher values will\n load significantly slower)\n \n Size of the graphic representation of each data point\n \n Uncheck this to disable the animations used on the graph\n \n (not including graph margins and axes)\n \n (not including graph margins and axes)\n \n Use the following controls to change the data used by the chart.\n Use the 'Draw' button to render (or re-render) the chart with the current settings.\n \n These will be displayed (along with the x and y values) when you hover over\n a data point.\n By column:
":"")+"Dist: "+o.dist+"
Annotation: "+n:""}).tooltip({placement:"top",trigger:"hover"})}});var a=d.extend({initialize:function(o){var n=this;n.margins=o.margins;n.layoutMode="Linear";n.stdInit(o);n.layout();n.updateAndRender(n.model.root)},layout:function(){var n=this;n.tree=new h().layoutMode("Linear");n.diagonal=m.svg.diagonal().projection(function(o){return[o.y,o.x]})},renderNodes:function(n){var u=this,v=u.model.get("fontSize")+"px";u.tree.separation(u.model.get("separation")).leafHeight(u.model.get("leafHeight"));var q=500,o=u.tree.separation(u.model.get("separation")).nodes(u.model.root);var p=u.vis.selectAll("g.node").data(o,function(w){return w.name+w.id||(w.id=++u.i)});u.nodes=o;u.duration=q;var r=p.enter().append("svg:g").attr("class","node").on("dblclick",function(){m.event.stopPropagation()}).on("click",function(w){if(m.event.altKey){u.selectNode(w)}else{if(w.children&&w.children.length===0){return}u.model.toggle(w);u.updateAndRender(w)}});r.attr("transform",function(w){return"translate("+n.y0+","+n.x0+")"});r.append("svg:circle").attr("r",0.000001).style("fill",function(w){return w._children?"lightsteelblue":"#fff"});r.append("svg:text").attr("class","nodeLabel").attr("x",function(w){return w.children||w._children?-10:10}).attr("dy",".35em").attr("text-anchor",function(w){return w.children||w._children?"end":"start"}).style("fill-opacity",0.000001);var s=p.transition().duration(q);s.attr("transform",function(w){return"translate("+w.y+","+w.x+")"});s.select("circle").attr("r",u.defaults.nodeRadius).style("fill",function(w){return w._children?"lightsteelblue":"#fff"});s.select("text").style("fill-opacity",1).style("font-size",v).text(function(w){return w.name});var t=p.exit().transition().duration(q).remove();t.select("circle").attr("r",0.000001);t.select("text").style("fill-opacity",0.000001);o.forEach(function(w){w.x0=w.x;w.y0=w.y})}});var j=Backbone.View.extend({className:"phyloviz",initialize:function(o){var n=this;n.MIN_SCALE=0.05;n.MAX_SCALE=5;n.MAX_DISPLACEMENT=500;n.margins=[10,60,10,80];n.width=$("#PhyloViz").width();n.height=$("#PhyloViz").height();n.radius=n.width;n.data=o.data;$(window).resize(function(){n.width=$("#PhyloViz").width();n.height=$("#PhyloViz").height();n.render()});n.phyloTree=new b(o.config);n.phyloTree.root=n.data;n.zoomFunc=m.behavior.zoom().scaleExtent([n.MIN_SCALE,n.MAX_SCALE]);n.zoomFunc.translate(n.phyloTree.get("translate"));n.zoomFunc.scale(n.phyloTree.get("scaleFactor"));n.navMenu=new c(n);n.settingsMenu=new i({phyloTree:n.phyloTree});n.nodeSelectionView=new e({phyloTree:n.phyloTree});n.search=new k();setTimeout(function(){n.zoomAndPan()},1000)},render:function(){var o=this;$("#PhyloViz").empty();o.mainSVG=m.select("#PhyloViz").append("svg:svg").attr("width",o.width).attr("height",o.height).attr("pointer-events","all").call(o.zoomFunc.on("zoom",function(){o.zoomAndPan()}));o.boundingRect=o.mainSVG.append("svg:rect").attr("class","boundingRect").attr("width",o.width).attr("height",o.height).attr("stroke","black").attr("fill","white");o.vis=o.mainSVG.append("svg:g").attr("class","vis");o.layoutOptions={model:o.phyloTree,width:o.width,height:o.height,vis:o.vis,margins:o.margins};$("#title").text("Phylogenetic Tree from "+o.phyloTree.get("title")+":");var n=new a(o.layoutOptions)},zoomAndPan:function(n){var t,p;if(typeof n!=="undefined"){t=n.zoom;p=n.translate}var w=this,r=w.zoomFunc.scale(),v=w.zoomFunc.translate(),s="",u="";switch(t){case"reset":r=1;v=[0,0];break;case"+":r*=1.1;break;case"-":r*=0.9;break;default:if(typeof t==="number"){r=t}else{if(m.event!==null){r=m.event.scale}}}if(r"),au,at,ax;for(au in ar){at=ar[au];ax=$("
").appendTo(aw);$(" ").appendTo(ax).text(au);$(" ").attr("align","left").appendTo(ax).text(typeof(at)==="number"?X(at,2):at)}ah.append($("
Dataset: <%= track.name %>
Region(s): ',ag=ac.template(ah,{track:ae});var aj=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},af=function(){var al=$('select[name="regions"] option:selected').val(),an,ak=new x.GenomeRegion({chrom:view.chrom,start:view.low,end:view.high}),am=ac.map($(".bookmark"),function(ao){return new x.GenomeRegion({from_str:$(ao).children(".position").text()})});if(al==="cur"){an=[ak]}else{if(al==="bookmarks"){an=am}else{an=[ak].concat(am)}}hide_modal();window.location.href=galaxy_paths.get("sweepster_url")+"?"+$.param({dataset_id:ae.dataset_id,hda_ldda:ae.hda_ldda,regions:JSON.stringify(new Backbone.Collection(an).toJSON())})},ai=function(ak){if((ak.keyCode||ak.which)===27){aj()}else{if((ak.keyCode||ak.which)===13){af()}}};show_modal("Visualize tool parameter space and output from different parameter settings?",ag,{No:aj,Yes:af})}},r.prototype.action_icons_def[2]],can_draw:function(){if(this.dataset_id&&r.prototype.can_draw.call(this)){return true}return false},build_container_div:function(){return $("").addClass("track").attr("id","track_"+this.id).css("position","relative")},build_header_div:function(){var ae=$("");if(this.view.editor){this.drag_div=$("").addClass(this.drag_handle_class).appendTo(ae)}this.name_div=$("").addClass("track-name").appendTo(ae).text(this.name).attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase());return ae},on_resize:function(){},add_resize_handle:function(){var ae=this;var ah=false;var ag=false;var af=$(""+ah.message+"
",{Close:hide_modal})});af.tiles_div.append(ai)}}else{if(ah==="no converter"){af.container_div.addClass("error");af.tiles_div.text(J)}else{if(ah==="no data"||(ah.data!==undefined&&(ah.data===null||ah.data.length===0))){af.container_div.addClass("nodata");af.tiles_div.text(E)}else{if(ah==="pending"){af.container_div.addClass("pending");af.tiles_div.html(v);setTimeout(function(){af.init()},af.data_query_wait)}else{if(ah==="data"||ah.status==="data"){if(ah.valid_chroms){af.valid_chroms=ah.valid_chroms;af.update_icons()}af.tiles_div.text(V);if(af.view.chrom){af.tiles_div.text("");af.tiles_div.css("height",af.visible_height_px+"px");af.enabled=true;$.when(af.predraw_init()).done(function(){ae.resolve();af.container_div.removeClass("nodata error pending");af.request_draw()})}else{ae.resolve()}}}}}}});this.update_icons();return ae},predraw_init:function(){},get_drawables:function(){return this}});var M=function(ag,af,ah){g.call(this,ag,af,ah);var ae=this;m(ae.container_div,ae.drag_handle_class,".group",ae);this.filters_manager=new i.FiltersManager(this,("filters" in ah?ah.filters:null));this.data_manager.set("filters_manager",this.filters_manager);this.filters_available=false;this.tool=("tool" in ah&&ah.tool?new s(this,ah.tool,ah.tool_state):null);this.tile_cache=new x.Cache(R);if(this.header_div){this.set_filters_manager(this.filters_manager);if(this.tool){this.dynamic_tool_div=this.tool.parent_div;this.header_div.after(this.dynamic_tool_div)}}this.tiles_div=$("").addClass("tiles").appendTo(this.content_div);this.overlay_div=$("").addClass("overlay").appendTo(this.content_div);if(ah.mode){this.change_mode(ah.mode)}};q(M.prototype,r.prototype,g.prototype,{action_icons_def:g.prototype.action_icons_def.concat([{name:"show_more_rows_icon",title:"To minimize track height, not all feature rows are displayed. Click to display more rows.",css_class:"exclamation",on_click_fn:function(ae){$(".bs-tooltip").remove();ae.slotters[ae.view.resolution_px_b].max_rows*=2;ae.request_draw(true)},hide:true}]),copy:function(ae){var af=this.to_dict();q(af,{data_manager:this.data_manager});var ag=new this.constructor(this.view,ae,af);ag.change_mode(this.mode);ag.enabled=this.enabled;return ag},set_filters_manager:function(ae){this.filters_manager=ae;this.header_div.after(this.filters_manager.parent_div)},to_dict:function(){return{track_type:this.get_type(),name:this.name,hda_ldda:this.hda_ldda,dataset_id:this.dataset_id,prefs:this.prefs,mode:this.mode,filters:this.filters_manager.to_dict(),tool_state:(this.tool?this.tool.state_dict():{})}},change_mode:function(af){var ae=this;ae.mode=af;ae.config.values.mode=af;ae.tile_cache.clear();ae.request_draw();this.action_icons.mode_icon.attr("title","Set display mode (now: "+ae.mode+")");return ae},update_icons:function(){var ae=this;if(ae.filters_available){ae.action_icons.filters_icon.show()}else{ae.action_icons.filters_icon.hide()}if(ae.tool){ae.action_icons.tools_icon.show();ae.action_icons.param_space_viz_icon.show()}else{ae.action_icons.tools_icon.hide();ae.action_icons.param_space_viz_icon.hide()}},_gen_tile_cache_key:function(af,ag,ae){return af+"_"+ag+"_"+ae},request_draw:function(af,ae){this.view.request_redraw(false,af,ae,this)},before_draw:function(){},_draw:function(af,ap){if(!this.can_draw()){return}var an=this.view.low,aj=this.view.high,al=aj-an,ag=this.view.container.width(),ar=this.view.resolution_px_b,ai=this.view.resolution_b_px;if(this.is_overview){an=this.view.max_low;aj=this.view.max_high;ai=(view.max_high-view.max_low)/ag;ar=1/ai}this.before_draw();this.tiles_div.children().addClass("remove");var ae=Math.floor(an/(ai*T)),am=true,aq=[],ak=function(at){return(at&&"track" in at)};while((ae*T*ai)"),au,at,ax;for(au in ar){at=ar[au];ax=$("
").appendTo(aw);$(" ").appendTo(ax).text(au);$(" ").attr("align","left").appendTo(ax).text(typeof(at)==="number"?X(at,2):at)}ah.append($("
Dataset: <%= track.name %>
Region(s): ',ag=ac.template(ah,{track:ae});var aj=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},af=function(){var al=$('select[name="regions"] option:selected').val(),an,ak=new x.GenomeRegion({chrom:view.chrom,start:view.low,end:view.high}),am=ac.map($(".bookmark"),function(ao){return new x.GenomeRegion({from_str:$(ao).children(".position").text()})});if(al==="cur"){an=[ak]}else{if(al==="bookmarks"){an=am}else{an=[ak].concat(am)}}hide_modal();window.location.href=galaxy_paths.get("sweepster_url")+"?"+$.param({dataset_id:ae.dataset_id,hda_ldda:ae.hda_ldda,regions:JSON.stringify(new Backbone.Collection(an).toJSON())})},ai=function(ak){if((ak.keyCode||ak.which)===27){aj()}else{if((ak.keyCode||ak.which)===13){af()}}};show_modal("Visualize tool parameter space and output from different parameter settings?",ag,{No:aj,Yes:af})}},r.prototype.action_icons_def[2]],can_draw:function(){if(this.dataset_id&&r.prototype.can_draw.call(this)){return true}return false},build_container_div:function(){return $("").addClass("track").attr("id","track_"+this.id).css("position","relative")},build_header_div:function(){var ae=$("");if(this.view.editor){this.drag_div=$("").addClass(this.drag_handle_class).appendTo(ae)}this.name_div=$("").addClass("track-name").appendTo(ae).text(this.name).attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase());return ae},on_resize:function(){},add_resize_handle:function(){var ae=this;var ah=false;var ag=false;var af=$(""+ah.message+"
",{Close:hide_modal})});af.tiles_div.append(ai)}}else{if(ah==="no converter"){af.container_div.addClass("error");af.tiles_div.text(J)}else{if(ah==="no data"||(ah.data!==undefined&&(ah.data===null||ah.data.length===0))){af.container_div.addClass("nodata");af.tiles_div.text(E)}else{if(ah==="pending"){af.container_div.addClass("pending");af.tiles_div.html(v);setTimeout(function(){af.init()},af.data_query_wait)}else{if(ah==="data"||ah.status==="data"){if(ah.valid_chroms){af.valid_chroms=ah.valid_chroms;af.update_icons()}af.tiles_div.text(V);if(af.view.chrom){af.tiles_div.text("");af.tiles_div.css("height",af.visible_height_px+"px");af.enabled=true;$.when(af.predraw_init()).done(function(){ae.resolve();af.container_div.removeClass("nodata error pending");af.request_draw()})}else{ae.resolve()}}}}}}});this.update_icons();return ae},predraw_init:function(){},get_drawables:function(){return this}});var M=function(ag,af,ah){g.call(this,ag,af,ah);var ae=this;m(ae.container_div,ae.drag_handle_class,".group",ae);this.filters_manager=new i.FiltersManager(this,("filters" in ah?ah.filters:null));this.data_manager.set("filters_manager",this.filters_manager);this.filters_available=false;this.tool=("tool" in ah&&ah.tool?new s(this,ah.tool,ah.tool_state):null);this.tile_cache=new x.Cache(R);if(this.header_div){this.set_filters_manager(this.filters_manager);if(this.tool){this.dynamic_tool_div=this.tool.parent_div;this.header_div.after(this.dynamic_tool_div)}}this.tiles_div=$("").addClass("tiles").appendTo(this.content_div);this.overlay_div=$("").addClass("overlay").appendTo(this.content_div);if(ah.mode){this.change_mode(ah.mode)}};q(M.prototype,r.prototype,g.prototype,{action_icons_def:g.prototype.action_icons_def.concat([{name:"show_more_rows_icon",title:"To minimize track height, not all feature rows are displayed. Click to display more rows.",css_class:"exclamation",on_click_fn:function(ae){$(".bs-tooltip").remove();ae.slotters[ae.view.resolution_px_b].max_rows*=2;ae.request_draw(true)},hide:true}]),copy:function(ae){var af=this.to_dict();q(af,{data_manager:this.data_manager});var ag=new this.constructor(this.view,ae,af);ag.change_mode(this.mode);ag.enabled=this.enabled;return ag},set_filters_manager:function(ae){this.filters_manager=ae;this.header_div.after(this.filters_manager.parent_div)},to_dict:function(){return{track_type:this.get_type(),name:this.name,hda_ldda:this.hda_ldda,dataset_id:this.dataset_id,prefs:this.prefs,mode:this.mode,filters:this.filters_manager.to_dict(),tool_state:(this.tool?this.tool.state_dict():{})}},change_mode:function(af){var ae=this;ae.mode=af;ae.config.values.mode=af;ae.tile_cache.clear();ae.request_draw();this.action_icons.mode_icon.attr("title","Set display mode (now: "+ae.mode+")");return ae},update_icons:function(){var ae=this;if(ae.filters_available){ae.action_icons.filters_icon.show()}else{ae.action_icons.filters_icon.hide()}if(ae.tool){ae.action_icons.tools_icon.show();ae.action_icons.param_space_viz_icon.show()}else{ae.action_icons.tools_icon.hide();ae.action_icons.param_space_viz_icon.hide()}},_gen_tile_cache_key:function(af,ag,ae){return af+"_"+ag+"_"+ae},request_draw:function(af,ae){this.view.request_redraw(false,af,ae,this)},before_draw:function(){},_draw:function(af,ap){if(!this.can_draw()){return}var an=this.view.low,aj=this.view.high,al=aj-an,ag=this.view.container.width(),ar=this.view.resolution_px_b,ai=this.view.resolution_b_px;if(this.is_overview){an=this.view.max_low;aj=this.view.max_high;ai=(view.max_high-view.max_low)/ag;ar=1/ai}this.before_draw();this.tiles_div.children().addClass("remove");var ae=Math.floor(an/(ai*T)),am=true,aq=[],ak=function(at){return(at&&"track" in at)};while((ae*T*ai)\n
\n\n";
+ buffer += "\n\n \n
\n \n ";
+ return buffer;}
+
+ buffer += "";
+ foundHelper = helpers.name;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.name; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + " ";
+ foundHelper = helpers.xval;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.xval; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + " ";
+ foundHelper = helpers.yval;
+ if (foundHelper) { stack1 = foundHelper.call(depth0, {hash:{}}); }
+ else { stack1 = depth0.yval; stack1 = typeof stack1 === functionType ? stack1() : stack1; }
+ buffer += escapeExpression(stack1) + " \n
";
+ return buffer;});
+})();
\ No newline at end of file
diff --git a/static/scripts/templates/visualization-templates.html b/static/scripts/templates/visualization-templates.html
index 4b27b515f71..37d0a3ed077 100644
--- a/static/scripts/templates/visualization-templates.html
+++ b/static/scripts/templates/visualization-templates.html
@@ -1,33 +1,155 @@
+
+
+
+
\ No newline at end of file
diff --git a/static/scripts/viz/scatterplot.js b/static/scripts/viz/scatterplot.js
index 16af45647d3..a87f8d3a8e9 100644
--- a/static/scripts/viz/scatterplot.js
+++ b/static/scripts/viz/scatterplot.js
@@ -1,54 +1,66 @@
define([
- "../libs/underscore",
- "../libs/d3",
- "../mvc/base-mvc",
- "../templates/compiled/template-visualization-scatterplotControlForm"
+ //"../libs/jquery/jquery",
+ "../libs/underscore",
+ "../mvc/base-mvc",
+ "../templates/compiled/template-visualization-scatterplotControlForm",
+ "../templates/compiled/template-visualization-statsTable",
+ "../templates/compiled/template-visualization-chartSettings",
+
+ "../libs/d3",
+
+ "../libs/bootstrap",
+ "../libs/jquery/jquery-ui-1.8.23.custom.min"
], function(){
+
+
+
/* =============================================================================
todo:
outside this:
BUG: visualization menu doesn't disappear
- BUG?: get column_names (from datatype if necessary)
+ BUG?: get metadata_column_names (from datatype if necessary)
BUG: single vis in popupmenu should have tooltip with that name NOT 'Visualizations'
- ??: maybe better to do this with a canvas...
+ wire label setters, anim setter
- move renderScatter plot to obj, possibly view?
-
- fix x axis - adjust ticks when tick labels are long - move odds down and extend tick line
-
- provide callback in view to load data incrementally - for large sets
-
- paginate
- handle rerender
- use endpoint (here and on the server (fileptr))
- fetch (new?) data
- handle rerender
+ TwoVarScatterplot:
+ ??: maybe better to do this with a canvas...
+ save as visualization
+ to seperate file?
+ remove underscore dependencies
+ add interface to change values (seperate)?
+ download svg -> base64 encode
+ incorporate glyphs, glyph state renderers
- selectable list of preset column comparisons (rnaseq etc.)
- multiple plots on one page (small multiples)
+ ScatterplotSettingsForm:
+ some css bug that lowers the width of settings form when plot-controls tab is open
+ causes chart to shift
+ what can be abstracted/reused for other graphs?
+ avoid direct manipulation of this.plot
+ allow option to put plot into seperate tab of interface (for small multiples)
- where are bad dataprovider params handled? api/datasets?
+ provide callback in view to load data incrementally - for large sets
+ paginate
+ handle rerender
+ use endpoint (here and on the server (fileptr))
+ fetch (new?) data
+ handle rerender
+ use d3.TSV?
+ render warning on long data (> maxDataPoints)
+ adjust endpoint
- config changes to the graph
- render stats on the data (max, min, count)
- render warning on long data (> maxDataPoints)
- adjust endpoint
-
- loading indicator
-
- download svg -> base64 encode
- dress up ui
-
- incorporate glyphs, glyph state renderers
-
- validate columns selection (here or server)
-
- ?? ensure svg styles thru d3 or css?
- d3: configable (easily)
- css: standard - better maintenance
- ? override at config
+ selectable list of preset column comparisons (rnaseq etc.)
+ how to know what sort of Tabular the data is?
+ smarter about headers
+ validate columns selection (here or server)
+
+ Scatterplot.mako:
+ multiple plots on one page (small multiples)
+ ?? ensure svg styles thru d3 or css?
+ d3: configable (easily)
+ css: standard - better maintenance
+ ? override at config
============================================================================= */
/**
@@ -61,8 +73,7 @@ todo:
* depends on: d3, underscore
*/
function TwoVarScatterplot( config ){
- var plot = this,
- TICK_LINE_AND_PADDING = 10,
+ var TICK_LINE_AND_PADDING = 10,
GUESS_AT_SVG_CHAR_WIDTH = 7,
GUESS_AT_SVG_CHAR_HEIGHT = 10,
PADDING = 8,
@@ -73,7 +84,7 @@ function TwoVarScatterplot( config ){
if( this.debugging && console && console.debug ){
var args = Array.prototype.slice.call( arguments );
args.unshift( this.toString() );
- console.debug.apply( null, args );
+ console.debug.apply( console, args );
}
};
this.log( 'new TwoVarScatterplot:', config );
@@ -84,10 +95,10 @@ function TwoVarScatterplot( config ){
this.defaults = {
id : 'TwoVarScatterplot',
containerSelector : 'body',
- maxDataPoints : 30000,
- bubbleRadius : 4,
+ maxDataPoints : 10000,
+ datapointSize : 4,
entryAnimDuration : 500,
- //TODO: no effect?
+ //TODO: variable effect (not always exactly # of ticks set to)
xNumTicks : 10,
yNumTicks : 10,
xAxisLabelBumpY : 40,
@@ -99,10 +110,12 @@ function TwoVarScatterplot( config ){
marginRight : 50,
marginBottom : 50,
marginLeft : 50,
+
xMin : null,
xMax : null,
yMin : null,
yMax : null,
+
xLabel : "X",
yLabel : "Y"
};
@@ -126,8 +139,9 @@ function TwoVarScatterplot( config ){
// ........................................................ initial element creation
//NOTE: called on new
this.svg = d3.select( this.config.containerSelector )
- .append( "svg:svg" ).attr( "class", "chart" ).style( 'display', 'none' );
- this.content = this.svg.append( "svg:g" ).attr( "class", "content" );
+ .append( "svg:svg" ).attr( "class", "chart" );
+
+ this.content = this.svg.append( "svg:g" ).attr( "class", "content" ).attr( 'id', this.config.id );
this.xAxis = this.content.append( 'g' ).attr( 'class', 'axis' ).attr( 'id', 'x-axis' );
this.xAxisLabel = this.xAxis.append( 'text' ).attr( 'class', 'axis-label' ).attr( 'id', 'x-axis-label' );
@@ -157,7 +171,9 @@ function TwoVarScatterplot( config ){
};
// ........................................................ data and scales
- this.preprocessData = function( data ){
+ this.preprocessData = function( data, min, max ){
+ //TODO: filter by min, max if set
+
// set a cap on the data, limit to first n points
return ( data.length > this.config.maxDataPoints )? ( data.slice( 0, this.config.maxDataPoints ) ): ( data );
};
@@ -313,63 +329,110 @@ function TwoVarScatterplot( config ){
};
- this.renderDatapoints = function( xCol, yCol ){
+ this.renderDatapoints = function( xCol, yCol, ids ){
+ // initial render, complete re-render (REPLACE datapoints)
- var xPosFn = function( d, i ){
- return plot.xScale( xCol[ i ] );
- };
- var yPosFn = function( d, i ){
- return plot.yScale( yCol[ i ] );
- };
-
- // select all existing glyphs and compare to incoming data
- // enter() will yield those glyphs that need to be added
- // exit() will yield existing glyphs that need to be removed
- this.datapoints = this.content.selectAll( ".glyph" )
- .data( xCol );
-
- // enter - new data to be added as glyphs: give them a 'entry' position and style
- this.datapoints.enter()
- .append( "svg:circle" ).attr( "class", "glyph" )
- // start all bubbles at corner...
- .attr( "cx", xPosFn )
- .attr( "cy", 0 )
- .attr( "r", 0 );
-
- // for all existing glyphs and those that need to be added: transition anim to final state
- this.datapoints
- // ...animate to final position
- .transition().duration( this.config.entryAnimDuration )
- .attr( "cx", xPosFn )
- .attr( "cy", yPosFn )
- .attr( "r", this.config.bubbleRadius );
+ this.datapoints = this.addDatapoints( xCol, yCol, ids, ".glyph" );
// glyphs that need to be removed: transition to from normal state to 'exit' state, remove from DOM
this.datapoints.exit()
.transition().duration( this.config.entryAnimDuration )
.attr( "cy", this.config.height )
.attr( "r", 0 )
- .style( "fill-opacity", 0 )
.remove();
- //this.log( this.datapoints, 'glyphs rendered' );
+ this.log( this.datapoints, 'glyphs rendered' );
+ };
+
+ this.addDatapoints = function( newXCol, newYCol, ids, selectorForExisting ){
+ // ADD datapoints to plot that's already rendered
+ // if selectorForExisting === undefined (as in not passed), addDatapoints won't update existing
+ // pass in the class ( '.glyph' ) to update exising datapoints
+ var plot = this,
+ xPosFn = function( d, i ){
+ return plot.xScale( newXCol[ i ] );
+ },
+ yPosFn = function( d, i ){
+ return plot.yScale( newYCol[ i ] );
+ };
+
+ // select all existing glyphs and compare to incoming data
+ // enter() will yield those glyphs that need to be added
+ var newDatapoints = this.content.selectAll( selectorForExisting ).data( newXCol );
+
+ // enter - new data to be added as glyphs: give them a 'entry' position and style
+ newDatapoints.enter()
+ .append( "svg:circle" )
+ .classed( "glyph", true )
+ // start all bubbles small...
+ .attr( "cx", xPosFn )
+ .attr( "cy", yPosFn )
+ .attr( "r", 0 );
+
+ // for all existing glyphs and those that need to be added: transition anim to final state
+ newDatapoints
+ // ...animate to final position
+ .transition().duration( this.config.entryAnimDuration )
+ .attr( "r", this.config.datapointSize );
+
+ // attach ids
+ if( ids ){
+ newDatapoints.attr( 'data', function( d, i ){ return ( ids[ i ] ); } );
+ }
+
+ // titles
+ newDatapoints.attr( 'title', function( d, i ){
+ return (( ids )?( ids[ i ] + ': ' ):( '' )) + newXCol[ i ] + ', ' + newYCol[ i ];
+ });
+
+ // events
+ newDatapoints
+ //TODO: remove magic numbers
+ .on( 'mouseover', function(){
+ var datapoint = d3.select( this );
+ datapoint
+ .style( 'fill', 'red' )
+ .style( 'fill-opacity', 1 );
+
+ // create horiz, vert lines to axis
+ plot.content.append( 'line' )
+ .attr( 'stroke', 'red' )
+ .attr( 'stroke-width', 1 )
+ .attr( 'x1', datapoint.attr( 'cx' ) ).attr( 'y1', datapoint.attr( 'cy' ) )
+ .attr( 'x2', 0 ).attr( 'y2', datapoint.attr( 'cy' ) )
+ .classed( 'hoverline', true );
+ plot.content.append( 'line' )
+ .attr( 'stroke', 'red' )
+ .attr( 'stroke-width', 1 )
+ .attr( 'x1', datapoint.attr( 'cx' ) ).attr( 'y1', datapoint.attr( 'cy' ) )
+ .attr( 'x2', datapoint.attr( 'cx' ) ).attr( 'y2', plot.config.height )
+ .classed( 'hoverline', true );
+ })
+ .on( 'mouseout', function(){
+ d3.select( this )
+ .style( 'fill', 'black' )
+ .style( 'fill-opacity', 0.2 );
+
+ d3.selectAll( '.hoverline' ).remove();
+ });
+
+ return newDatapoints;
};
this.render = function( columnData, meta ){
//pre: columns passed are numeric
//pre: at least two columns are passed
- //assume: first column is x, second column is y, any remaining aren't used
+ //assume: first column is x, second column is y, any remaining aren't used
var xCol = columnData[0],
- yCol = columnData[1];
+ yCol = columnData[1],
+ ids = ( columnData.length > 2 )?( columnData[2] ):( undefined );
this.log( 'renderScatterplot', xCol.length, yCol.length, this.config );
//pre: xCol.len == yCol.len
- //TODO: ^^ isn't necessarily true with current ColumnDataProvider
xCol = this.preprocessData( xCol );
yCol = this.preprocessData( yCol );
//this.log( 'xCol len', xCol.length, 'yCol len', yCol.length );
- //TODO: compute min, max on server.
this.setUpDomains( xCol, yCol, meta );
this.log( 'xMin, xMax, yMin, yMax:', this.xMin, this.xMax, this.yMin, this.yMax );
@@ -380,28 +443,31 @@ function TwoVarScatterplot( config ){
this.setUpYAxis();
this.renderGrid();
- this.renderDatapoints( xCol, yCol );
- //TODO: on hover red line to axes, display values
+ this.renderDatapoints( xCol, yCol, ids );
};
}
//==============================================================================
/**
* Scatterplot control UI as a backbone view
- *
+ * handles:
+ * getting the desired data
+ * configuring the plot display
*/
var ScatterplotControlForm = BaseView.extend( LoggableMixin ).extend({
//logger : console,
tagName : 'form',
className : 'scatterplot-settings-form',
- loadingIndicatorImagePath : ( galaxy_paths.get( 'image_path' ) + '/loading_large_white_bg.gif' ),
+ loadingIndicatorImage : 'loading_large_white_bg.gif',
events : {
- 'click #render-button' : 'renderScatterplot'
+ 'click #render-button' : 'renderPlot',
+ 'click #include-id-checkbox' : 'toggleThirdColumnSelector'
},
initialize : function( attributes ){
+
if( !attributes || !attributes.dataset ){
throw( "ScatterplotView requires a dataset" );
} else {
@@ -414,106 +480,230 @@ var ScatterplotControlForm = BaseView.extend( LoggableMixin ).extend({
// set up the basic chart infrastructure with config (if any)
this.chartConfig = attributes.chartConfig || {};
- this.log( 'this.chartConfig:', this.chartConfig );
+ this.log( 'initial chartConfig:', this.chartConfig );
this.plot = new TwoVarScatterplot( this.chartConfig );
+ this.chartConfig = this.plot.config;
+
+ this.$statsPanel = null;
+ this.$chartSettingsPanel = null;
+ this.$dataSettingsPanel = null;
+ this.dataFetch = null;
},
render : function(){
var view = this,
- html = '';
-
- // build column select controls for each x, y (based on name if available)
- var formData = {
- loadingIndicatorImagePath : this.loadingIndicatorImagePath,
- config : this.chartConfig,
- availableColumns : []
- };
- _.each( this.dataset.metadata_column_types.split( ', ' ), function( type, index ){
- // use only numeric columns
- if( type === 'int' || type === 'float' ){
- //TODO: using 0-based indeces
- var name = 'column ' + index;
- // label with the name if available
- if( view.dataset.metadata_column_names ){
- name = view.dataset.metadata_column_names[ index ];
- }
- formData.availableColumns.push({ index: index, name: name });
- }
- });
- //TODO: other vals: max_vals, start_val, pagination
+ formData = {
+ config : this.chartConfig,
+ allColumns : [],
+ numericColumns : [],
+ loadingIndicatorImagePath : galaxy_paths.get( 'image_path' ) + '/' + this.loadingIndicatorImage
+ };
- html = ScatterplotControlForm.templates.form( formData );
- this.$el.append( html );
+ // gather column indeces (from metadata_column_types) and names (from metadata_columnnames)
+ _.each( this.dataset.metadata_column_types.split( ', ' ), function( type, index ){
+ //TODO: using 0-based indeces
+ // label with the name if available (fall back on 'column X Y \n ";
+ stack1 = depth0.stats;
+ stack1 = helpers.each.call(depth0, stack1, {hash:{},inverse:self.noop,fn:self.program(1, program1, data)});
+ if(stack1 || stack1 === 0) { buffer += stack1; }
+ buffer += "\n