Related
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"
}
]
I'm trying to display total monthly sales and daily stock level. This way you could easily see that you didn't have any sales a particular month because you had low stock. Monthly sales is a bar chart that should be in the center of each month (in between the ticks).
In order to get it close to the middle my data is using the 15th of each month as the date to center it. I would want to know if there is a better way to achieve this?
JSFiddle to play around with: https://jsfiddle.net/8Lydhpqc/3/
const dailyStock = [
{ x: "2017-08-02", y: 1 },
{ x: "2017-08-25", y: 3 },
{ x: "2017-09-10", y: 7 },
{ x: "2017-09-28", y: 0 },
{ x: "2017-10-02", y: 3 },
{ x: "2017-10-24", y: 2 },
{ x: "2017-11-01", y: 1 },
{ x: "2017-11-30", y: 0 },
];
//using the 15th of each month to center it
const monthlyTotal = [
{ x: "2017-08-15", y: 1 },
{ x: "2017-09-15", y: 10 },
{ x: "2017-10-15", y: 5 },
{ x: "2017-11-15", y: 5 },
];
var ctx = document.getElementById("myChart").getContext("2d");
var myChart = new Chart(ctx, {
type: "bar",
data: {
labels: ["2017-08", "2017-09", "2017-10", "2017-11"],
datasets: [
{
label: "sales",
data: data,
backgroundColor: "green",
borderColor: "black",
borderWidth: 1,
order: 2,
},
{
label: "stock",
type: "line",
data: dailyStock,
backgroundColor: "orange",
borderColor: "orange",
fill: false,
order: 1,
},
],
},
options: {
scales: {
xAxes: [
{
type: "time",
time: {
unit: "month",
displayFormats: {
month: "MMM",
},
},
distribution: "linear",
},
],
yAxes: [
{
ticks: {
beginAtZero: true,
},
},
],
},
},
});
Welcome to Stackoverflow!
It seems that there is a way better than using the 15th of the month.
You need to add another axis for the bar that is a category type axis. Also its pretty critical that you have "offset: true" on that axis as well. Otherwise it will not center.
In the code below I named that category "bar" and the existing one "line"
I also created a fiddle: https://jsfiddle.net/jyf8ax3e/
var myChart = new Chart(ctx, {
type: "bar",
data: {
labels: ["2017-08", "2017-09", "2017-10", "2017-11"],
datasets: [
{
barPercentage: .7,
xAxisID: "bar",
label: "sales",
data: monthlyTotal,
backgroundColor: "green",
borderColor: "black",
borderWidth: 1,
width: 55,
order: 2,
},
{
label: "stock",
type: "line",
data: dailyStock,
backgroundColor: "orange",
borderColor: "orange",
fill: false,
order: 1,
},
],
},
options: {
scales: {
xAxes: [
{
id: "line",
type: "time",
time: {
unit: "month",
displayFormats: {
month: "MMM",
},
},
distribution: "linear",
},
{
id: "bar",
offset: true,
type: "category",
distribution: "series",
}
],
yAxes: [
{
ticks: {
beginAtZero: true,
},
},
],
},
},
});
I need to show labels on the x-axis every 2 hours (0h, 2h, 4h...). What am I missing?
<script>
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
//labels: ['0h', '2h', '4h', '6h', '8h', '10h', '12h', '14h', '16h', '18h', '20h', '22h', '0h'],
datasets: [{
label: 'AAA1111',
//xAxisID: 'Hora',
//yAxisID: 'Velocidade',
data: [{
t: new Date("2015-3-15 12:30"),
y: 12
},
{
t: new Date("2015-3-15 14:40"),
y: 45
},
{
t: new Date("2015-3-15 17:50"),
y: 77
}
],
borderColor: 'rgba(255, 0, 0, 1)',
borderWidth: 4,
fill: false,
lineTension: 0,
lineJoint: "round",
spanGaps: true
}]
},
options: {
scales: {
xAxes: [{
type: 'time',
distribution: 'linear',
time: {
unit: 'hour',
//stepSize: 24??
},
ticks: {
source: 'data'
}
}]
}
}
});
</script>
The chart plots Time x Velocity.
Two small changes will achieve the desired result:
set stepSize: 2 to 'show labels on X axis every 2 hours (0h, 2h, 4h...)'
remove ticks: { source: 'data' } as that:
generates ticks from data (including labels from data {t|x|y} objects)"
Here's a working example based on the posted code:
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
//labels: ['0h', '2h', '4h', '6h', '8h', '10h', '12h', '14h', '16h', '18h', '20h', '22h', '0h'],
datasets: [{
label: 'AAA1111',
//xAxisID: 'Hora',
//yAxisID: 'Velocidade',
data: [{
t: new Date("2015-3-15 12:30"),
y: 12
},
{
t: new Date("2015-3-15 14:40"),
y: 45
},
{
t: new Date("2015-3-15 17:50"),
y: 77
}
],
borderColor: 'rgba(255, 0, 0, 1)',
borderWidth: 4,
fill: false,
lineTension: 0,
lineJoint: "round",
spanGaps: true
}]
},
options: {
scales: {
xAxes: [{
type: 'time',
distribution: 'linear',
time: {
unit: 'hour',
stepSize: 2
}
}]
}
}
});
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.9.3/dist/Chart.bundle.min.js"></script>
<canvas id="myChart"></canvas>
I'm using shapeDefaults in constructing the diagram. The content -> text value should refer to the node's name property. For example, if the node object is:
var data = [{
designation: "CEO",
name: "Mitchell Johnson",
manages: [{
designation: "Vice President",
name: "Smith Cooper",
manages: [{
designation: "Director",
name: "Andrew Walker"
}, {
designation: "Director",
name: "Robert Thompson"
}]
}, {
designation: "Vice President",
name: "Harris Jones",
manages: [{
designation: "Director",
name: "Edward Collins"
}, {
designation: "Director",
name: "Thomas Wang"
}]
}]
}];
Here content -> text should refer to 'name' attribute and display it in the center.
Make sure you add 'shapeDefaults' to the diagram config:
shapeDefaults:
{
visual: visualTemplate
},
In the function visualTemplate, add your way of marking it up like :
function visualTemplate(options)
{
var dataviz = kendo.dataviz;
var g = new dataviz.diagram.Group();
var dataItem = options.dataItem;
g.append(new dataviz.diagram.Rectangle(
{
width: 210,
height: 75,
stroke:
{
width: 0
},
fill: dataItem.color
}));
g.append(new dataviz.diagram.TextBlock(
{
text: dataItem.name,
x: 5,
y: 20,
color: "#fff"
}));
}
if you ever get to know how to center the text, please tell me. I am looking for it :)
you can also look at : http://demos.telerik.com/kendo-ui/diagram/index
being new to cytoscapeweb 2, i am following one of the provided example in order to learn how to use the API.
i cant make it work and firebug reports the following message:
Component returned failure code: 0x80004005 (NS_ERROR_FAILURE)
var n = this.nodesGroup.getBBox();
in my html document, the cytoscapeweb div is embedded in a jquery tab widget (just to provide context)
somewhere in my.html
<div id="tabs">
<ul>
<li>Interactors Selection</li>
<li>Interactome Builder</li>
</ul>
<div id="tabs-1">
<p>Tab 1 content</p>
</div>
<div id="tabs-2">
<p>Tab 2 content</p>
<div id="cy"></div>
</div>
</div>
and in foo.js
function cytoscapeInit() {
alert ("intializing cytoscape");
// create a mapper for node size
var nodeSizeMapper = {
continuousMapper: {
attr: {
name: "weight",
min: 0,
max: 100
},
mapped: {
min: 15,
max: 30
}
}
};
// call cytoscape web on the `cy` div
$("#cy").cytoscapeweb({
// define the elements in the graph
elements: {
nodes: [
{ data: { id: "a", weight: 43 }, classes: "foo" },
{ data: { id: "b", weight: 2 }, classes: "bar" },
{ data: { id: "c", weight: 88 }, classes: "foo bar" }
],
edges: [
{ data: { id: "ab", source: "a", target: "b", weight: 32 }, classes: "foo" },
{ data: { id: "bc", source: "b", target: "c", weight: 12 }, classes: "bar baz" },
{ data: { id: "ca", source: "c", target: "a", weight: 96 }, classes: "baz foo" },
{ data: { id: "ac", source: "a", target: "c", weight: 65 }, classes: "bar" }
]
},
// define the layout to use
layout: {
name: "preset",
positions: {
"a": { x: 30, y: 30 },
"b": { x: 125, y: 131 },
"c": { x: 200, y: 50 }
},
fit: false,
stop: function(){
cy.reset();
cy.center();
}
},
// define the visual style (like css) of the graph
style: {
selectors: {
"node":{
shape: "ellipse",
fillColor: "#888",
height: nodeSizeMapper,
width: nodeSizeMapper,
labelText: {
passthroughMapper: "id"
}
},
".yay": {
fillColor: "red",
lineColor: "red",
targetArrowColor: "red"
},
"edge": {
lineColor: "#ccc",
targetArrowColor: "#ccc",
width: {
continuousMapper: {
attr: {
name: "weight"
},
mapped: {
min: 2,
max: 5
}
}
},
targetArrowShape: "triangle"
},
"node:selected": {
fillColor: "#333"
},
"edge:selected":{
lineColor: "#666",
targetArrowColor: "#666"
}
}
},
// define the callback for when cytoscape web is ready
ready: function( cy ){
window.cy = cy;
}
});
Did i miss something obvious?
If so, all apologies.
(1) Don't put alerts in your code like that even when you're testing. It can break asynchronous code, like initialising Cytoscape Web or doing an AJAX call. Use console.log() instead.
(2) You're probably hiding the Cytoscape Web div, cy, with the tabs. You shouldn't be using display: none;, because the Cytoscape Web viewport will then be 0x0 px. Try something like position: absolute; left: -9999px; or similiar for hiding. This entails modifying whatever class name jQuery uses for hidden tabs (probably .ui-state-hidden or something similar).
(3) I'll look into making the renderer code more tolerant of hidden Cytoscape Web divs.
I have a better solution to this problem:
Execute jquery tab just after all of the graphs are loaded.
http://cytoscape.github.io/cytoscape.js/#core/events/cy.ready
for(var t = 0, tot=pageList.length; t<tot; t++) //draw graph for all pages
{
var currPage = pageList[t];
getData(currPage);
}
function getData(currPage)
{
//initiate graph, do stuff...
//graph ready
window[currPage + "Container"].ready(function(e)
{
if (currPage.indexOf(lastPage) != -1)//executetabsafterlastgraphisdrawn
{
setTabs();
}
});
}