change axis color dimple - d3.js

I have a very simple line chart in dimple. I want to change the x and y axis colour to white.
var svg = dimple.newSvg(".line_chart_container", 400, 300),dataset;
var chart = new dimple.chart(svg, dataset);
var x = chart.addCategoryAxis("x", "Attempts");
//x.style ("fill","red")
var y = chart.addMeasureAxis("y", "Value");
y.showGridlines = true;
x.showGridlines = true;
var s = chart.addSeries(["Metric", "Value"], dimple.plot.bubble);
var lines = chart.addSeries("Metric", dimple.plot.line);
lines.lineWeight = 2;
lines.lineMarkers = true;
chart.assignColor("Metric", "#30D630");
chart.draw();
s.shapes.style("opacity", function (d) {
return (d.yValue === 0 ? 0 : 0.8);
});
I've checked dimple.axis documentation in GitHub but couldn't find any thing. There is a dimple.axis.colors attribute, but it changes the color of the data and not the axis. Does dimple even support this?
I've also tried to add style attribute(like in D3):
x.style ("fill","red")
but caught an error: Uncaught TypeError: x.style is not a function
Any idea?

x is not a d3 selection, it is a dimple.axis. You can access the inner d3 selection with the shapes property (which is standard for any dimple object). There is an example of that here.
Depending on if you want to change the line color, text color, or everything, you would do
x.shapes.selectAll("*").style("fill", "white")
where * could also be "text" or "line".
One note : the individual tick marks are <line> nodes, and to change their color you need to use 'stroke', not 'fill'.
Also, the actual axis line itself is not a <line> element, it's a <path> element. So to change that color would be :
x.shapes.select("path.dimple-custom-axis-line").style("stroke", "white");
You can also just manually write a css rule to override the style for a chart :
g.dimple-axis > g.tick > line, g.dimple-axis path.dimple-custom-axis-line {
stroke:white;
}

For X-axis
d3.selectAll("path.domain")[0][0].style.stroke = "red";
For Y-axis
d3.selectAll("path.domain")[0][1].style.stroke = "yellow";

Related

Adding a horizontal baseline to a SeriesChart in dc.js

So I have a SeriesChart with this code:
'use strict';
var sectorChart = new dc.SeriesChart('#test-chart');
d3.json('php/query.php?option=sectors').then(data => {
const dateFormatSpecifier = '%Y-%m-%d';
const dateFormat = d3.timeFormat(dateFormatSpecifier);
const dateFormatParser = d3.timeParse(dateFormatSpecifier);
const numberFormat = d3.format('.2f');
var minDate = new Date();
var maxDate = new Date();
var minRatio = .5
var maxRatio = .5
data.forEach(d => {
d.dd = dateFormatParser(d.date);
d.ratio = +d.ratio; // coerce to number
if (d.dd < minDate ) minDate = d.dd;
if (d.dd > maxDate ) maxDate = d.dd;
if (d.ratio < minRatio ) minRatio = d.ratio
if (d.ratio > maxRatio ) maxRatio = d.ratio
});
const ndx = crossfilter(data);
const all = ndx.groupAll();
// Dimension is an array of sector and date.
// Later we'll take the date for the y-axis
// and the sector for the labels
const dateDimension = ndx.dimension(d => [d.name, d.dd]);
// group by the average, the value we'll plot
var avgGroup = dateDimension.group().reduceSum(d => d.average);
sectorChart /* dc.lineChart('#monthly-move-chart', 'chartGroup') */
.width(990)
.height(500)
.chart(c => new dc.LineChart(c))
.x(d3.scaleTime().domain([minDate, maxDate]))
.y(d3.scaleLinear().domain([minRatio, maxRatio]))
.margins({top: 30, right: 10, bottom: 20, left: 40})
.brushOn(false)
.yAxisLabel("ratio avg 10")
.renderHorizontalGridLines(true)
.dimension(dateDimension)
.group(avgGroup)
.seriesAccessor(d => d.key[0])
.keyAccessor(d => d.key[1])
.valueAccessor(d => +d.value)
.legend(dc.legend().x(450).y(15).itemHeight(13).gap(5).horizontal(1).legendWidth(340).autoItemWidth(true));
dc.renderAll();
});
It works all right, but now I would like to add a black horizontal line at 0.5 value. I guess I could modify the query to return a fictious "name" to the dataset with all values at 0.5, but that won't allow me to control the color, and anyway I would like to know if there is a better way, not to mess with the returned data.
Edit: According to Gordon's info, I have transposed as good as I could the vertical line to a horizontal line. It has worked, with some eccentricities. One, the line starts at the left side of the viewport, not on the y axis as it should. Two, and more mysterious, the line is drawn at 0.51 instead of 0.50. I have created a fiddle if anybody wants to play with it.
Fiddle
Based on Gordon's solution I have written a function, with the idea not to burden the chart's definition with all that code. Not fully happy with it, particularly the use of random, but anyways, the world keeps turning and other things to do. In any case, I leave it here in case somebody find it useful.
/**
* Draws a fixed line horizontally or vertically in a dc.js chart
* To be used in the "pretransition" event
* #example
* .on('pretransition', function (chart) {
* dcUtilFixedLine(chart, true, 0.5, [{attr: 'stroke', value: 'red'}] )
* }
* #param (Chart} [chart] The chart provided by the triggered event
* #param (Boolean) [horizontal] True if the line is horizontal, false if vertical
* #param (Object) [value] Value of the point where the line is to be inserted,
* usually the type of the dimension if vertical or group if horizontal
* #param (Array) [attributes] Array of Objects with attr/value pairs)
* giving the attributes added to the line.
* It defaults to stroke = black, stroke-width = 1.
*/
function dcUtilFixedLine(chart, horizontal, value, attributes ) {
if (horizontal) {
var extra_data = [
{y: chart.y()(value) + chart.margins().top, x: chart.margins().left},
{y: chart.y()(value) + chart.margins().top, x: chart.margins().left + chart.effectiveWidth()}
]
} else {
var extra_data = [
{x: chart.x()(value) + chart.margins().left, y: chart.margins().top},
{x: chart.x()(value) + chart.margins().left, y: chart.margins().top + chart.effectiveHeight()}
]
}
var addedPath = 'extraLine' + Math.random().toString().substr(2, 8)
var line = d3.line().x(d => d.x ).y(d => d.y );
var chartBody = chart.select('g');
var path = chartBody.selectAll('path.'+addedPath).data([extra_data]);
path = path.enter()
.append('path')
.attr('class', addedPath)
.attr('id', 'oeLine')
.attr('stroke', 'black')
.merge(path);
attributes.forEach( attribute => path.attr(attribute.attr, attribute.value))
path.attr('d', line);
}
And forked and added it to the new fiddle
Although you can add artificial data series in order to display extra lines, it's often easier to "escape to D3", especially if the lines are static and don't move.
The row vertical line example shows how to do this.
The basic outline is:
use the pretransition event to draw something each time the chart is updated
create two x/y points of data to draw, using the chart's scales and margins to determine pixel coordinates
select a path with a unique class name (here .extra) and join the data to it
For a horizontal line, it could look like this:
.on('pretransition', function(chart) {
var y_horiz = 0.5;
var extra_data = [
{y: chart.y()(y_horiz) + chart.margins().top, x: chart.margins().left},
{y: chart.y()(y_horiz) + chart.margins().top, x: chart.margins().left + chart.effectiveWidth()}
];
var line = d3.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; });
var chartBody = chart.select('g');
var path = chartBody.selectAll('path.extra').data([extra_data]);
path = path.enter()
.append('path')
.attr('class', 'extra')
.attr('stroke', 'black')
.attr('id', 'oeLine')
.attr("stroke-width", 1)
.merge(path);
path.attr('d', line);
});
Each X coordinate is offset by chart.margins().left, each Y coordinate by chart.margins().top.
The width of the chart in pixels is chart.effectiveWidth() and the height in pixels is chart.effectiveHeight(). (These are just the .width() and .height() with margins subtracted.)
Fork of your fiddle
(Note: the example apparently has a typo, using oeExtra instead of extra in the join. This could cause multiple lines to be drawn when the chart is updated. The class in the select should match the class in the join.)

How to calculate SVG Linear Gradient

If I have an upward sloping straight line is it possible to set the gradient fill such that it is red when the curved line is below the straight line and blue when above. Below are the two line functions and the d3 area function generator and their associated arrays.
//global Variables:
var trendGrowthX =[0,1,2,3,4,5,6,7,8,9,10];
var trendGrowthY = [2,2.5,3,3.5,4,4.5,5,5.5,6,6.5,7];
var actualX = [0,1,2,3,4,5,6,7,8,9,10];
var actualY = [2,4.3,5.5,3.5,2,3,5.5,7.5,6.5,5.5,5];
//D3 path generator functions:
function addTrend(){
var trendLine = d3.line()
.x(function(d,i,a){return xScale(trendGrowthX[i]);})
.y(function(d,i,a){return xScale(trendGrowthY[i]);})
}
function addActual(){
var actualIncome = d3.line()
.x(function(d,i,a){return xScale(actualX[i])})
.y(function(d,i,a){return yScale(actualY[i])})
.curve(d3.curveCatmullRom.alpha(0.5))
g.append("path").attr("d",actualIncome(actualX))
.style("stroke","green")
.style("stroke-width",3)
}
function addArea(){
var area = d3.area()
.attr("x1",function(d,i,a){return xScale(actualX[i]); })
.attr("y1",function(d,i,a){return xScale(actualY[i]); })
.attr("y0",function(d,i,a){return xScale(trendGrwothY[i]); })
.attr("x0",function(d,i,a){return xScale(trendGrowthX[i]); })
g.append("path")
.attr("d",function(d,i,a){ area(actualX)})
.style("fill","url(#area-gradient)")
.attr("pointer-events","none")
}
SVG has a transformGradient property to allow you to manipulate the linear gradient using standard transform instructions, solution is below:
var gradient = d3.select("#area- gradient").attr("gradientTransform",`rotate(-18),${xScale(trendGrowthX[5]),${yScale(trendGrowthY[5])})`);

How to highlight the axis?

I have many axes in the chart.
How to highlight the axis when hovering on a line(axis-value)?
It is necessary to highlight the axis to which the line(axis-value) belongs (when hovering on a line(axis-value))
(Highlight = make bold or change color)
Sorry for the bad english :)
You could use the seriesHover event:
seriesHover: function(e) {
var axis = e.sender.getAxis( e.series.axis);
for (var i=0; i<e.sender.options.valueAxis.length; i++){
if (i ==axis._axis.axisIndex){
e.sender.options.valueAxis[i].line.width = 3;
} else {
e.sender.options.valueAxis[i].line.width = 1;
}
}
e.sender.refresh();
}
From the series you can get theassociated axis, then set the axis line width and refresh the chart.
DEMO

Getting graph coordinates from mouse in nvd3

I'm trying to see where in the chart the user clicked. The below code almost works but it is offset by some amount. I suspect I need to handle the click relative to the charting area and not take the axis into account. What is the proper way to do this?
d3.select('#chart1 svg')
.datum(chartData)
.on("click", mouseClick )
.call(chart);
...
function mouseClick()
{
var coordinates = d3.mouse(this);
var x = chart.lines.xScale().invert(coordinates[0]);
var y = chart.lines.yScale().invert(coordinates[1]);
console.log(x+','+y);
}
You just have to subtract the margin:
function mouseClick()
{
var coordinates = d3.mouse(this);
var x = chart.lines.xScale().invert(coordinates[0]-chart.margin().left);
var y = chart.lines.yScale().invert(coordinates[1]-chart.margin().top);
}

Adding gradient text in createjs

I am currently looking for a way to add a gradient to my text object that was generated with textjs.
this.text = new createjs.Text(this.val, this.font, "#efa146");
this.text.textAlign = "center";
this.text.y = this.pos.y;
this.text.x = this.pos.x;
console.log(this.text);
window.stage.addChild(this.text);
It's actually very easy, just extend the Text object like this
<script>
(function () {
function GradientText(text, font, color) {
this.Text_constructor(text, font, color);
}
var p = createjs.extend(GradientText, createjs.Text);
p._drawTextLine = function (ctx, text, y) {
if (this.gradient) {
var height = this.getMeasuredLineHeight();
var my_gradient = ctx.createLinearGradient(0, y, 0, y + height);
my_gradient.addColorStop(0, "black");
my_gradient.addColorStop(1, "white");
ctx.fillStyle = my_gradient;
ctx.fillText(text, 0, y, this.maxWidth || 0xFFFF);
} else {
this.Text__drawTextLine(ctx, text, y);
}
};
window.GradientText = createjs.promote(GradientText, "Text");
}());
</script>
Now just create an instance of GradientText like this:
var text = new GradientText("Hello world!", "20px Arial", "#ff0000");
text.gradient = true;
You should see a gradient over text when you add it to the stage. Here is the jsfiddle for it: https://jsfiddle.net/r94zmwxa/
P.S. I've just written an example (black and white), but you can change the addColorStop from the this.gradient to set the gradient colors dynamically!
There is currently no way to do this. Sorry. There are plans to add better control on fill and stroke methods for Text in the future.

Resources