sap.ui.core.Control.extend("control.linechart", {
/* the control API */
metadata : {
properties : {
"items" : { type: "any" },
"height" : {type: "int"},
"width" : {type: "int"},
"popup" : {type: "any"}
},
events: {
"select" : {},
"selectEnd": {}
}
},
// the part creating the HTML:
renderer : function(oRm, oControl) { // static function, so use the given "oControl" instance instead of "this" in the renderer function
oRm.write("<div");
oRm.writeControlData(oControl); // writes the Control ID and enables event handling - important!
oRm.addClass("lineChart"); // add a CSS class for styles common to all control instances
oRm.writeClasses(); // this call writes the above class plus enables support for my.Bubble.addStyleClass(...)
oRm.write(">");
oRm.write("</div>");
},
onAfterRendering: function() {
data = this.getItems();
//alert(JSON.stringify(this.getItems()));
var passedheight = this.getHeight();
//var containerWidth = jQuery.sap.byId(this.oParent.sId).width() || 800; // gets super parent width
var containerWidth = $("#"+this.getId()).parent().width(); // gets immediate parent width
var margin = {top: 15, right: 30, bottom: 20, left: 30},
width = containerWidth- margin.left - margin.right,
height = passedheight - margin.top - margin.bottom;
var parseDate = d3.time.format("%d-%b-%y %H:%M %p").parse;
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left").ticks(4).tickSize(-width, 0, 0);
var line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.tonneValue); });
var svg = d3.select("#"+this.getId()).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var wsfs = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, tonneValue: +d[name]};
})
};
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
d3.min(wsfs, function(c) { return d3.min(c.values, function(v) { return v.tonneValue; }); }),
d3.max(wsfs, function(c) { return d3.max(c.values, function(v) { return v.tonneValue; }); })
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var wsf = svg.selectAll(".wsf")
.data(wsfs)
.enter().append("g")
.attr("class", "wsf");
wsf.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
var legendNames = d3.keys(data[0]).filter(function(key) { return key !== "date" });
data.forEach(function(d) {
d.ages = legendNames.map(function(name) { return {name: name, value: +d[name]}; });
});
var legend = svg.selectAll(".legend")
.data(legendNames.slice())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 4)
.style("fill", function(d) {return color(d); });
legend.append("text")
.attr("x", width - 24)
.attr("y", 6)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
},
});
In this whole program why Right hand side value is changing i.e. when I do data = this.getItems(); I assume this.getItems is assigned to data. But data is manipulated in rest of the program but when oAfterRendering is invoked again I get the manipulated data thats there in variable data. How is this.getItems() i.e. items property is manipulated when data is the one that is manipulated.Due to use of Openui5 and custom controls there is no way to save the data in some temporary variable.
When you return an object from a function, JavaScript does not copy that object, but return a reference to it (similar a pointer in C).
This means that if it's modified, it will be reflected in all variables that point to it.
This is the same behaviour for Arrays and other objects.
Anything to avoid that.
You could return a clone of the object. Depending on how complex your object is, a deep clone could be necessary. Keep in mind that this shouldn't be done often, as it can likely affect performance.
An easy way of creating a shallow clone (really just copying properties, cloning any object is more nuanced in JavaScript) in ES6 is...
var cloned = Object.assign({}, objectToBeCloned);
Related
I adapted a multi-line chart which has a legend and axis and displays correctly on the bl.ocks.org site (http://bl.ocks.org/Matthew-Weber/5645518). The legend reorganizes itself when you select a different type from the drop down field. On my adaptation when the legend reorganizes itself the items start to overlap each other when some types are selected. Also the axes draw on top of each other. The original code uses tipsy but I have not checked it.
// original author's code http://bl.ocks.org/Matthew-Weber/5645518;
//set the margins
var margin = {
top: 50,
right: 160,
bottom: 80,
left: 50
},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//set dek and head to be as wide as SVG
d3.select('#dek')
.style('width', width + 'px');
d3.select('#headline')
.style('width', width + 'px');
//write out your source text here
var sourcetext = "xxx";
// set the type of number here, n is a number with a comma, .2% will get you a percent, .2f will get you 2 decimal points
var NumbType = d3.format(",");
// color array
var bluescale4 = ["red", "blue", "green", "orange", "purple"];
//color function pulls from array of colors stored in color.js
var color = d3.scale.ordinal().range(bluescale4);
//defines a function to be used to append the title to the tooltip.
var maketip = function(d) {
var tip = '<p class="tip3">' + d.name + '<p class="tip1">' + NumbType(d.value) + '</p> <p class="tip3">' + formatDate(d.date) + '</p>';
return tip;
}
//define your year format here, first for the x scale, then if the date is displayed in tooltips
var parseDate = d3.time.format("%Y-%m-%d").parse;
var formatDate = d3.time.format("%b %d, '%y");
//create an SVG
var svg = d3.select("#graphic").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//make a rectangle so there is something to click on
svg.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr("class", "plot"); //#fff
// force data to update when menu is changed
var menu = d3.select("#menu select")
.on("change", change);
//suck in the data, store it in a value called formatted, run the redraw function
d3.csv("/sites/default/d3_files/d3-provinces/statistics-april-15-2.csv", function(data) {
formatted = data;
redraw();
});
d3.select(window)
.on("keydown", function() {
altKey = d3.event.altKey;
})
.on("keyup", function() {
altKey = false;
});
var altKey;
// set terms of transition that will take place
// when a new type (Death etc.)indicator is chosen
function change() {
d3.transition()
.duration(altKey ? 7500 : 1500)
.each(redraw);
} // end change
// REDRAW all the meat goes in the redraw function
function redraw() {
// create data nests based on type indicator (series)
var nested = d3.nest()
.key(function(d) {
return d.type;
})
.map(formatted)
// get value from menu selection
// the option values are set in HTML and correspond
//to the [type] value we used to nest the data
var series = menu.property("value");
// only retrieve data from the selected series, using the nest we just created
var data = nested[series];
// for object constancy we will need to set "keys", one for each type of data (column name) exclude all others.
color.domain(d3.keys(data[0]).filter(function(key) {
return (key !== "date" && key !== "type");
}));
var linedata = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
name: name,
date: parseDate(d.date),
value: parseFloat(d[name], 10)
};
})
};
});
//make an empty variable to stash the last values into so we can sort the legend // do we need to sort it?
var lastvalues = [];
//setup the x and y scales
var x = d3.time.scale()
.domain([
d3.min(linedata, function(c) {
return d3.min(c.values, function(v) {
return v.date;
});
}),
d3.max(linedata, function(c) {
return d3.max(c.values, function(v) {
return v.date;
});
})
])
.range([0, width]);
var y = d3.scale.linear()
.domain([
d3.min(linedata, function(c) {
return d3.min(c.values, function(v) {
return v.value;
});
}),
d3.max(linedata, function(c) {
return d3.max(c.values, function(v) {
return v.value;
});
})
])
.range([height, 0]);
//will draw the line
var line = d3.svg.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.value);
});
//create and draw the x axis - need to clear the existing axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickPadding(8)
.ticks(10);
//create and draw the y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickSize(0 - width)
.tickPadding(8);
svg.append("svg:g")
.attr("class", "x axis");
svg.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(" + (0) + ",0)")
.call(yAxis);
//bind the data
var thegraph = svg.selectAll(".thegraph")
.data(linedata)
//append a g tag for each line and set of tooltip circles and give it a unique ID based on the column name of the data
var thegraphEnter = thegraph.enter().append("g")
.attr("class", "thegraph")
.attr('id', function(d) {
return d.name + "-line";
})
.style("stroke-width", 2.5)
.on("mouseover", function(d) {
d3.select(this) //on mouseover of each line, give it a nice thick stroke // works
.style("stroke-width", '6px');
var selectthegraphs = $('.thegraph').not(this); //select all the rest of the lines, except the one you are hovering on and drop their opacity
d3.selectAll(selectthegraphs)
.style("opacity", 0.2);
var getname = document.getElementById(d.name); //use get element cause the ID names have spaces in them - not working
var selectlegend = $('.legend').not(getname); //grab all the legend items that match the line you are on, except the one you are hovering on
d3.selectAll(selectlegend) // drop opacity on other legend names
.style("opacity", .2);
d3.select(getname)
.attr("class", "legend-select"); //change the class on the legend name that corresponds to hovered line to be bolder
}) // end of mouseover
.on("mouseout", function(d) { //undo everything on the mouseout
d3.select(this)
.style("stroke-width", '2.5px');
var selectthegraphs = $('.thegraph').not(this);
d3.selectAll(selectthegraphs)
.style("opacity", 1);
var getname = document.getElementById(d.name);
var getname2 = $('.legend[fakeclass="fakelegend"]')
var selectlegend = $('.legend').not(getname2).not(getname);
d3.selectAll(selectlegend)
.style("opacity", 1);
d3.select(getname)
.attr("class", "legend");
});
//actually append the line to the graph
thegraphEnter.append("path")
.attr("class", "line")
.style("stroke", function(d) {
return color(d.name);
})
.attr("d", function(d) {
return line(d.values[0]);
})
.transition()
.duration(2000)
.attrTween('d', function(d) {
var interpolate = d3.scale.quantile()
.domain([0, 1])
.range(d3.range(1, d.values.length + 1));
return function(t) {
return line(d.values.slice(0, interpolate(t)));
};
});
//then append some 'nearly' invisible circles at each data point
thegraph.selectAll("circle")
.data(function(d) {
return (d.values);
})
.enter()
.append("circle")
.attr("class", "tipcircle")
.attr("cx", function(d, i) {
return x(d.date)
})
.attr("cy", function(d, i) {
return y(d.value)
})
.attr("r", 3) // was 12
.style('opacity', .2)
.attr("title", maketip);
//append the legend
var legend = svg.selectAll('.legend')
.data(linedata);
var legendEnter = legend
.enter()
.append('g')
.attr('class', 'legend')
.attr('id', function(d) {
return d.name;
})
.on('click', function(d) { //onclick function to toggle off the lines
if ($(this).css("opacity") == 1) {
//uses the opacity of the item clicked on to determine whether to turn the line on or off
var elemented = document.getElementById(this.id + "-line"); //grab the line that has the same ID as this point along w/ "-line"
//use get element cause ID has spaces
d3.select(elemented)
.transition()
.duration(1000)
.style("opacity", 0)
.style("display", 'none');
d3.select(this)
.attr('fakeclass', 'fakelegend')
.transition()
.duration(1000)
.style("opacity", .2);
} else {
var elemented = document.getElementById(this.id + "-line");
d3.select(elemented)
.style("display", "block")
.transition()
.duration(1000)
.style("opacity", 1);
d3.select(this)
.attr('fakeclass', 'legend')
.transition()
.duration(1000)
.style("opacity", 1);
}
});
//create a scale to pass the legend items through // this is broken for some types
var legendscale = d3.scale.ordinal()
.domain(lastvalues)
.range([0, 30, 60, 90, 120, 150, 180, 210]);
//actually add the circles to the created legend container
legendEnter.append('circle')
.attr('cx', width + 20) // cx=width+50 made circle overlap text
.attr('cy', function(d) {
var newScale = (legendscale(d.values[d.values.length - 1].value) + 20);
return newScale;
})
.attr('r', 7)
.style('fill', function(d) {
return color(d.name);
});
//add the legend text
legendEnter.append('text')
.attr('x', width + 35) // is this an issue?
.attr('y', function(d) {
return legendscale(d.values[d.values.length - 1].value);
})
.text(function(d) {
return d.name;
});
// set variable for updating visualization
var thegraphUpdate = d3.transition(thegraph);
// change values of path and then the circles to those of the new series
thegraphUpdate.select("path")
.attr("d", function(d, i) {
lastvalues[i] = d.values[d.values.length - 1].value;
lastvalues.sort(function(a, b) {
return b - a
});
legendscale.domain(lastvalues);
return line(d.values);
// }
});
thegraphUpdate.selectAll("circle")
.attr("title", maketip) // displays HTML but not circle
.attr("cy", function(d, i) {
return y(d.value)
})
.attr("cx", function(d, i) {
return x(d.date)
});
// and now for legend items
var legendUpdate = d3.transition(legend);
legendUpdate.select("circle")
.attr('cy', function(d, i) {
return legendscale(d.values[d.values.length - 1].value);
});
legendUpdate.select("text")
.attr('y', function(d) {
return legendscale(d.values[d.values.length - 1].value);
});
d3.transition(svg).select(".x.axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
//make my tooltips work
$('circle').tipsy({
opacity: .9,
gravity: 'n',
html: true
});
//end of the redraw function
}
svg.append("svg:text")
.attr("text-anchor", "start")
.attr("x", 0 - margin.left)
.attr("y", height + margin.bottom - 10)
.text(sourcetext)
.attr("class", "source");
My adapted code (including a lot of console.log messages) is in jsfiddle https://jsfiddle.net/pwarwick43/13fpn567/2/
I am beginning to think the problem might be with the version of d3 or jquery. Anyone got suggestions about this?
I am trying to create a bar chart that has 'attendees' and 'coins'. The data is being read from an external file and I'd like to update the chart as the data changes (or check the file every couple seconds and update the data). I have been trying to follow along mbostock's tutorial on the general update pattern but have had a heck of a time even starting to adapt for my own chart. I didn't find any other questions/answers that dealt with transitioning data from external files, but if I missed something, please let me know. So, with that, I turn you all!
Here is my current JS code:
var margin = {top: 40, right: 20, bottom: 30, left: 40},
width = 950 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var formatNumber = d3.format(".1f");
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(formatNumber);
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Coins:</strong> <span style='color:red'>" + d.coins + "</span>";
})
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.call(tip);
d3.tsv("data.tsv", type, function(error, data) {
x.domain(data.map(function(d) { return d.attendee; }));
y.domain([0, d3.max(data, function(d) { return d.coins; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".5em")
.style("text-anchor", "end")
.text("Coins");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.attendee); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.coins); })
.attr("height", function(d) { return height - y(d.coins); })
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
});
function type(d) {
d.coins = +d.coins;
return d;
}
var inter = setInterval(function() {
update();
}, 1000);
function update() {
}
I should also mention that this is the first time really trying to dig deeper with D3. I hope I am not missing something too obvious! Thank you in advance for any help, suggestions, or pushes in the right direction!
** Edit to note the bar chart is an attempt to add functionality upon the sample found here.
Edit 2: Adding .tsv here for better formatting:
attendee coins
George 35
Charlie 50
Harrison 50
Billy 45
Wally 30
Harley 40
Steven 120
Paul 30
First of all you can call the update function like this:
var inter = setInterval(updateChart, 5000);
The logic which would simulate the fetch is the following:
function fetchData() {
console.log('fetching');
return new Promise(function(resolve, reject) {
var data = [{
attendee: "Paul",
coins: Math.floor(Math.random() * 40) + 1
}, {
attendee: "Bessy the Cow",
coins: Math.floor(Math.random() * 40) + 1
}, {
attendee: "Zeke",
coins: Math.floor(Math.random() * 40) + 1
}];
setTimeout(function() { // Adding timeout to simulate latency
resolve(data);
}, 4000)
})
}
Then we create an update function which will use the newly retrieved data:
function updateChart() {
fetchData()
.then(function(data) {
// Update our y domain with new coin values
y.domain([0, d3.max(data, function(d) {
return d.coins;
})]);
// Update our axis because our y domain just changed
svg.select('g.y')
.transition()
.duration(300)
.ease("linear")
.call(yAxis);
// Create a new data join with the simuldated data
var bars = svg.selectAll('.bar').data(data);
// Remove extra elements (say new data just has 2 bars, this would remove third one)
bars.exit().remove();
// Update existing elements
bars.transition()
.duration(300)
.ease("linear")
.call(renderBar);
// Add new elements (say new data has 5 bars, this would add the additional 2)
bars.enter().append('rect')
.transition()
.duration(300)
.ease("linear")
.call(renderBar);
})
}
I created the renderBar function since we are basically repeating the same routine at adding and updating.
function renderBar(rect) {
rect.attr("class", "bar")
.attr("x", function(d) {
return x(d.attendee);
})
.attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d.coins);
})
.attr("height", function(d) {
return height - y(d.coins);
});
}
This plunkr shows the working code, I removed the d3.tip part:
http://plnkr.co/edit/X3vZp5sReOWBsuZrxf8D?p=preview
I'm trying to build a linechart based off of the example at http://bl.ocks.org/mbostock/3884955. I'm not using temperatures but rather value levels from 0-1 that I call sentiments.
I'm not using a tsv file either, but rather rendering JSON in real time through an AJAX call where my data object is an array with objects in it:
[{date: '20140716', ESPN.com: 0.4, SI.com: 0.5})]
There are typically multiple dates in an array and at least 6-7 websites (they're included in every object).
The code looks like this:
function renderHistoricalData(array) {
var data = array;
var margin = {top: 20, right: 80, bottom: 30, left: 50},
width = 1200 - margin.left - margin.right,
height = 450 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y%m%d").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.interpolate("basis")
.defined(function(d) { return d.y!=0; })
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.sentiment); });
var svg = d3.select("#historical_chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
data.forEach(function(d) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));
d.date = parseDate(d.date);
var sites = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, sentiment: +d[name]};
})
};
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
d3.min(sites, function(c) { return d3.min(c.values, function(v) { return v.sentiment; }); }),
d3.max(sites, function(c) { return d3.max(c.values, function(v) { return v.sentiment; }); })
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Sentiment (%)");
var site = svg.selectAll(".site")
.data(sites)
.enter().append("g")
.attr("class", "site");
site.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
site.append("text")
.datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.sentiment) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
// .style("text-anchor", "inherit")
.text(function(d) { return d.name; });
});
}
This results in something that looks like:
http://imgur.com/hFTItGp
Any ideas? I'd greatly appreciate your help. Thanks!
The root cause of the problem is that in the code
{name: d.name, value: d.values[d.values.length - 1]}
you're assuming that values are sorted by date, latest date last. In your data, the opposite is the case -- the latest date is first. Therefore, your text appears at the beginning of the x axis. To fix, simply sort your data.
I've fixed this and a few other things here.
I'm trying to show at least one line of a graph with this, hopefully to be adding a second later. I just can't get anything to show up though, when this hits the line return line(d.values); I get Error: parsing d="MNaN,260L... for a bunch of lines. I started out with this example, my data structure is identical to the one in that example, and I kept all the variable names and when I console.log(cities), my structure is the same. Ideas?
cities var looks like :
[
{name:"count", values: [{date:1, temperature: 10}, {date:1, temperature: 10}]}
]
JS code looks like:
var margin = {top: 20, right: 0, bottom: 20, left: 20},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y%m%d").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) {
console.log(d)
return x(d.date);
})
.y(function(d) {
return y(d.temperature);
});
var svg = d3.select(".heartbeat").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("/static/data/test.json", function(error, data) {
color.domain(["one"]);
function get_vs(){
a = []
$.each(data, function(k, v){
if(v.hasOwnProperty("CN")){
a.push({"date": new Date(), "temperature": v.CN.count})
}else{
a.push({"date": new Date(), "temperature": 0})
}
})
return a
}
var cities = [
{
name: "count",
values: get_vs()
}
]
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
d3.min(cities, function(c) { return d3.min(c.values, function(v) { return v.temperature; }); }),
d3.max(cities, function(c) { return d3.max(c.values, function(v) { return v.temperature; }); })
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
var city = svg.selectAll(".city")
.data(cities)
.enter().append("g")
.attr("class", "city");
city.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values);
})
.style("stroke", function(d) { return color(d.name); });
});
The data just needed a bit of adapting - the original code was expecting an array of objects.
data = []
d3.json("/static/data/test.json", function(error, json_data) {
$.each(json_data, function(k, v){
a = {count: count, avg: avg, date: date}
data.push(a)
})
})
Im working on a streamgraph at the moment, I want to add tooltips to each layer similar to this http://archive.stamen.com/mtvmovies-streamgraph/chart.html
The tooltips I have now dont really work at all. All I get is 'NaN' displayed in the tooltip box.
Any suggestions?? My code is below.
Thanks in advance.
var customPalette = [
"#ff7f0e", "#2ca02c", "#00FFFF", "#d62728", "#9467bd",
"#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"
];
var format = d3.time.format("%y");
//creating margins around the graph
var margin = {top: 20, right: 30, bottom: 30, left: 200},
width = 1200 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//OUTPUT RANGE
var x = d3.time.scale()
.range([0, width]);
//OUTPUT RANGE
var y = d3.scale.linear()
.range([height, 0]);
//assining custom colors to layers
var colours = d3.scale.ordinal().range(customPalette);
var xAxis = d3.svg.axis()
.scale(x)
.orient("top")
.ticks(d3.time.years);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
//ctreate stack layout
var stack = d3.layout.stack()
.offset("wiggle")
.order("reverse")
.values(function(d) { return d.values; })
.x(function(d) { return d.date; })
.y(function(d) { return d.amount; });
//creates array of datya elements for stacked bar graph
var nest = d3.nest()
.key(function(d) { return d.age; });
//create area
var area = d3.svg.area()
//adds curviture
.interpolate("monotone")
.x(function(d) { return x(d.date); })
.y0(function(d) { return y(d.y0); })
.y1(function(d) { return y(d.y0 + d.y); });
var svg = d3.select("body").append("svg")
//defines length of x-axis
.attr("width", width + margin.left + margin.right)
//defines height of y-axis
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.csv("data6.csv", function(data) {
data.forEach(function(d) {
// Convert strings to numbers
d.date = format.parse(d.date);
d.amount = +d.amount;
});
//returns an array of objects with a key feild (0-20yrs....)
//and a value array which contains associated records
var layers = stack(nest.entries(data));
//.extent() returns min and max values of argument
x.domain(d3.extent(data, function(d) { return d.date; }));
//
y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]);
svg.selectAll(".layer")
.data(layers)
.enter().append("path")
.attr("class", "layer")
.attr("d", function(d) { return area(d.values); })
.style("fill", function(d, i) { return colours(i); });
//CURRENT TOOLTIP CODE
var toolTip = svg.selectAll("path")
.append("svg:title")
.text(function(d) { return (d.date) + (d.amount) });;
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + 0 + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
Are you hoping to concatenate the values of d.date and d.close, or are you interested in adding the values and returning the result?
If the latter:
I would bet that the types of d.date and d.close are not what you expect. I'd recommend, if you haven't already, putting some debug code in to check the types of those variables. Example:
//CURRENT TOOLTIP CODE
var toolTip = svg.selectAll("path")
.append("svg:title")
.text(function(d) {
console.log('d.date type:' + typeof d.date + 'd.close type:' + typeof d.close);
return (d.date) + (d.close);
}
);
Also, you have an extra semicolon at the end of that statement in your code snippet.
If the former:
One or both are of type number and Javascript will try to add them when you use the + operator instead of concatenating them. To return the strings:
.text(function(d) {
return d.date.toString() + ' ' + d.close.toString();
});