I'm trying to use Susie Lu's legend plugin.
Here is a plnk of work so far;
http://plnkr.co/edit/wrOWPYu4PAqwr8f5OOjw?p=preview
Image below is what I would like to achieve (just layout, content/colour doesn't matter)
I've tried the standard float:left + display: inline on the classes attached to the text/rectangles but it didn't work for me. Maybe I was making a mistake. I'm not sure If I should be doing this inside the d3 script or in the css file anyway?
Hope this is a simple fix - any help is much appreciated!
Thanks
Those attributes (float:left + display: inline) only work on html elements, what the legend library here produces is svg
It's possible, but needs a little bit of work
http://plnkr.co/edit/YRFRWEtMFDmje06Mg5KY?p=preview
var cells= d3.selectAll(".cell");
var cellGap = legendLinear.shapeWidth()+(legendLinear.shapePadding()/4);
cells.select("text")
.attr ("transform", "translate("+cellGap+" 13)")
.style ("text-anchor", "start")
;
var offset = 0;
cells.each (function(d,i) {
var d3sel = d3.select(this);
var textWidth = d3sel.select("text").node().getComputedTextLength();
var offsetInc = textWidth + legendLinear.shapeWidth() + legendLinear.shapePadding();
d3sel.attr("transform", "translate("+offset+" 0)");
offset += offsetInc;
});
This firstly moves the text element for each legend 'cell' to the right of the colour swatch rather than below.
Then it moves the g elements that hold the label and swatch horizontally apart dependent on the width of the label (and the swatch and the declared padding) using .getComputedTextLength() and a running total for the offset.
Related
I have this group element and a text node inside it:
var legend = svg_container.append('g');
var legend_text = legend.append('text').attr('y', 0)
//other elements inside 'g'
This is just part of the code. In actuality, I have other child nodes inside the group element created on the fly after the text node(these other elements decide the width of the group element).
How do I position the text node centrally inside the group element?
To center it, you can use the text attribute text-anchor="middle" with the x and y attributes for positioning:
legend_text.attr("text-anchor","middle");
legend_text.attr("x", 0);
legend_text.attr("y", 0);
Here, your text will be fixed to the center origin of your group legend.
In fact, it won't be perfectly centered, to do so, you should set y to the half of the font-size attribute of your text.
Hoping I've correctly understood your problem !
EDIT: With your indication, I see two solutions :
My favorite one would include a modification of the legend group, by adding him a transform="translate(x,y)" attribute,
where the x and y are the center of your svg element. This one may not work with the rest of your code, but I find that doing so in general is a clean way to have multiple drawings living together in a single svg.
The other one is simplier by far, and I think will answer to your problem for now :
You replace the x and y values by the center of your legend group. The little problem here is the calculation of thoses values, because you can't read them directly from the element.
In fact, a g element doesn't have width or height attributes, it's only a container for applying attributes for a group of svg elements. That's why I recommend using my first solution.
Another way of centering it in the group using bbox
//make a g
var svg = d3.select("body").append("svg").attr("width", "900")
.attr("height", "900").append("g");
//add circle to it
svg.append("circle").attr("cx", 50).attr("cy", 50).attr("r", 30);
svg.append("circle").attr("cx", 250).attr("cy", 150).attr("r", 30);
//get bbox
var bb = svg.node().getBBox();
var centery = bb.y + bb.height/2;
var centerx = bb.x + bb.width/2;
svg.append("text").text("Hello").attr("x",centerx).attr("y", centery)
Working code here
I'm using d3 library to create a svg graphic. The problem I have is when I resize the window. The whole graphic resizes meaning that texts (legend and axis) resize as well, to the point where it's unreadable. I need it to keep the same size when resizing.
I've been searching online and I found this solution:
var resizeTracker;
// Counteracts all transforms applied above an element.
// Apply a translation to the element to have it remain at a local position
var unscale = function (el) {
var svg = el.ownerSVGElement;
var xf = el.scaleIndependentXForm;
if (!xf) {
// Keep a single transform matrix in the stack for fighting transformations
xf = el.scaleIndependentXForm = svg.createSVGTransform();
// Be sure to apply this transform after existing transforms (translate)
el.transform.baseVal.appendItem(xf);
}
var m = svg.getTransformToElement(el.parentNode);
m.e = m.f = 0; // Ignore (preserve) any translations done up to this point
xf.setMatrix(m);
};
[].forEach.call($("text"), unscale);
$(window).resize(function () {
if (resizeTracker) clearTimeout(resizeTracker);
resizeTracker = setTimeout(function () { [].forEach.call($("text"), unscale); }, 0);
});
And added it to my code, but it's not working. I debugged it and at this part of the code:
var xf = el.scaleIndependentXForm;
It always returns the same matrix: 1 0 0 1 0 0 and the text keeps resizing as does the rest of the svg elements instead of keeping static.
Could anyone help me, please?
Thanks in advance.
The same thing was happening to me with an SVG generated by SnapSVG until I noted that the example page on which this does work wraps its 'main' SVG tag in another SVG tag before using el.ownerSVGElement.ownerSVGElement rather than el.ownerSVGElement.
Wrapping my SVG in an 'empty' wrapper SVG (note style overflow:visible;) I had much better results!
Edit: oh, wait. Internet Explorer still isn't happy. Seems the author of the solution is aware...
I am working on a visualization tool that uses a svg image of the brain. Now this svg has paths that are filled with a color. I want to loop over all these paths to set the fill color to white, but for some reason I cannot get the element.
The project can be seen here. The svg is inside a div and I even assigned an identifier brain to the div. The svg itself has an id svg2. So far I've tried the following:
function clearBrainColors() {
var brain = d3.select("#svg2");
console.log(brain);
var paths = brain.selectAll("path");
console.log(paths.length);
brain.selectAll('path').each(function(d,i) { console.log(this); });
}
But it outputs null in the array[0] component of the selection and 0 with paths.length.
I've also tried to use lines such as
var brain = d3.select("#brain svg"); and var brain = d3.select("#brain svg#svg2"); but those do not work either.
So, how can I select the brain svg using d3?
Decided to put the svg inline as it apparently speeds things up.
The code I used to fill the svg is now:
$("#svg2").find("path").each(function(){
$(this).css({ fill: "#ff0000" });
});
You can try setTimeOut() , following example
setTimeOut(function() {
var brain = d3.select("#svg2");
console.log(brain);
}, 1000);
this could be the svg is generate on the spot, d3 unable get at that moment.
Hope this help :)
The label on the left most node disappears if the node is partially displayed on the canvas.
How do I resolve this?
Thanks
InfoVis tries to hide the node labels when it asserts that the label would fall off the canvas, if it were to be displayed on the node.
It basically computes the canvas position and dimensions, the node position and dimensions, and tries to see if the label's position is out of the canvas.
This can be seen on placeLabeland fitsInCanvas functions, around lines 9683 and 7669 of the final jit.js file, respectively.
I faced this problem too, while working with SpaceTree visualizations. This became an issue when we tried present a decent experience in mobile, where I could not find a way to put the canvas panning to work (so, when a node label partially disappeared, I had no way to select that node and centre the whole tree by consequence, to allow the further exploration of other nodes...).
What I did was change the function fitsInCanvas:
fitsInCanvas: function(pos, canvas, nodeW, nodeH) {
var size = canvas.getSize();
if(pos.x >= size.width || (pos.x + nodeW) < 0
|| pos.y >= size.height || pos.y + nodeH < 0) return false;
return true;
}
And called it accordingly on placeLabel:
placeLabel: function(tag, node, controller) {
...
...
...
var style = tag.style;
style.left = labelPos.x + 'px';
style.top = labelPos.y + 'px';
// Now passing on the node's width and heigh
style.display = this.fitsInCanvas(labelPos, canvas, w, h)? '' : 'none';
controller.onPlaceLabel(tag, node);
}
However, this is no solution.
You now will probably see your labels falling off the canvas, in a weird effect, until the whole node disappears.
And, obviously, I changed the source directly... a ticket should be filled on github.
EDIT:
Actually, it seems that I was working with an old version of the lib. The discussed behaviour changed to something similar to what I was describing. So, there is no need to change the code. Just download again your files. Specifically, the following link should give you these changes:
https://github.com/philogb/jit/blob/3d51899b51a17fa630e1af64d5393def589f874e/Jit/jit.js
There is a much simpler way to fix this although it might not be as elegant.
First, use the CSS 3 overflow attribute on the div associated with the Spacetree. For example, if the div in your HTML page that is being used by infovis is
<div id="infovis"> </div>
Then, you want some CSS that makes sure that your canvas does not allow overflow.
#infovis {
position:relative;
width:inherit;
height:inherit;
margin:auto;
overflow:hidden;
}
Next, in your your space tree definition, you probably have a placeLabel : function defined. At the end of it, simply set the style.display = "";. This force the label to be shown if the node is placed onto the canvas. For example:
onPlaceLabel: function(label, node, controllers){
//override label styles
var style = label.style;
if (node.selected) {
style.color = '#ccc';
}
else {
style.color = '#fff';
}
// show the label and let the canvas clip it
style.display = '';
}
Thus, you are displaying the text and turning it over to the browser to clip any part of the node or the label that extend off the canvas.
I am dynamically generating some charts using jqplot.
Some of my labels are very long text strings that don't display very nicely - I have printed them at a 30 degree angle along the x axis but the long labels run off to the right of the page.
Is there a way of setting a maximum width for the label (ideally same as it's bar on the chart) and making the text of the label wrap?
I think you can do it using CSS.
Ticks from xaxis are customizable thanks to .jqplot-xaxis-tick{ width: xxx px;} (respectively jqplot-yaxis-tick, jqplot-y2axis-tick...)
I had the same problem some months ago. Here is the question with a nice solution from #boro :
JqPlot : Set a fix height value for the graph area not including y axe labels
I resolved with a javascript. You need to execute the script on window.onload, otherwise you can't get DOM elements of the charts.
window.onload = function() {
var xAxisLabel = document.getElementsByClassName("jqplot-xaxis-tick");
var i;
for (i = 0; i < xAxisLabel.length; i++) {
if(i%2 == 0)
xAxisLabel[i].style.top = "32px";
}
};
Basically I change the vertical position of the elements in 2%0 position.
Than you need to adjust some css property
.jqplot-xaxis{margin-top:10px; height: 50px !important;}
.jqplot-xaxis is the class of the xaxis label bar.