d3.js - Show labels conditionally on multi series line chart -
in d3 example of multi series line chart, series labels shown on right of graph. i'm interested in doing changing conditionally show on parts of chart, or not @ all.
so using example, if wanted labels show @ max value on each series , same colour each series (perhaps this), how go achieving that? can figure it'll block
city.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.temperature) + ")"; }) .attr("x", 3) .attr("dy", ".35em") .text(function(d) { return d.name; });
but can't work out. i've tried using indexof or maxof features, can't either work in context. aware might cause overlap on graph, though can deal later. bonus points can show how add in conditional labels lines point max well.
thanks :)
without paying attention readability of chart, 1 solution might first add function:
function find_max_date(max,values) { (i=0;i<values.length;i++) { if (values[i].temperature==max) { return i; break; } } }
and use code:
city.append("text") .datum(function(d) {return {name: d.name, max: d3.max(d.values,function(d){return d.temperature}), max_date: d.values[find_max_date(d3.max(d.values,function(d){return d.temperature}),d.values)].date };}) .attr("transform", function(d) {return "translate(" + x(d.max_date) + "," + y(d.max) + ")"; }) .attr("x", 3) .attr("dy", ".35em") .text(function(d) { return d.name; }).attr("fill",function(d) {return color(d.name);});
so basically, adds max , max_data data-object - max using d3.max-method find highest temperature, , max_data looping through original data per city , returning position of maximum temperature in array, used access corresponding date. color added same way added lines.
edit: not sure "bonus"-question, should result like? lines pointing @ maximum value each city?
edit2: possible solution bonus-question curious if doable, there might better ways it. changed different parts of code, can delete function find_max_date.
at start of script added global variable
var offset=20;
which serves variable control distance of labels between each other , border. then, between cities-color-domain-part , scale-domains, added following code:
for (i=0;i<cities.length;i++) { cities[i].max=0; cities[i].label_off=0; (j=0;j<cities[i].values.length;j++) { if (cities[i].values[j].temperature>cities[i].max) { cities[i].max=cities[i].values[j].temperature; cities[i].max_date=cities[i].values[j].date; } } }
this same did before d3.max , custom function find date of maximum temperature, time suggest add values directly cities-variable. cities.label_off value put space between labels otherwise overlapping.
next, added loop compare values on x-scale check if overlap. put, if distance between 2 x-values smaller 100 pixels, label_off-value set offset-value (in case 20 pixel), otherwise label_off-value remains @ 0. might want adjust that, depending on how short/long labels are.
for (i=0;i<cities.length;i++) { (j=i+1;j<cities.length;j++) { if (math.abs(x(cities[i].max_date)-x(cities[j].max_date))>0 && math.abs(x(cities[i].max_date)-x(cities[j].max_date))<100) cities[j].label_off=offset; } }
next, added offset-variable y-domain, have little more space put labels in place:
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; })+offset; }) ]);
and part add labels , lines. cities-variable contains necessary information don't need use datum
, can adress them using property name:
city.append("text") .attr("transform", function(d) {return "translate(" + (x(d.max_date)+30+d.label_off) + "," + (offset+d.label_off) + ")"; }) .attr("x", 3) .attr("dy", ".35em") .text(function(d) { return d.name; }) .attr("fill",function(d) {return color(d.name);}); city.append("line") .attr("class","line_con") .attr("x1",function(d){return x(d.max_date);}) .attr("y1",function(d){return y(d.max);}) .attr("x2",function(d){return (x(d.max_date)+30+d.label_off);}) .attr("y2",function(d){return (offset+d.label_off);}) .attr("stroke",function(d) {return color(d.name);});
as can see, transform puts labels on x-scale @ position of date of maximum temperature. adds 30, arbitrary value move them right don't sit directly above value. d.label_off adds 0 if there no overlapping, , in our case 20 if there is.
on y-scale positioned them @ top of chart space between them , border using offset. again, if there overlapping, moved down little bit.
for lines, it's connecting maximum value , starting point of label. use different css-class allow correspondning color:
.line_con { fill: none; stroke: none; stroke-width: 1.5px;
again, there might better ways accomplish , i'm not sure if works fine on sorts of data, might give idea how handle it.
Comments
Post a Comment