Got it working by obliterating tooltips. It still shows node names on mouseover, but the giant tooltip is gone. I had to remove the mouseover effect being called from the d3 library. I also changed the CSS, but I don't know if that's germane to the problem.
Javascript:
// Force Directed Graphs!
// these require an input of (at least) 3 fields in the format
// 'stats count by field1 field2 field3'
// ---- settings ----
// height, width
// panAndZoom: the ability to zoom (true, false)
// directional: true, false
// valueField: what field to count by
// charges, gravity: change the look of the graph, play around with these!
// linkDistance: the distance between each node
// ---- expected data format ----
// a splunk search like this: source=*somedata* | stats count by artist_name track_name device
// each group is an artist/song pairing
// {
// "nodes":[
// {
// "source":"Bruno Mars",
// "group":0
// },
// {
// "source":"It Will Rain",
// "group":0
// },
// {
// "source":"Cobra Starship",
// "group":1
// },
// {
// "source":"You Make Me Feel",
// "group":1
// },
// {
// "source":"Gym Class Heroes",
// "group":2
// },
// {
// "source":"Stereo Hearts",
// "group":2
// },
// ],
// "links":[
// {
// "source":0,
// "target":1,
// "value":null
// },
// {
// "source":2,
// "target":3,
// "value":null
// },
// {
// "source":4,
// "target":5,
// "value":null
// },
// ],
// - we add this part -
// "groupNames":{
// "iphone":49,
// "android":53,
// "blackberry":48,
// "ipad":52,
// "ipod":50
// },
// "groupLookup":[
// "iphone",
// "android",
// "blackberry",
// "ipad",
// "ipod"
// ]
// }
define(function(require, exports, module) {
var _ = require('underscore');
var d3 = require("../d3/d3");
var SimpleSplunkView = require("splunkjs/mvc/simplesplunkview");
require("css!./forcedirected.css");
var ForceDirected = SimpleSplunkView.extend({
moduleId: module.id,
className: "splunk-toolkit-force-directed",
options: {
managerid: null,
data: 'preview',
panAndZoom: true,
directional: true,
valueField: 'count',
charges: -500,
gravity: 0.2,
linkDistance: 15,
swoop: false,
isStatic: true
},
output_mode: "json_rows",
initialize: function() {
SimpleSplunkView.prototype.initialize.apply(this, arguments);
// in the case that any options are changed, it will dynamically update
// without having to refresh.
this.settings.on("change:charges", this.render, this);
this.settings.on("change:gravity", this.render, this);
this.settings.on("change:linkDistance", this.render, this);
this.settings.on("change:directional", this.render, this);
this.settings.on("change:panAndZoom", this.render, this);
this.settings.on("change:swoop", this.render, this);
this.settings.on("change:isStatic", this.render, this);
},
createView: function() {
var margin = {top: 10, right: 10, bottom: 10, left: 10};
var availableWidth = parseInt(this.settings.get("width") || this.$el.width(), 10);
var availableHeight = parseInt(this.settings.get("height") || this.$el.height(), 10);
this.$el.html("");
var svg = d3.select(this.el)
.append("svg")
.attr("width", availableWidth)
.attr("height", availableHeight)
.attr("pointer-events", "all");
return { container: this.$el, svg: svg, margin: margin };
},
// making the data look how we want it to for updateView to do its job
formatData: function(data) {
var nodes = {};
var links = [];
data.forEach(function(link) {
var sourceName = link[0];
var targetName = link[1];
var groupName = link[2];
var newLink = {};
newLink.source = nodes[sourceName] ||
(nodes[sourceName] = {name: sourceName, group: groupName, value: 0});
newLink.target = nodes[targetName] ||
(nodes[targetName] = {name: targetName, group: groupName, value: 0});
newLink.value = +link[3];
newLink.source.value += newLink.value;
newLink.target.value += newLink.value;
links.push(newLink);
});
return {nodes: d3.values(nodes), links: links};
},
updateView: function(viz, data) {
var that = this;
var containerHeight = this.$el.height();
var containerWidth = this.$el.width();
// Clear svg
var svg = $(viz.svg[0]);
svg.empty();
svg.height(containerHeight);
svg.width(containerWidth);
// Add the graph group as a child of the main svg
var graphWidth = containerWidth - viz.margin.left - viz.margin.right;
var graphHeight = containerHeight - viz.margin.top - viz.margin.bottom;
var graph = viz.svg
.append("g")
.attr("width", graphWidth)
.attr("height", graphHeight)
.attr("transform", "translate(" + viz.margin.left + "," + viz.margin.top + ")");
// Get settings
this.charge = this.settings.get('charges');
this.gravity = this.settings.get('gravity');
this.linkDistance = this.settings.get('linkDistance');
this.zoomable = this.settings.get('panAndZoom');
this.swoop = this.settings.get('swoop');
this.isStatic = this.settings.get('isStatic');
this.isDirectional = this.settings.get('directional');
this.zoomFactor = 0.5;
this.groupNameLookup = data.groupLookup;
// Set up graph
var r = 6;
var height = graphHeight;
var width = graphWidth;
var force = d3.layout.force()
.gravity(this.gravity)
.charge(this.charge)
.linkDistance(this.linkDistance)
.size([width, height]);
this.color = d3.scale.category20();
if (this.zoomable) {
initPanZoom.call(this, viz.svg);
}
graph.style("opacity", 1e-6)
.transition()
.duration(1000)
.style("opacity", 1);
graph.append("svg:defs").selectAll("marker")
.data(["arrowEnd"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 0)
.attr("refY", 0)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("markerUnits", "userSpaceOnUse")
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
var link = graph.selectAll("line.link")
.data(data.links)
.enter().append('path')
.attr("class", "link")
.attr("marker-end", function(d) {
if (that.isDirectional) {
return "url(#" + "arrowEnd" + ")";
}
})
.style("stroke-width", function(d) {
var num = Math.max(Math.round(Math.log(d.value)), 1);
return _.isNaN(num) ? 1 : num;
});
link
.on('click', function(d) {
that.trigger('click:link', {
source: d.source.name,
sourceGroup: d.source.group,
target: d.target.name,
targetGroup: d.target.group,
value: d.value
});
});
var node = graph.selectAll("circle.node")
.data(data.nodes)
.enter().append("svg:circle")
.attr("class", "node")
.attr("r", r - 1)
.style("fill", function(d) {
return that.color(d.group);
})
.call(force.drag);
node.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) {
return d.name || "";
});
//var labels = node.append("text")
// .text(function(d) { return d.name; });
node.append("title")
.text(function(d) { return d.name; });
node
.on('click', function(d) {
that.trigger('click:node', {
name: d.name,
group: d.group,
value: d.value
});
});
force.nodes(data.nodes)
.links(data.links)
.on("tick", function() {
link.attr("d", function(d) {
var diffX = d.target.x - d.source.x;
var diffY = d.target.y - d.source.y;
// Length of path from center of source node to center of target node
var pathLength = Math.sqrt((diffX * diffX) + (diffY * diffY));
// x and y distances from center to outside edge of target node
var offsetX = (diffX * (r * 2)) / pathLength;
var offsetY = (diffY * (r * 2)) / pathLength;
if (!that.swoop) {
pathLength = 0;
}
return "M" + d.source.x + "," + d.source.y + "A" + pathLength + "," + pathLength + " 0 0,1 " + (d.target.x - offsetX) + "," + (d.target.y - offsetY);
});
node.attr("cx", function(d) {
d.x = Math.max(r, Math.min(width - r, d.x));
return d.x;
})
.attr("cy", function(d) {
d.y = Math.max(r, Math.min(height - r, d.y));
return d.y;
});
}).start();
if (this.isStatic) {
forwardAlpha(force, 0.005, 1000);
}
function forwardAlpha(layout, alpha, max) {
alpha = alpha || 0;
max = max || 1000;
var i = 0;
while (layout.alpha() > alpha && i++ < max) {
layout.tick();
}
}
// draggin'
function initPanZoom(svg) {
var that = this;
svg.on('mousedown.drag', function() {
if (that.zoomable) {
svg.classed('panCursor', true);
}
// console.log('drag start');
});
svg.on('mouseup.drag', function() {
svg.classed('panCursor', false);
// console.log('drag stop');
});
svg.call(d3.behavior.zoom().on("zoom", function() {
panZoom();
}));
}
// zoomin'
function panZoom() {
graph.attr("transform",
"translate(" + d3.event.translate + ")"
+ " scale(" + d3.event.scale + ")");
}
//TODO: This doesn't seem to be used in this file
function getSafeVal(getobj, name) {
var retVal;
if (getobj.hasOwnProperty(name) && getobj[name] !== null) {
retVal = getobj[name];
} else {
retVal = name;
}
return retVal;
}
function highlightNodes(val) {
var self = this, groupName;
if (val !== ' ' && val !== '') {
graph.selectAll('circle')
.filter(function(d, i) {
groupName = self.groupNameLookup[d.group];
if (d.source.indexOf(val) >= 0 || groupName.indexOf(val) >= 0) {
d3.select(this).classed('highlight', true);
} else {
d3.select(this).classed('highlight', false);
}
});
} else {
graph.selectAll('circle').classed('highlight', false);
}
}
}
});
return ForceDirected;
});
CSS
.splunk-toolkit-force-directed {
overflow: hidden;
font-family: arial;
}
.splunk-toolkit-force-directed circle.node {
stroke: #fff;
stroke-width: 1.5px;
}
.splunk-toolkit-force-directed .link, .splunk-toolkit-force-directed #arrowEnd {
stroke: #999;
stroke-opacity: .6;
fill: none;
}
.splunk-toolkit-force-directed #arrowEnd {
fill: #999;
}
.splunk-toolkit-force-directed circle.node {
stroke: #fff;
stroke-width: 1.5px;
}
.splunk-toolkit-force-directed circle.nodeHighlight,
.splunk-toolkit-force-directed circle.highlight {
stroke-width: 2px;
stroke: #E89595;
}
.linkHighlight {
stroke: red !important;
}
.splunk-toolkit-force-directed circle.nodeHighlight.highlight {
stroke-width: 3px;
}
.splunk-toolkit-force-directed line.link {
stroke: #999;
stroke-opacity: .6;
}
.splunk-toolkit-force-directed #chart {
width: 100%;
height: 100%;
}
.splunk-toolkit-force-directed .group-swatch {
width:20px;
height:20px;
float:left;
margin:2px;
margin-right: 10px
}
.splunk-toolkit-force-directed .group-name {
padding-top: 5px;
}
.splunk-toolkit-force-directed .panCursor {
cursor: move;
}
... View more