Using dc.js, I made an ordinal bar chart with numerical values. I chose ordinal chart because I wanted to add a x-tick for 'unknown'.
The problem is with y-axis. I cannot figure out to fix the axis labeling / scaling. As the picture shows, it alternates between [00, 20, 40, 60, 80]. I would like it to have a full range. I tried to manually insert y ticks, but there doesn't seem to be an accessor method for y-axis. Below is my code and the capture.
ghg2Chart /* dc.barChart('#volume-month-chart', 'chartGroup') */
.width(FULL_CHART_WIDTH)
.height(FULL_CHART_HEIGHT)
.margins({top: 10, right: 20, bottom: 40, left: 20})
.dimension(GHG2)
.group(GHG2Group)
.elasticY(true)
.centerBar(true)
.gap(5)
.round(dc.round.floor)
.alwaysUseRounding(true)
.x(d3.scale.ordinal().domain(GHG2bins))
.xUnits(dc.units.ordinal)
.renderHorizontalGridLines(true)
//Customize the filter displayed in the control span
.filterPrinter(function (filters) {
var filter = filters[0], s = '';
s += numberFormat(filter[0]) + ' -> ' + numberFormat(filter[1]);
return s;
})
.filterHandler(function (dimension, filter) {
var selectedRange = ghg2Chart.filter();
dimension.filter(function (d) {
var range;
var match = false;
// Iterate over each selected range and return if 'd' is within the range.
for (var i = 0; i < filter.length; i++) {
range = filter[i];
if (d >= range - .1 && d <= range) {
match = true;
break;
}
}
return selectedRange != null ? match : true;
});
return filter;
});
The only problem with your chart is that there is not enough space for the ticks, so the labels are getting clipped.
The default left margin is 30 pixels:
https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.marginMixin+margins
Try increasing the margin. The lazy way is:
chart.margins().left = 40;
But that's sort of confusing, so the more common way is
chart.margins({top: 10, right: 50, bottom: 30, left: 40});
It would be awesome for dc.js to do this automatically:
https://github.com/dc-js/dc.js/issues/713
but doing layout based on text sizes is rather difficult, so for now all this stuff is still manual (affects legends, axis labels, etc.)
Related
I'm working on a highcharts solution which includes several different graph types in one single chart. Is it possible to have the exact time of mouse position displayed in the tooltip instead of a calculated range? (We're using highstock together with the boost and xrange module)
Also i need to always show the newest value of a series left from cursor position. Due to having an xRange Series i needed to refresh the tooltip with my own implementation since stickytracking doesnt work with xrange.
But right now the display of the green temperature series constantly switches from the actual value to the first value in the series (e.g when hovering around March 11th it constantly changes from 19.85°C back to 19.68°C which is the very first entry)
So i'm having 2 issues:
displaying the exact time in tooltip
displaying specific values in tooltip
I guess both could be solved with having the exact x position of cursor and for displaying the values i guess i did somewhat the right thing already with refreshing the tooltip on mousemove. Still the values won't always display properly.
I understand that Highcharts makes a best guess on the tooltip time value by displaying the range but to me it seems like it orients itself around the xRange Series.
I already tries to tinker around with the plotoptions.series.stickyTracking and tooltip.snap values but this doesn't really help me at all.
I understand too that this.x in tooltip formatter function will be bound to the closest point. Still i need it to use the current mouse position.
In a first attempt i was filtering through the series in the tooltip formatter itself before i changed to calculating the points on the mousemove event. But there i also couldn't get the right values since x was a rough estimate anyways.
Is there any solution to that?
At the moment i'm using following function onMouseMove to refresh the tooltip:
chart.container.addEventListener('mousemove', function(e) {
const xValue = chart.xAxis[0].toValue(chart.pointer.normalize(e).chartX);
const points = [];
chart.series.filter(s => s.type === "xrange").forEach(s => {
s.points.forEach(p => {
const { x, x2 } = p;
if (xValue >= x && xValue <= x2) points.push(p);
})
})
chart.series.filter(s => s.type !== "xrange").forEach(s => {
const point = s.points.reverse().find(p => p.x <= xValue);
if(point) points.push(point);
})
if (points.length) chart.tooltip.refresh(points, chart.pointer.normalize(e));
})
also i'm using this tooltip configuration and formatter:
tooltip: {
shared: true,
followPointer: true,
backgroundColor: "#FFF",
borderColor: "#AAAAAA",
borderRadius: 5,
shadow: false,
useHTML: true,
formatter: function(){
const header = createHeader(this.x)
return `
<table>
${header}
</table>
`
}
},
const createHeader = x => {
const headerFormat = '%d.%m.%Y, %H:%M Uhr';
const dateWithOffSet = x - new Date(x).getTimezoneOffset() * 60 * 1000;
return `<tr><th colspan="2" style="text-align: left;">${Highcharts.dateFormat(headerFormat, dateWithOffSet)}</th></tr>`
}
See following jsFiddle for my current state (just remove formatter function to see the second issue in action): jsFiddle
(including the boost module throws a script error in jsFiddle. Don't know if this is important so i disabled it for now)
finally found a solution to have access to mouse position in my tooltip:
extending Highcharts with a module (kudos to Torstein Hønsi):
(function(H) {
H.Tooltip.prototype.getAnchor = function(points, mouseEvent) {
var ret,
chart = this.chart,
inverted = chart.inverted,
plotTop = chart.plotTop,
plotLeft = chart.plotLeft,
plotX = 0,
plotY = 0,
yAxis,
xAxis;
points = H.splat(points);
// Pie uses a special tooltipPos
ret = points[0].tooltipPos;
// When tooltip follows mouse, relate the position to the mouse
if (this.followPointer && mouseEvent) {
if (mouseEvent.chartX === undefined) {
mouseEvent = chart.pointer.normalize(mouseEvent);
}
ret = [
mouseEvent.chartX - chart.plotLeft,
mouseEvent.chartY - plotTop
];
}
// When shared, use the average position
if (!ret) {
H.each(points, function(point) {
yAxis = point.series.yAxis;
xAxis = point.series.xAxis;
plotX += point.plotX + (!inverted && xAxis ? xAxis.left - plotLeft : 0);
plotY += (point.plotLow ? (point.plotLow + point.plotHigh) / 2 : point.plotY) +
(!inverted && yAxis ? yAxis.top - plotTop : 0); // #1151
});
plotX /= points.length;
plotY /= points.length;
ret = [
inverted ? chart.plotWidth - plotY : plotX,
this.shared && !inverted && points.length > 1 && mouseEvent ?
mouseEvent.chartY - plotTop : // place shared tooltip next to the mouse (#424)
inverted ? chart.plotHeight - plotX : plotY
];
}
// Add your event to Tooltip instances
this.event = mouseEvent;
return H.map(ret, Math.round);
}
})(Highcharts)
http://jsfiddle.net/2h951hdj/
Also you can wrap dragStart on the pointer and get exactly mouse position, in this case when you click on the chart area you will have the mouse position on the x-axis.
(function(H) {
H.wrap(H.Pointer.prototype, 'dragStart', function(proceed, e) {
let chart = this.chart;
chart.mouseIsDown = e.type;
chart.cancelClick = false;
chart.mouseDownX = this.mouseDownX = e.chartX;
chart.mouseDownY = this.mouseDownY = e.chartY;
chart.isZoomedByDrag = true;
console.log(chart.mouseDownX);
});
}(Highcharts));
Highcharts.chart('container', {
chart: {
events: {
load: function() {
let chart = this,
tooltip = chart.tooltip;
console.log(tooltip);
}
}
},
series: [{
data: [2, 5, 2, 3, 6, 5]
}],
});
Live demo: https://jsfiddle.net/BlackLabel/1b8rf9hc/
How to do this? In highchart ..
horizontal bar with gradient.. red to gray for negative to positive values
If you use already a Highcharts module which uses a color axis, you can use its tweenColors function to find the intermediate color.
As was mentioned, you have to loop through the data and set the point's color. If you don't want to use any other module, you need to find intermediate colors - answer can be found here.
var data = [100, 50, 20, -10],
min = Math.min.apply(null, data),
max = Math.max.apply(null, data),
total = max - min,
colorMin = new Highcharts.Color('rgb(255, 0, 0)'),
colorMax = new Highcharts.Color('white'),
tweenColors = Highcharts.ColorAxis.prototype.tweenColors;
coloredData = data.map(value => {
var pos = (value - min) / total;
return {
y: value,
color: tweenColors(colorMin, colorMax, pos)
};
});
example: http://jsfiddle.net/oudktg0o/
I'm working with crossfilter and dc.js and I want to make something like this :
So, after creating the dimension and the group:
var Time1 = ndx.dimension(function(d) { return d.Dep_Time1; });
var FlightsByTime1 = Time1.group();
I tried to draw a barchart just like this :
var Time1Chart = dc.barChart("#Time1-chart");
Here is its definiton :
Time1Chart
.height(160)
.transitionDuration(1000)
.dimension(Time1)
.group(FlightsByTime1)
.brushOn(true)
.margins({top: 10, right: 50, bottom: 30, left: 50})
.centerBar(false)
.gap(1)
.elasticY(true)
.x(d3.scale.ordinal().domain(Time1))
.xUnits(dc.units.ordinal)
.renderHorizontalGridLines(true)
.renderVerticalGridLines(true)
.ordering(function(d){return d.value;})
.yAxis().tickFormat(d3.format("f"));
And I get this result :
The problems :
. As you can see, in axis values I have a lot of data ... :s
. I don't have the brush, despite brushOn(true)
. I want to segment the axis like in the example, 2 hours by 2 hours
Please who can help me tho achieve the above ?
1. First we should create the crossfilter instance.
var ndx = crossfilter(dataSet);
2. Then we should create the dimension.
var Time1 = ndx.dimension(function(d) { return d.Dep_Time1.getHours() + d.Dep_Time1.getMinutes() / 60; });
3. Then we should group the result.
var FlightsByTime1 = Time1.group(Math.floor);
4. Then we create the chart.
Time1Chart = dc.barChart("#Time1-chart");
5. Finally give the definiton of the chart
Time1Chart
.height(133)
.transitionDuration(1000)
.dimension(Time1)
.group(FlightsByTime1)
.brushOn(true)
.margins({top: 10, right: 50, bottom: 30, left: 50})
.centerBar(false)
.gap(1)
.elasticY(true)
.x(d3.scale.linear()
.domain([0, 24])
.rangeRound([0, 10 * 24]))
.renderHorizontalGridLines(true)
.renderVerticalGridLines(true)
.ordering(function(d){return d.value;})
.yAxis().tickFormat(d3.format("f"));
I created a realtime (updates every 10ms) vertical spline chart using Flot. The chart can be seen here on Codepen. I included the Flot multiple threshold plugin, but I would like for the thresholds to use the x-axis values (on the bottom of the vertical chart) and not the y-axis values (left of the chart). The plot would then paint all values outside of the dashed black lines in red.
In the example you can see that the thresholds use the y-axis to color thresholds (in my case, all values below constraintMax, which is 60).
The operative lines of code are where I set up the options (line 79 in the update function):
var options = {
xaxis: {
position: 'bottom',
min: -10,
max: 100
},
yaxis: {
position: 'left',
min: iterator,
max: updatedData.length-1+iterator,
transform: function (v) { return -v; },
inverseTransform: function (v) { return -v; }
}
};
Where I set up the constraints (line 66 in the update function):
var constraintMax = {
threshold: 60,
color: "rgb(255,0,0)",
evaluate : function(y,threshold){ return y < threshold; }
}
var constraintMin = {
threshold: 25,
color: "rgb(255,0,0)",
evaluate : function(y,threshold){ return y < threshold; }
}
And where I actually plot (line 93 in the update function):
$.plot("#"+elementID, [{data: updatedData, constraints: [constraintMin, constraintMax]}, {data: initialMinData, color: "#000000", dashes: { show: true }}, {data: initialMaxData, color: "#000000", dashes: { show: true }}], options);
Does anyone have any ideas on how to paint the plot points that are outside of the dashed lines red? Thank you in advance.
The multiple threshold plugin only support y-value thresholds out of the box. Therefore you have to change it for your plot. I copied the code into a jsfiddle (I don't like codepen) and changed it there.
1) Your constraintMax threshold is wrong for what you want to do, you need return y > threshold.
2) Changes in the multiple threshold plugin:
if (evaluate(currentPoint[1], threshold)) {
v
if (evaluate(currentPoint[0], threshold)) {
and
function _getPointOnThreshold(threshold, prevP, currP) {
var currentX = currP[0];
var currentY = currP[1];
var prevX = prevP[0];
var prevY = prevP[1];
var slope = (threshold - currentX) / (prevX - currentX);
var yOnConstraintLine = slope * (prevY - currentY) + currentY;
return [threshold, yOnConstraintLine];
}
See the fiddle for the working example.
I have the following bubble chart coded with dc.js which is built upon d3.js.
All is good, but for some reason I cannot see the tick marks. When I inspect the DOM I can see that they are present:
<line y2="6" x2="0"></line>
And I have applied CSS styles to them, but still they do not show!
#referrals-bubble-chart .axis .tick line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
I even added a stroke-width of 2px and still nothing shows! I know I'm targeting the right elements in my CSS because when I give it a stroke width of 10px and hover (Chrome Inspector), I see that the line is now 10px wide.
Why is this happening? Chart Code is below:
// define the referrals bubble chart attributes
referralsChart
.width(700)
.height(400)
.transitionDuration(1500) // (optional) define chart transition duration, :default = 750
.margins({top: 10, right: 50, bottom: 40, left: 50})
.dimension(diagnosisDimension)
//Bubble chart expect the groups are reduced to multiple values which would then be used
//to generate x, y, and radius for each key (bubble) in the group
.group(diagnosisDimensionGroup)
.colors(colorbrewer.RdYlGn[9]) // (optional) define color function or array for bubbles
.colorDomain([0, 100]) //(optional) define color domain to match your data domain if you want to bind data or color
.colorAccessor(function (d) {
// color - mapped to internal scale
return d.value.cost % 100;
})
.keyAccessor(function (p) {
// x-axis
return p.value.avgrtt / p.value.referrals;
})
.valueAccessor(function (p) {
// y-axis
return p.value.cost / 1000;
})
.radiusValueAccessor(function (p) {
// radius size - default is [0, 100]
return p.value.referrals;
})
.maxBubbleRelativeSize(0.1)
// .x(d3.scale.linear().domain([0, 5000]))
.x(d3.scale.linear().domain([1, 15]))
.y(d3.scale.linear().domain([1000, 10000]))
.r(d3.scale.linear().domain([0, 4000]))
//##### Elastic Scaling
//`.elasticX` and `.elasticX` determine whether the chart should rescale each axis to fit data.
//The `.yAxisPadding` and `.xAxisPadding` add padding to data above and below their max values in the same unit domains as the Accessors.
.elasticY(true)
.elasticX(false)
.yAxisPadding(200)
.xAxisLabel('Average Waiting Time - (weeks)') // (optional) render an axis label below the x axis
.yAxisLabel('Cost - (£1K)') // (optional) render a vertical axis lable left of the y axis
//#### Labels and Titles
//Labels are displaed on the chart for each bubble. Titles displayed on mouseover.
.renderLabel(true) // (optional) whether chart should render labels, :default = true
.label(function (p) {
return p.key;
})
.renderTitle(true) // (optional) whether chart should render titles, :default = false
.title(function (p) {
return [p.key,
"Referrals: " + p.value.referrals,
"Cost: £" + p.value.cost,
"RTT: " + p.value.avgrtt / p.value.referrals + " weeks"]
.join("\n");
})
//#### Customize Axis
//Set a custom tick format. Note `.yAxis()` returns an axis object, so any additional method chaining applies to the axis, not the chart.
.yAxis().tickFormat(function (v) {
return v;
});
As mentioned in the comments, it's hard to help you without a complete example, but this works for me. Since I don't have your data I made my own very simple data and adjusted a few things on my bubble chart.
var data = [];
for (var i = 1; i < 10; i++) {
data.push({
val: i
});
}
var ndx = crossfilter(data);
var dim = ndx.dimension(function(d) {
return d.val;
});
var group = dim.group().reduceSum(function(d) {
return d.val;
});
bubbleChart = dc.bubbleChart("#bubbleChart");
bubbleChart
.width(700)
.height(400)
.transitionDuration(1500)
.margins({top: 10, right: 50, bottom: 40, left: 50})
.dimension(dim)
.group(group)
.keyAccessor(function (p) {
return p.value;
})
.valueAccessor(function (p) {
return p.value;
})
.maxBubbleRelativeSize(0.1)
.x(d3.scale.linear().domain([-1, 10]))
.y(d3.scale.linear().domain([0, 10]))
.radiusValueAccessor(function (p) {
return p.value;
})
.r(d3.scale.linear().domain([0, 100]))
.elasticY(true)
.elasticX(false)
.yAxisPadding(200)
.xAxisLabel('Average Waiting Time - (weeks)')
.yAxisLabel('Cost - (£1K)')
.renderLabel(true)
.label(function (p) {
return p.key;
})
.renderTitle(true)
.title(function (p) {
return "This is the title";
})
.yAxis().tickFormat(function (v) {
return v;
});
dc.renderAll();