I have the following plunker where I want to wrap the X Axis labels to next line. I have the following configuration in my controller to customize the x axis but it is not working
xAxis: {
axisLabel: 'X Axis',
tickFormat: function(d){
var words = d.split(' '),label = '';
for (var i=0; i< words.length; i++) {
label += words[i] + "\n"; // does not work either
// label += "<tspan class='x'>" + words[i] + "<tspan>"; // does not work either
}
return label;
}
}
How should i make the code such that x axis does not overlap the labels. I am looking for same result as this but I could not understand how can I apply the logic without touching the NVD3 Directive I am using. Is it possible ?
Related
I have existing charts that displays data for a full day 12:00am - 12:00am.
Now required to change one chart forward to display 4:00am - 4:00am.
I have managed to shift the x axis labels (.add(4, 'hours')) but the chart data is still in the same position.
How do I shift the charted data forward 4 hours?
Limited scope to change global variables as this will impact other charts.
var getChartSeries = function(response, chart_series_data) {
var lines = response.graph_data.lines;
for (var i=0; i<lines.length; i++) {
var series = lines[i];
var dateFormat = graphDateFormat;
if (chartIntraday) dateFormat = 'HH:mm:ss';
var currSeriesData = [];
for (var j=0; j<series.data.length; j++) {
var row = series.data[j];
var yValue = parseFloat(row[1]);
var point = {
x: moment(row[0], dateFormat).add(4, 'hours').valueOf(),
y: yValue,
displayValue: row[3]
};
currSeriesData.push(point);
}
// Set the series name, legend label, and the line identifier
var name = formatLegendLabel(series.display_name, response);
var label = response.label;
if (response.display_name != undefined && response.display_name != '') label = series.display_name + ' : ' + label;
By default chart adjusts extremes to the provided data. To display these few hours before the first point use xAxis.min property.
Live demo: http://jsfiddle.net/kkulig/xqdqooh9/
API reference: https://api.highcharts.com/highcharts/xAxis.min
I am using plotly.js to create charts. Some charts have long text inside legend, so these text make legend unnecessary bigger. I want to give legend fixed width and if a length of text is long, I want it to be wrapped.
I have been trying to do this by manipulating svg element on DOM, but since legend is svg:g tag, I can not really do much. I also try give textLength to <text>, but it does wrap text but it does not make a new line.
Is there a way to give legend fixed width?
Perhaps just adding </br> to the legend text would be an easy solution to get a fixed width?
You could use any text wrapping trick in Javascript to do so, e.g.
for (i = 0; i < data.length; i += 1) {
data[i].name = data[i].name.replace(/([\w\s,]{13,}?)\s?\b/g, "$1</br>");
}
var numbers = [];
var total = 10000;
var i = 0;
var data = [];
for (i = 0; i < total; i++) {
numbers[i] = ((Math.random() + Math.random() + Math.random() + Math.random() + Math.random() + Math.random()) - 3) / 3;
}
data.push({
x: numbers,
name: "that's a histogram with lots of points, it therefore needs lots of text as well",
type: 'histogram',
marker: {color: 'blue'}
});
numbers = [];
for (var i = 0; i < total; i++) {
numbers[i] = 0.25 + ((Math.random() + Math.random() + Math.random() + Math.random() + Math.random() + Math.random()) - 3) / 3;
}
data.push({
x: numbers,
name: "that's yet another histogram with lots of points, it therefore needs lots of text as well, like for example that it was shifted by 0.25",
type: 'histogram',
marker: {color: 'red'}
});
for (i = 0; i < data.length; i += 1) {
data[i].name = data[i].name.replace(/([\w\s,]{13,}?)\s?\b/g, "$1</br>");
}
Plotly.newPlot('myDiv', data, {showlegend: true});
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="myDiv"></div>
Maximilian's answer almost works with the current version of Plotly.js. There doesn't seem to be a property for setting the width of the legend, but you can rig it by inserting breaks (</br>) into the legend text manually. For some reason Plotly seems to mishandle the first break in the text, however; to get around this, you can add a "ghost" break at the beginning of the text. For instance:
// Rudimentary function to break a string on spaces
// attempting to get no more than `maxChars` characters in a line
const breakString = (str, maxChars) => {
if (str.length > maxChars && str.includes(' ')) {
let index = str.lastIndexOf(' ', maxChars);
if (index === -1) index = str.indexOf(' ', maxChars + 1);
return str.substr(0, index) + '</br>' + breakString(str.substr(index + 1), maxChars);
} else {
return str;
}
}
const original_text = "Respondents who think Tenet is Christopher Nolan's most interesting movie";
const final_text = '</br>' + breakString(original_text, 20);
The color symbols next to each legend entry by default are aligned to the center of the corresponding text; with multiline legend text, you'll probably want to change the symbols to be aligned to the top of the text. To do that, add this to your layout:
legend: {
valign: 'top',
}
This works for me using Plotly with javascript.
title: 'first part' + '<br>' + 'Second part'
eg
yaxis2: {
domain: [0.7, 1],
title: 'Sea Surface' + '<br>' + 'Temperature',
showgrid: true,
showline: true,
titlefont: {color: 'blue'},
tickfont: {color: 'blue'},
side: 'right',
anchor: 'free',
position: 0.91
},
I am trying to make a stacked bar chart with NVd3 but unfortunately the x-axis does not show properly. I want to show the year from 1999 to 2014 in the x-axis but this is what i get.
It seems that nvd3 is grouping all the values for the separate years together.
Javascript code
var data1 = [{}],
deaths = 0;
console.log(data1);
d3.csv('data/CENS-R1/ageGroups.csv', function(data) {
for (var i = 0; i < 12; i++) {
// setup a new entry for that age group
data1[i] = {
key: "1",
values: []
}
var v = i * 16;
// go through all the data
for (var j = 0; j < data.length; j++) {
// check if i equals the correct entries
if (0+v <= j && j < 16+v) {
data1[i].key = data[j].Age;
data1[i].values.push(
{
"x": (new Date()).getTime(parseInt(data[j]).Year),
"y": parseInt(data[j].Deaths)
}
);
}
}
}
});
console.log(data1);
nv.addGraph(function() {
var chart = nv.models.multiBarChart();
// format x-axis
chart.xAxis.tickFormat(function(d) { return d3.time.format('%Y')(new Date(d)) });
chart.yAxis
.tickFormat(d3.format(',.1f'));
d3.select('#stacked-chart svg')
.datum(data1)
.transition()
.duration(500)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
CSV file sample
CENS-R1,1,1999,1999,4390
CENS-R1,1,2000,2000,4354
CENS-R1,1,2001,2001,4188
Any help would be appreciated.
So apparently my function for parsing the date was wrong. instead of creating a new date with "x": (new Date()).getTime(parseInt(data[j]).Year) i used the Date.parse() function and that seemed to work.
I am making a chart using jQuery flot (plot)
https://jsfiddle.net/5gtqwkjg/2/
var updateLegendTimeout = null;
var latestPosition = null;
function updateLegend() {
updateLegendTimeout = null;
var pos = latestPosition;
var axes = plot.getAxes();
if (pos.x < axes.xaxis.min || pos.x > axes.xaxis.max || pos.y < axes.yaxis.min || pos.y > axes.yaxis.max) {
return;
}
/*
var o = plot.pointOffset({ x: pos.x, y: -1.25 });
var ctx = plot.getCanvas().getContext("2d");
ctx.beginPath();
ctx.moveTo(o.left, o.top);
o.top = 0;
ctx.lineTo(o.left, o.top);
ctx.stroke();
*/
var i, j, dataset = plot.getData();
var halfDist = (dataset[0].data[1][0] - dataset[0].data[0][0]) / 2;
for (i = 0; i < dataset.length; ++i) {
var series = dataset[i];
// Find the nearest points, x-wise
for (j = 0; j < series.data.length; ++j) {
if (series.data[j][0] - halfDist > pos.x) {
break;
}
}
// Now Interpolate
var y,
p1 = series.data[j - 1],
p2 = series.data[j];
if (p1 == null) y = p2[1];
else if (p2 == null) y = p1[1];
else y = p1[1];
legends.eq(i).text(series.label.replace(/=.*/, "= " + y.toFixed(2)));
//dataset[i].highlightColor = "#abcdef";
//plot.highlight(dataset[0].series, dataset[0].datapoint);
}
}
$("#placeholder").bind("plothover", function (event, pos, item) {
latestPosition = pos;
if (!updateLegendTimeout) {
updateLegendTimeout = setTimeout(updateLegend, 50);
}
});
I want to add in a functionality that when the user moves the mouse along the x-axis the dot will highlight to indicate what point they are hovering nearest to. I already have the legend reflect the values but how would I highlight the dots?
EDIT: Very helpful answers guys! Here is the finished result if anyone is interested
https://jsfiddle.net/5gtqwkjg/4/
You can make use of the highlight and unhighlight functions provided by Flot.
highlight(series, datapoint)
Highlight a specific datapoint in the data series. You can either
specify the actual objects, e.g. if you got them from a "plotclick"
event, or you can specify the indices, e.g. highlight(1, 3) to
highlight the fourth point in the second series (remember, zero-based
indexing).
unhighlight(series, datapoint) or unhighlight()
Remove the highlighting of the point, same parameters as highlight.
If you call unhighlight with no parameters, e.g. as
plot.unhighlight(), all current highlights are removed.
See https://github.com/flot/flot/blob/master/API.md#plot-methods for reference.
Applying that logic to your question, I think I managed to create the desired result you were looking for.
I first start by unhighlighting everything, just to make sure nothing slips past us when we do highlight points.
for (i = 0; i < dataset.length; ++i) {
plot.unhighlight(); // Unhighlight everything!
var series = dataset[i];
Next up we go do the fun part, highlight all the points! (Just the ones we actually want to highlight)
In your "Find the nearest points, x-wise" loop I added another loop!
for (j = 0; j < series.data.length; ++j) {
if (series.data[j][0] - halfDist > pos.x) {
for(a = 0; a < dataset.length; a++) { // <-- The added loop
// You might want to optimize the way this is done
// The way you were storing the series data didn't seem to work like I..
// ..wanted it do, so I had to iterate the dataset variable again.
// The yellow line won't highlight if you change dataset[a] to series.
plot.highlight(dataset[a], series.data[j][0]);
}
break;
}
}
The result https://jsfiddle.net/qj3068zn/6/, for ease of use.
Do note, none of this is optimized. You're probably better off restructuring your code to provide a more general way to approach this and increase reusability and readability.
Using the highlight() function like Michel de Nijs in his answer, but a simpler version:
1) Put the plot.unhighlight(); at the start of your updateLegend function (you might also want to rename that since it not longer only updates the legend).
2) Add plot.highlight(i, j-1); after your for (j ...) loop.
See this fiddle for the code.
I've implemented the HighStock chart shown at:
http://www.highcharts.com/stock/demo/compare
This chart shows percent change from the first, or left-most, data point. I've got some stock analysts telling me this isn't of much use, and that they would rather see percent change from the previous data point, or in other words, if I was looking at "today" on the chart, I would want to see percent change from yesterday. I've looked over the highstock API and I can't seem to find a way to define the percent change function. Is there a proper way to have highstock do this or should I use a highcharts line chart to accomplish this?
I had to do something similar in highcharts before. I wanted to get the actual change from one datapoint to the next.
What I do, is I take a look at the current point (this.x) and I find it's position in the series array. From there, I easily find the index of the previous point (index = a - 1) and do the math. Hope this helps :)
EDIT:
Looked into the stockcharts API and figured it out for that product:
tooltip: {
formatter: function() {
var s = '<b>'+ Highcharts.dateFormat('%A, %b %e, %Y', this.x) +'</b><br>';
$.each(this.points, function(i, point) {
s += '<span style="font-weight: bold; font-size: 1.2em; color: ' + point.point.series.color + ';">' + point.point.series.name + '</span>:<span style="font-weight: bold; font-size: 1.2em;">' + point.y + '</span>' ;
s += '<span style="font-size: .8em;">';
var target = this.x;
index = 0;
for (var a = 0; a < point.series.points.length; a++) {
if (point.series.points[a].x == target)
{
index = a - 1;
}
}
var delta = 0;
var change = 0;
if (index >= 0)
{
delta = this.y - point.series.points[index].y;
change = (delta / point.series.points[index].y) * 100;
}
s += Math.round(change,2) + '%</span>';
s +='</span><br>';
});
return s;
}
},
And a working jsfiddle: http://jsfiddle.net/nLjsc/2/