I'm drawing couple of graphics on a webpage. Let's say I have 4 graphics (A-B-C-D). I would like to lay them out as follows:
A B
C D
But currently they appear like this:
A
B
C
D
I set the margin, width, and height for A as follows:
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 500 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
Any ideas on how to set these (or some other) properties for B, C and D so that they appear as intended?
Thanks!
Set them .style('display', 'inline-block') and set the width to less than 50% or put a </br> tag between them.
Related
I'm drawing a CDF chart using d3.js.
In the chart I have a symbol at 100th tick at the top of the chart.
The problem is that the symbol is getting cut-off.
I might not be able to post the entire code here posting only the part where the axis are drawn.
Attached is a screenshot of the top most part of the chart.
Below is the code to draw the axis.
const margin = {
top: 5,
right: 20,
bottom: 50,
left: 60
};
this.x = this.addXScale(this.width - margin.left - margin.right);
this.y = this.addYScale(this.height - margin.top - margin.bottom);
this.yAxis = this.addLeftAxis(this.y , this.width - margin.left - margin.right, margin);
this.xAxis = this.addBottomAxis(this.x , this.height - (margin.top+0) - margin.bottom, margin);
// Define svg component and attributes
const svg = this.chartService.drawChartArea(chartElement, this.height + this.xDelta,this.width, margin);
How do I add a buffer space at the top of the chart so that the symbol doesn't get cut off.
I tried adding a buffer space to x and y (margin.top+7) but on adding that the graph plot also goes outside the 100 line.
this.y = this.addYScale(this.height - (margin.top+7) - margin.bottom);
You can adjust the range of the y-axis scale by increasing the maximum value by the amount of buffer space you want to add.
Example:
const margin = {
top: 20, // increase top margin to create buffer space
right: 20,
bottom: 50,
left: 60
};
// add buffer space to y-axis range
const yMax = d3.max(data, d => d.y) + bufferSpace; // replace `data` with your actual data array and `bufferSpace` with the amount of buffer space you want to add
this.y = this.addYScale(this.height - margin.top - margin.bottom, yMax);
// draw y-axis with updated scale
this.yAxis = this.addLeftAxis(this.y, this.width - margin.left - margin.right, margin);
To prevent the symbol from being cut off in the chart, you can make the chart taller by increasing the maximum value of the y-axis. You can control how much extra space is added at the top by adjusting the bufferSpace variable.
I am currently working on a project where i need to create a bowtie diagram from data coming from an application.
From a bit of research, the library that looks the best to acheive this is D3.js
I have played about with / looked at these examples:
Collapsible Tree: https://observablehq.com/#d3/collapsible-tree
Hierarchy Chart: https://bl.ocks.org/willzjc/a11626a31c65ba5d319fcf8b8870f281
Here is a basic bowtie diagram example that i would like to try and replicate:
As you can see in the image - i need multiple top level items and/or a double tree that can open each side/tree independently with data flowing from left to right (red side) and right to left (blue side) of the top level item.
Is this acheivable using D3.js?
Tutorials about d3.js only cover standard charts like bar charts and the tree examples online that i have found only have 1 top level item.
Any help, advice or pointers in the right direction would be greatly appreciated.
There's useful material to consider:
Tree drawing orientation - which links to a block showing that a left-to-right orientation can be achieved by altering use of x and y coordinates of the node
Similar question to yours - accepted answer says to draw two trees in two <g>s and shift one 'over'
Simple D3 v6 example - v6 is useful because it allows Array.from to get a list of the nodes from the d3.tree method without traversing the hierarchy.
The 2nd one isn't quite similar enough to your question so adapted the 3rd to use principles from the 1st:
You want two trees - one going left-to-right from the root and one going right-to-left from the root
The d3.tree method usefully computes the right xs and ys for the positioning of the nodes (except for 'horizontal' rendering you flip use of x and y in the code per the Mike Bostock block). We want to keep the understanding of the relative positions between nodes but do a translation on both RH tree (center in g) and LH tree (flip vertically left of center of g).
The exception being the root node e.g. where you have more nodes on the 'left' vs the 'right' the root will be in a slightly different position. You have to choose one root node for both trees.
You can recompute the coordinates such that:
in the right hand tree, the y coordinates (now meaning x) should have width / 2 added to shift them to the vertical center of the g and then halved to keep 'branch' lengths within the width.
in the left hand tree, the y coordinates (now meaning x) should be halved (same deal for 'branch lengths' in RH tree) and negated to flip the node positions to the left hand side of the root node (where you have opted to choose e.g. the LH tree's root position).
Here's the demo:
// useful links
// https://bl.ocks.org/mbostock/3184089
// https://bl.ocks.org/d3noob/72f43406bbe9e104e957f44713b8413c
var treeDataL = {
"name": "Root",
"children": [
{
"name": "L_A_1",
"children": [
{"name": "L_B_1"},
{"name": "L_B_2"}
]
},
{
"name": "L_A_2",
"children": [
{"name": "L_B_3"},
{"name": "L_B_4"}
]
}
]
}
var treeDataR = {
"name": "Root",
"children": [
{
"name": "R_A_1",
"children": [
{"name": "R_B_1"},
{"name": "R_B_2"},
{"name": "R_B_3"}
]
},
{
"name": "R_A_2",
"children": [
{"name": "R_B_3"},
{"name": "R_B_4"},
{"name": "R_B_5"},
{"name": "R_B_6"}
]
}
]
}
// set the dimensions and margins of the diagram
var margin = {top: 50, right: 50, bottom: 50, left: 50},
width = 400 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// declares a tree layout and assigns the size
var tree = d3.tree()
.size([width, height]);
// create 2x trees using d3 hierarchy
// this is where the computation of coordinates takes place
var nodesL = tree(d3.hierarchy(treeDataL));
var nodesR = tree(d3.hierarchy(treeDataR));
// get arrays of nodes - need v6
nodesLArray = Array.from(nodesL);
nodesRArray = Array.from(nodesR);
// switch out nodesR root for nodesL
// here the choice is to assign coords of root of LH tree to RH
nodesLRoot = nodesLArray.find(n => n.data.name == "Root");
nodesRRoot = nodesRArray.find(n => n.data.name == "Root");
nodesRRoot.x = nodesLRoot.x;
nodesRRoot.y = nodesLRoot.y;
// this is kinda like the 'important bit'
// REMEMBER for horizontal layout, flip x and y...
// LH: halve and negate all y's in nodesL add width / 2
nodesLArray.forEach(n => n.y = ((n.y * 0.5) * -1) + width / 2);
// RH: halve and add width / 2 to all y's nodesR
nodesRArray.forEach(n => n.y = (n.y * 0.5) + width / 2);
// now sticking a bit more closely to the tutorial in link 3
// append svg
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
// align g with margin
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// render both trees
[nodesL, nodesR].forEach(function(nodes, i) {
// adds the links between the nodes
// need to select links based on index to prevent bad rendering
var link = g.selectAll(`links${i}`)
.data( nodes.descendants().slice(1))
.enter()
.append("path")
.attr("class", `link links${i}`) // note two classes
.attr("d", function(d) {
// x and y flipped here to achieve horizontal placement
return `M${d.y},${d.x}C${d.y},${(d.x + d.parent.x) / 2} ${d.parent.y},${(d.x + d.parent.x) / 2} ${d.parent.y},${d.parent.x}`
});
// adds each node as a group
// need to select nodes based on index to prevent bad rendering
var node = g.selectAll(`.nodes${i}`)
.data(nodes.descendants())
.enter()
.append("g")
.attr("class", `node nodes${i}`) // note two classes
.attr("transform", function(d) {
// x and y flipped here to achieve horizontal placement
return `translate(${d.y},${d.x})`;
});
// adds the circle to the node
node.append("circle")
.attr("r", 10);
// adds the text to the node
node.append("text")
.attr("dy", ".35em")
.attr("y", function(d) { return d.children ? -20 : 20; })
.style("text-anchor", "middle")
.text(function(d) { return d.data.name; });
});
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.node text {
font: 10px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
<script src="https://d3js.org/d3.v6.min.js"></script>
The demo is basic and you might need to look into tree.nodeSize to achieve node placements accommodating boxes containing text etc. I think the principle of updating the y (being x) coordinates still applies to flip the LH tree around.
How can I display the values of the given array in javascript? In other words, how can use console.log over "pie" to display (42.9, 37.9 and 19.2)?
It tried console.log(Object.values(pie)) but it didn't work. Thanks a lot.
This is how I created the array:
var width = 350
height = 350
margin = 40
// The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
var radius = Math.min(width, height) / 2 - margin
// append the svg object to the div called 'my_dataviz'
var svg = d3.select("#my_dataviz_b")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var color =["#98abc5", "#8a89a6", "#7b6888"]
var annotations = ["Home win", "Draw game", "Away win"]
var data = d3.selectAll('.values_half_before').nodes();
var pie = d3.pie() //we create this variable, for the values to be readeable in the console
.value(function(d) {return d.innerHTML; })(data);
You can do it this way:
pie.forEach((item) => {
console.log(item.value)
});
If you are looking to log individual values of your array you could loop over them with a for loop.
for (let i = 0; i < pie.length; i++) {
console.log(pie[i].value);
}
You could also use console.table. This will display the values in a nice table overview.
console.table(pie);
I have a group of two objects, what I want is that when resizing the group one of the objects does not change size but the position does.
I have searched the documentation but I can not find references to solve this problem.
var r = 15;
var rect = new fabric.Rect({
left: 50,
top: 50,
fill: 'blue',
width: 100,
height: 100
});
var linkBtn = new fabric.Circle({
left: 50 + rect.width / 2 - r,
top: 50 - r,
strokeWidth: 1,
radius: r,
fill: '#fff',
stroke: '#666'
});
var g = new fabric.Group([rect, linkBtn]);
g.hasRotatingPoint = false;
canvas.add(g);
when I stretch the width of the group, the object "linkBtn" is deformed like an oval, it happens the same with the height. I want that when changing the size, the object "linkBtn" is always the same size, and that is always half the width of the object "rect".
Add {lockUniScaling : true} as the second parameter on this line
var g = new fabric.Group([rect, linkBtn], {lockUniScaling : true});
lockUniScaling
Prevents scaling in either X or Y direction but not in both. In other words, prevents non-proportional scaling of an object.
Is there a way to position a rectangle in Raphael.js based on the center coordinates (much like positioning a circle)?
You can do it by simply creating your own custom element, here is an example on how it can be done :
Raphael.fn.MyRect = function( cx, cy, width, height ) {
var xLeftTop = cx - (width / 2);
var yLeftTop = cy - (height / 2);
this.rectObj = paper.rect( xLeftTop, yLeftTop, width, height );
return this;
};
var paper = Raphael(10, 50, 320, 200);
paper.MyRect( 95, 35, 50, 50 ); // 95 is the center in x and 35 the center in y
A live example : http://jsfiddle.net/7QMbH/
This way, you can create as many rectangle you want, and it makes your code understandable.
The easiest way is to create your rectangle with coordinates x - rect_width/2 and y - rect_heigth/2:
Example:
var rect_w = 180,
rect_h = 80;
var paper = Raphael(10, 10, 500, 500);
paper.rect(100 - rect_w/2, 100 - rect_h/2, rect_w, rect_h, 5);
// The last 5 gives curved edges to you rectangle (if you need it of course).
Basically, instead of 100,100 for top-left, you give 10,60. Good Luck