Automatic layout of DAG diagram - d3.js

I need an algorithm to layout topologically sorted DAG similar to how my JSFiddle shows.
Is there any open source library that can do that? What are my options?
At the moment it's just a simple manual calculation of X and Y coordinates:
var nodes = [
{label: 'A', x: constant, y: 255, width:70, height:50 },
{label: 'B', x: 2.5*constant, y: 410, width:70, height:50 },
{label: 'C', x: 2.5*constant, y: 255, width:70, height:50 },
{label: 'D', x: 4.0*constant, y: 255, width:70, height:50 },
{label: 'E', x: 2.5*constant, y: 100, width:70, height:50 },
{label: 'F', x: 4.0*constant, y: 100, width:70, height:50 }
];

You can do this sort of thing with d3-graphviz. There are plenty of options for styling and several different layout engines. This is pretty close to yours:
let dotSource = `graph{
rankdir = LR;
node [
style=filled;
fillcolor=steelblue;
fontcolor=white;
shape=box
]
A -- B;
A -- C;
A -- E;
C -- D;
C -- E;
E -- F;
}`
d3.select("#graph").graphviz()
.renderDot(dotSource);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.2/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-graphviz/5.0.2/d3-graphviz.min.js"></script>
<div id="graph" style="text-align: center;"></div>

Related

Multiple Shape annotations on D3plus line plot

I have a line plot created using d3plus, and I need to have multiple line annotations (one horizontal and two vertical) with Shape labels to identify them. I am able to get the lines to show properly, but I am unable to get multiple labels to show up on the graph.
Here is my set of data:
[
{"id":"line","myDate":"Sep 2011","value":8303269},
{"id":"line","myDate":"Jul 2012","value":8389066},
{"id":"line","myDate":"Sep 2012","value":8632844},
{"id":"line","myDate":"Mar 2013","value":8926414},
{"id":"line","myDate":"Jun 2013","value":9169985},
{"id":"line","myDate":"Mar 2014","value":9273689},
{"id":"line","myDate":"Sep 2014","value":9343712},
{"id":"line","myDate":"Dec 2014","value":9416974},
{"id":"line","myDate":"May 2015","value":9546380},
{"id":"line","myDate":"Sep 2015","value":10484320},
{"id":"line","myDate":"Sep 2015","value":11455165},
{"id":"line","myDate":"Dec 2015","value":11997581},
{"id":"line","myDate":"Apr 2016","value":12104931},
{"id":"line","myDate":"May 2016","value":12111915},
{"id":"line","myDate":"Jun 2016","value":12127119},
{"id":"line","myDate":"Jul 2016","value":12800226},
{"id":"line","myDate":"Mar 2017","value":12915303},
{"id":"line","myDate":"Nov 2017","value":12947360},
{"id":"line","myDate":"Nov 2018","value":12957309}
]
and here is my LinePlot annotations array.
new LinePlot()
.select("#demo")
.height(500)
.config({
data: vm.plotData,
x: 'myDate',
y: 'value',
groupBy: 'id',
annotations: [
{
data: [
{id: "start", x: "Jul 2012", y: 8000000},
{id: "start", x: "Jul 2012", y: 20000000},
{id: "end", x: "Nov 2017", y: 8000000},
{id: "end", x: "Nov 2017", y: 20000000},
{id: "dotted", x: "Sep 2011", y: this.item.ceilingValue},
{id: "dotted", x: "Nov 2018", y: this.item.ceilingValue}
],
shape: "Line",
stroke: function(d) {
return d.id === "box" ? "blue" : "green";
},
strokeDasharray: "10",
strokeWidth: 2
},
{
data: [
{
x: 'Jul 2012',
y: 20000000,
width: 100,
height: 25
}
],
fill: "#0c1971",
label: "Start Date",
labelConfig: {
textAnchor: "middle",
verticalAlign: "middle"
},
shape: "Rect"
},
{
data: [
{
x: 'Nov 2017',
y: 20000000,
width: 10,
height: 25
}
],
fill: "#255832",
label: "End Date",
labelConfig: {
textAnchor: "middle",
verticalAlign: "middle"
},
shape: "Rect"
}
]
})
.render()
What happens is that when I just have the Start Date item, it works fine, but when I add the End Date object, the first one disappears, and the second one isn't fully rendered.
According to the docs, annotations accepts
custom config objects for the Shape class, either a single config object or an array of config objects.`,
which is what I've provided, so I'm not sure where the problem is. What do I need to change to get all of my labels to appear properly?
I was able to figure it out based on this comment. The gist is that you have to combine all items of a particular Shape into one object:
annotations: [
{
data: [
{id: "start", x: "Jul 2012", y: 8000000},
{id: "start", x: "Jul 2012", y: 20000000},
{id: "end", x: "Nov 2017", y: 8000000},
{id: "end", x: "Nov 2017", y: 20000000},
{id: "dotted", x: "Sep 2011", y: this.item.ceilingValue},
{id: "dotted", x: "Nov 2018", y: this.item.ceilingValue}
],
shape: "Line",
stroke: function(d) {
return d.id === "box" ? "blue" : "green";
},
strokeDasharray: "10",
strokeWidth: 2
},
{
data: [
{
id: 'start',
label: 'Start Date',
x: 'Jul 2012',
y: 20000000,
width: 100,
height: 25
},
{
id: 'end',
label: 'End Date',
x: 'Nov 2017',
y: 20000000,
width: 100,
height: 25
}
],
fill: function(d) {
let result;
switch (d.id) {
case 'start':
result = "#0c1971";
break;
case 'end':
result = "#255832";
break;
}
return result;
},
label: function (d) {
let result;
switch (d.id) {
case 'start':
result = "Start Date";
break;
case 'end':
result = "End Date";
break;
}
return result;
},
labelConfig: {
textAnchor: "middle",
verticalAlign: "middle"
},
shape: "Rect"
}
]

canvasjs in decimal format with suffix is not working properly

I use canvasJS to make a line graph report, the issue now is it didn't show properly in tooltip using yValueFormatString.
my goal is to display the value:
{
type:"stepLine",
name: "title",
showInLegend: true,
connectNullData: true,
yValueFormatString: "##.## %",
dataPoints: [
{ x: new Date(2019, 1, 20), y: 12.78 },
{ x: new Date(2019, 1, 19), y: 12.79 },
{ x: new Date(2019, 1, 18), y: 12.80 },
]
}
in tooltip, it shows
1278 %
1279 %
1280 %
I think there's something wrong with it, I wanted to display like:
12.78 %
12.79 %
12.80 %
any idea?
According to documentation, "%" Multiplies a number by 100 i.e. 12.78("##.## %") => 1278%. Instead setting yValueFormatString to "##.#0 '%'" should work fine in this case.
Here is an example:
var chart = new CanvasJS.Chart("chartContainer", {
data: [{
type:"stepLine",
name: "title",
showInLegend: true,
connectNullData: true,
yValueFormatString: "##.#0 '%'",
dataPoints: [
{ x: new Date(2019, 1, 20), y: 12.78 },
{ x: new Date(2019, 1, 19), y: 12.79 },
{ x: new Date(2019, 1, 18), y: 12.80 },
]
}]
});
chart.render();
<script src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
<div id="chartContainer" style="width: 100%; height: 260px"></div>

Generate an array of charts

I want to generate an array of charts and insert them into the DOM.
(I want to use these charts as templates and insert them at the right time)
like that:
Var array = [];
array[0].kendoChart ({// .........});
array[1].kendoChart ({// .........});
array.forEach (function (el) {
$('body').append(el);
})
Tell me how to be?
I would keep an array of the data used to generate the charts, and then generate them at the time of insert.
In HTML provide a container for the charts to be inserted (could be body as in your example):
<div id="chartsContainer" ></div>
Then in script have an array of the data needed to generate the multiple charts, e.g. :
var charts = [
{
chartid: "id1", title: "Chart 1",
data: [{ x: "item1", y: 2}, { x: "item2", y: 4},{ x: "item3", y: 1}]
},
{
chartid: "id2", title: "Chart 2",
data: [{ x: "item1", y: 8}, { x: "item2", y: 3},{ x: "item3", y: 7},{ x: "item4", y: 2}]
},
{
chartid: "id3", title: "Chart 3",
data: [{ x: "item1", y: 1}, { x: "item2", y: 2},{ x: "item3", y: 4} ]
},
];
Finally loop through the array, insert DIVs in the container and create a chart in each DIV:
$("#chartsContainer").empty();
var arrayLength = charts.length;
for (var i = 0; i < arrayLength; i++) {
var div = $('<div id="' + charts[i].chartid + '"></div>');
$("#chartsContainer").append(div);
div.kendoChart({
theme: "Flat",
chartArea: { height: 200 },
dataSource: {data: charts[i].data },
title: { text: charts[i].title},
seriesDefaults: {type: "column"},
series: [{field: "y" }],
categoryAxis: {
field: "x",
majorGridLines: {visible: false },
}
});
}
Working DEMO

AmXYChart - How to add padding to prevent hidden overflows

I have create a simple XY Chart graph with percent as y axes and customers as x axes, I randomised the data between 0...100% with a set of 184 points. and have a bit of difficulty display the lower/upper region values. I have included an image for the demonstration.
Here my config file, I cant seem to find some sort of offset/padding ?
{
type: 'xy',
addClassNames: true,
autoMargins: false,
marginLeft: 67,
marginBottom: 55,
graphs: [{
balloonFunction,
bullet: 'round',
xField: 'customers',
yField: 'rate',
bulletSize: 16,
lineColorField: 'color',
}],
valueAxes: [
{
title,
borderThickness: 0,
axisThickness: 2,
maximum: 100,
labelFunction: (e,val) => { return val + "%"; },
},
{
title,
position: 'bottom',
axisAlpha: 0,
borderThickness: 0,
axisThickness: 0,
gridThickness: 0,
},
],
dataProvider: data,
};
Thanks.
There isn't a way to pad this without modifying your minimum and maximum to be further outside your 0-100 range to accommodate. Since you're using a labelFunction, you can set it up so that you don't display any labels above and below 0-100% if you want, for example:
labelFunction: (e, val) => { return (val > 100 || val < 0 ? "" : val + "%"); }
Demo below using -10 as a minimum and 110 as a maximum:
var data = [{"rate": 99, "customers": 2421},{"rate": 76,"customers": 100},{"rate": 68,"customers": 1711},{"rate": 38,"customers": 313},{"rate": 94,"customers": 393},{"rate": 57,"customers": 946},{"rate": 99,"customers": 1772},{"rate": 20,"customers": 2168},{"rate": 100,"customers": 754},{"rate": 40,"customers": 121},{"rate": 51,"customers": 2412},{"rate": 15,"customers": 2364},{"rate": 32,"customers": 2161},{"rate": 55,"customers": 1506},{"rate": 29,"customers": 986},{"rate": 0,"customers": 698},{"rate": 4,"customers": 1285},{"rate": 22,"customers": 2108},{"rate": 17,"customers": 2081},{"rate": 79,"customers": 251},{"rate": 48,"customers": 258},{"rate": 41,"customers": 1541},{"rate": 35,"customers": 1132},{"rate": 86,"customers": 1213},{"rate": 1,"customers": 1936},{"rate": 51,"customers": 1737},{"rate": 5,"customers": 2447},{"rate": 60,"customers": 305},{"rate": 37,"customers": 776},{"rate": 64,"customers": 886}];
var chart = AmCharts.makeChart("chartdiv", {
type: 'xy',
addClassNames: true,
autoMargins: false,
marginLeft: 67,
marginBottom: 55,
graphs: [{
//balloonFunction,
bullet: 'round',
xField: 'customers',
yField: 'rate',
bulletSize: 16,
lineAlpha: 0, //for testing only
lineColorField: 'color',
}],
valueAxes: [
{
title: "Rate (%)",
borderThickness: 0,
axisThickness: 2,
maximum: 110,
minimum: -10,
labelFunction: (e,val) => { return (val > 100 || val < 0 ? "" : val + "%"); },
},
{
title: "Customers",
position: 'bottom',
axisAlpha: 0,
borderThickness: 0,
axisThickness: 0,
gridThickness: 0,
},
],
dataProvider: data,
});
<script type="text/javascript" src="//www.amcharts.com/lib/3/amcharts.js"></script>
<script type="text/javascript" src="//www.amcharts.com/lib/3/xy.js"></script>
<div id="chartdiv" style="width: 100%; height: 300px;"></div>
If you want to remove the extra grid lines from the additional points generated by the new minimum and maximum, you'll have to use guides as your grid lines and labels instead of the ones auto-generated by the chart. For example:
valueAxes: [{
guides: [{
"value": 0,
"label": "0%",
"lineAlpha": .2,
"tickLength": 5
},
// repeat for each tick/grid line
],
"gridAlpha": 0,
"tickLength": 0,
"labelsEnabled": false,
// ...
Demo:
var data = [{"rate": 99, "customers": 2421},{"rate": 76,"customers": 100},{"rate": 68,"customers": 1711},{"rate": 38,"customers": 313},{"rate": 94,"customers": 393},{"rate": 57,"customers": 946},{"rate": 99,"customers": 1772},{"rate": 20,"customers": 2168},{"rate": 100,"customers": 754},{"rate": 40,"customers": 121},{"rate": 51,"customers": 2412},{"rate": 15,"customers": 2364},{"rate": 32,"customers": 2161},{"rate": 55,"customers": 1506},{"rate": 29,"customers": 986},{"rate": 0,"customers": 698},{"rate": 4,"customers": 1285},{"rate": 22,"customers": 2108},{"rate": 17,"customers": 2081},{"rate": 79,"customers": 251},{"rate": 48,"customers": 258},{"rate": 41,"customers": 1541},{"rate": 35,"customers": 1132},{"rate": 86,"customers": 1213},{"rate": 1,"customers": 1936},{"rate": 51,"customers": 1737},{"rate": 5,"customers": 2447},{"rate": 60,"customers": 305},{"rate": 37,"customers": 776},{"rate": 64,"customers": 886}];
var chart = AmCharts.makeChart("chartdiv", {
type: 'xy',
addClassNames: true,
autoMargins: false,
marginLeft: 67,
marginBottom: 55,
graphs: [{
//balloonFunction,
bullet: 'round',
xField: 'customers',
yField: 'rate',
bulletSize: 16,
lineAlpha: 0, //for testing only
lineColorField: 'color',
}],
valueAxes: [
{
title: "Rate (%)",
borderThickness: 0,
axisThickness: 2,
maximum: 110,
minimum: -10,
guides: [{
value: 0,
label: "0%",
lineAlpha: .2,
tickLength: 5
},{
value: 20,
label: "20%",
lineAlpha: .2,
tickLength: 5
},{
value: 40,
label: "40%",
lineAlpha: .2,
tickLength: 5
},{
value: 60,
label: "60%",
lineAlpha: .2,
tickLength: 5
},{
value: 80,
label: "80%",
lineAlpha: .2,
tickLength: 5
},{
value: 100,
label: "100%",
lineAlpha: .2,
tickLength: 5
}],
gridAlpha: 0,
tickLength: 0,
labelsEnabled: false
},
{
title: "Customers",
position: 'bottom',
axisAlpha: 0,
borderThickness: 0,
axisThickness: 0,
gridThickness: 0,
},
],
dataProvider: data,
});
<script type="text/javascript" src="//www.amcharts.com/lib/3/amcharts.js"></script>
<script type="text/javascript" src="//www.amcharts.com/lib/3/xy.js"></script>
<div id="chartdiv" style="width: 100%; height: 300px;"></div>

C3.js add color to an horizontal line

Is there a way in C3.js for to add COLOR to an horizontal line, the level 0 in axis y in bar graphs? By default you have this graph:
What I need is this:
Any idea? thanks.
UPDATE: I've made a line with this, but I need to add color.
grid: {
y: {
lines: [
{value: 0, text: ''}
]
}
}
In case anyone need it, this does the magic:
https://github.com/c3js/c3/issues/362#issuecomment-46377069
The reference has an example for horizontal (x-axis) lines: https://c3js.org/samples/grid_x_lines.html
// Copied from the reference
var chart = c3.generate({
data: {
columns: [
['sample', 30, 200, 100, 400, 150, 250],
['sample2', 1300, 1200, 1100, 1400, 1500, 1250],
],
axes: {
sample2: 'y2'
}
},
axis: {
y2: {
show: true
}
},
grid: {
y: {
lines: [
{value: 50, text: 'Label 50 for y'},
{value: 1300, text: 'Label 1300 for y2', axis: 'y2', position: 'start'},
{value: 350, text: 'Label 350 for y', position: 'middle'}
]
}
}
});
Also for x-axis lines (vertical): https://c3js.org/samples/grid_x_lines.html

Resources