susielu / d3-legend Goto Github PK
View Code? Open in Web Editor NEWA reusable d3 legend component.
Home Page: http://d3-legend.susielu.com/
License: Apache License 2.0
A reusable d3 legend component.
Home Page: http://d3-legend.susielu.com/
License: Apache License 2.0
Hi, could you please publish a new release? I use bower for dependency management and latest release has some serious bugs (undefined vars) that are already fixed in the master. Thanks!
Problem: Webpack source map loader expects indexRollup.js.map to exist in this library, producing a warning because it is missing.
Solution: Include indexRollup.js.map in the package.json file list and re-publish the npm module.
It is a one line change, if given permission I can make a pull request for it. If you sanction a pull request please let me know any etiquette such as re-generating builds or formatting commit messages that I must adhere to.
Thanks
I believe the 'g.cell' selector here:
https://github.com/susielu/d3-legend/blob/master/src/legend.js#L88
needs to have the class prefix inserted.
Without formatter:
Less than -0.1
More than 0.1
With percentage formatter labelFormat(d3.format('+.1%'))
:
+NaN% to -12.0%
More than +15.0% to +NaN%
This is due to the replacements made in helpers.js relying on the formatter returning exactly NaN
. This would also cause an issue if the default labelDelimiter
is changed from "to".
Workaround: copy and adapt the helper.
E.g. -
var legend = d3.legend.color()
.shapeWidth(70)
.orient('horizontal');
console.log(legend.shapeWidth() === 70); // false
console.log(legend.orient() === 'horizontal'); // false
console.log(legend.shapeWidth() === legend); // true
console.log(legend.orient() === legend); // true
Hi, You did a fantastic job with the legend implementation using d3. Wanted to know if implementation of hierarchical legend in in line?
Hello,
unfortunately cannot include d3-legend into my project. I am using npm, typescript and webpack(with pretty default configurations).
So, when I import d3-legend via import * as legend from 'd3-svg-legend';
, webpack copies lib-file into assets folder instead of including it into the main bundle. And when I log that legend variable, it contains only string-path to file in assets folder, like '/assets/build/assets/f7cef8a85c1c33ef2fdaf592dca074c1.js'.
P.S. I am using a lot of libraries, including d3 itself, and all they import fine.
Hi,
I wanted to reduce the space between the title and the cells but I didn't find the option.
I found here the transform of the cells.
The fix is simple but not very pretty. Do you have an option to do it?
// For exemple, a padding of 7px
svg.select('.legendCells').attr('transform', 'translate(0, 7)')
Thanks
Hi,
I am new on Javascript, may I know how to put the label next to the legend? I see all examples are putting the label under the legend in different ways.
Thank you very much.
Bruce
Currently I have to set a fixed size for the legend to look good on my maps. But the problem is that I don't know the label lengths or number of legend entries in advance, and doing computations for width/height myself seems too complicated. Is this something which can be easily done here? Ideally, the SVG size should actually adjust to its contents. I'm new to D3, maybe this is a more general issue.
First of all, great work @susielu ! I have really enjoyed using your legend.
A useful setting would be a .filter()
or .cull()
method that made it easy to only display legend cells that are in a particular dataset.
I am happy to do this when I can get around to it!
An example implementation might look something like below,
where only the 0th, 1st, and 3rd cells are appended to the legend
var dataset = [0, 150, 500, 250, 500, 250, 500];
var quantize = d3.scale.quantize()
.domain([0, 1000])
.range([0,1,2,3,4]);
var svg = d3.select(this)
.select("svg");
svg.append("g")
.attr("class", "legendQuant");
var legend = d3.legend.color()
.labelFormat(d3.format.si)
.useClass(false)
.filter(_.uniq(dataset)) // _.uniq(dataset) => [0,1,3]
.scale(quantize);
I believe there is a bug in legendColor when passing it updated scales. If I start with scale data like so (omitting quotes):
domain = [a, b, c] and range = [green, blue, gold]
The initial legend renders fine. But if I then call it again with:
domain = [b, c] and range = [blue, gold]
then the first two cells are both blue.
Likewise, updating by progressively dropping the last cell and adding it back with subsequent calls causes the last cell color to repeat.
I am using D3 v4. Looking at the source code, legendColor appears to be updating, exiting, and entering, but it is not updating correctly on the screen, and I can't see where the issue is. I noted though that it does not appear to be using a key function.
This is being done on a closed system, so unfortunately I cannot post the code.
Nice lib!
I've seen scenarios where user wants to highlight legends on visualization. I did an experiment with choropleth. Here's my highlight code:
d3.select(".legendQuant").selectAll(".cell")
.on("mouseover", function() {
var className = d3.select(this).select("rect").attr("class").split(/\s+/)[1];
d3.selectAll("." + className).style("fill", "#bbb");
})
.on("mouseout", function() {
var className = d3.select(this).select("rect").attr("class").split(/\s+/)[1];
d3.selectAll("." + className).style("fill", "");
});
https://vida.io/documents/jspBB83bm6EvEYE3n
It'd be nice to have some support from the lib to turn on highlight.
I tried making a color legend using a d3.scaleThreshold, and here's what the legend looks like:
Here's the relevant bit of code for setting up the scale and the legend:
var colorScale = d3.scaleThreshold()
.domain([ 0, 1000, 2500, 5000, 10000 ])
.range(["#ffffb2","#fed976","#feb24c","#fd8d3c","#f03b20","#bd0026"]);
var legend = d3.legendColor()
.scale(colorScale);
A nice solution to this would:
Hi! I'd like to change the labels (and maybe colors) of a legend after it is created, like this page. Please drag the slider to see the labels of the legend change dynamically.
The gist of the problem is summarized in this short piece.
In the function rescaleLegend() I try two ways of updating the legend. Presently it works by manually picking out the labels [ selectAll('.label') ] and manually changing their text. Obviously this is not in the API and may well break when the internal workings of d3-legend.js changes. The ideal solution would be to change the scale of the legend and somehow re-apply it to the legend, like the commented out statement: [ legendBuilder.scale(createScale(cap)); ] Unfortunately this does not work.
To simplify things, let's assume that the cell number remains the same. If it does not, then it probably makes more sense simply to delete the old legend and create a new one.
I am not even sure how to request for a nice API to do this. Does anybody have similar needs? What would you suggest?
Thanks to susielu and all for this nice library!
Can we have an option to reverse the direction of the legend? For example with a rainfall graph having a vertical legend, it is customary to have larger amount of rainfall on top and lower amount at the bottom. Thanks for the great work!
Is it currently possible to use combinations of commas, decimals, and SI abbreviations in legend text labels? For example a label like "1.3M to 2.52M" for values of 1,300,000 to 2,520,000.
First off, awesome work with this library. It really makes legends super easy!
One issue I ran into while using d3-legend
was namespace clashes. For example, the labels in d3-legend
have the class label
, and if you use it with bootstrap
, they look really weird as the fonts are displayed at 75% of their size.
One way to solve this issue would be to allow uses to specify a prefix for all classes introduced by d3-legend
. It could default to ""
to maintain backward compatibility. This way, one could specify a prefix d3-legend-
and style appropriately, without having to worry about namespace clashes.
I would be happy to contribute a PR if you think this is something you would like to incorporate.
Hi,
It could be usefull to have a locale parameter for thresholdLabels
. It could work the same way than timeFormat. What do you think?
Hi,
First of all: thanks for your amazing legend api.
My wish: can update the API to support the newest version of d3?
Since version 4 some major changes prevent me from using your api.
best regarfs,
jT
This library is awesome! For my purposes, I just want to scale the symbols in my symbol legend so they're a little more visible. I think this is the "ShapeWidth" feature, which is available for color and size legends but not symbol legends?
angular.min.js:108 TypeError: shapes.nodes is not a function
at Array.legend (http://localhost:3000/lib/d3/d3-legend.js:4101:36)
at Array.d3_selectionPrototype.call (http://localhost:3000/lib/d3/d3.js:975:14)
at post (http://localhost:3000/js/directives.js:2505:22)
at ea (http://localhost:3000/lib/angular/angular.min.js:74:16)
at w (http://localhost:3000/lib/angular/angular.min.js:61:340)
at g (http://localhost:3000/lib/angular/angular.min.js:54:250)
at g (http://localhost:3000/lib/angular/angular.min.js:54:267)
at g (http://localhost:3000/lib/angular/angular.min.js:54:267)
at g (http://localhost:3000/lib/angular/angular.min.js:54:267)
at g (http://localhost:3000/lib/angular/angular.min.js:54:267) <div my-legend-info="">
var linear = d3.scale.linear()
.domain([0,10])
.range(["rgb(46, 73, 123)", "rgb(71, 187, 94)"]);
d3.select(iElement[0]).remove("svg");
var svg = d3.select(iElement[0]).append("svg");
svg.append("g")
.attr("class", "legendLinear")
.attr("transform", "translate(20,20)");
var legendLinear = d3.legendColor()
.shapeWidth(30)
.orient('horizontal')
.scale(linear);
svg.select(".legendLinear")
.call(legendLinear);
Hi Susie,
Awesome library. Definitely simplifies my life quite a bit.
There seems to be a bug with the implementation of the d3.legendSize() computations, as per Tufte's data-ink ratio.
If you take the circles example, you can see the computed radius mappings are 5.0->20px and 10.0->30px.
For the purposes of simplifying the mapping, I will assume that the scale should be zeroed (where 0->0px, 5.0->10px, 10.0->20px). According to Tufte's data-ink theory, the entire area of each circle should scale linearly. However, in the rendered example 5.0->10px radius->100 sq px area and 10.0->20px radius->400 sq. px area. As you can see, double the radius is 4x the area. 5.0 should be mapped to 14px radius if the radius of 10.0->20px.
This bug affects rects as well.
Is it possible to add line wrapping for long legend titles?
Hello,
I can't manage to customize shape of my legend using your example.
Here is my HTML: <svg id="d3legend"></svg>
And my Javascript:
var colorPalette = d3.scaleLinear().domain(steps).range(shades);
var legend = d3.legendColor()
.shape("path", d3.symbol().type(d3.symbolTriangle).size(150)) /* doesn't work */
.scale(colorPalette);
var svg = d3.select("#d3legend");
svg.empty();
svg.append("g")
.attr("class", "legend")
.attr("transform", "translate(20, 20)");
svg.select(".legend").call(window.legend);
Hello, I've made my legend, but I want it over the chart, so obviously I need to set a background color?
Can you tell me how can I make this?
Whenever I try to display numbers somewhere between 0 (0.002, 0.02, 0.2, ...) and 1, the tool rounds the numbers to either 0 or 1. Is there a way to not let it do that?
Add a method to set the legend title. This way it can be automatically styled similar to the legend labels. Users could always use the returned object to translate the title if they wish to reposition it.
I'm using d3 v4 with the new release
cell.exit().transition().style("opacity", 0).remove();
throws the error when I do .call(legend)
.
TypeError: cell.exit(...).transition is not a function
Threshold scale domain values indicate the lower bound of every slice except the first. They are inclusive:
var color = d3.scaleThreshold()
.domain([ 0, 1])
.range(["red", "white", "green"]);
color(-1); // "red"
color(-0.5); // "red"
color(0); // "white"
color(0.5); // "white"
color(1); // "green"
Given the above scale and .labels(d3.legendHelpers.thresholdLabels)
, d3-scale will produce the following legend:
RED Less than 0.0
WHITE 0.0 to 1.0
GREEN More than 1.0
... where the last label being "more than" incorrectly suggests that 1.0 would not be green. In other words, the slice is inclusive but its label is exclusive.
It should say "1.0 or more".
"Less than" at the other end of the scale is correct, because 0 is the upper bound of that slice and is therefore exclusive.
Numeraljs library has to be installed by the developer just like d3.
It looks like the wrong colors are used when colorLegend is passed a scale whose range includes more colors than values in the domain.
As an example, the mismatched colors will result when using d3.scaleOrdinal(d3.schemeCategory10)
when only two values in the domain are present.
The npm package doesn’t include the file indexRollupNext.js
, though it’s referenced in the module
and jsnext:main
fields of package.json
.
I get the error "Uncaught TypeError: a.getBBox is not a function" when I try to pass a d3.legend.color the following scale:
d3.scale.linear().domain([0, 0.25353535353535356]).range(["#ffff99", "#ff6600"]);
Hi,
I am running D3 in NodeJS with JSDom so I can render images server side. The d3 chart rendered fine when I copied it from my client-side test, but when I copied the d3-legend code I received this error.
Here is my include:
const fs = require('fs'),
d3 = require('d3'),
d3_legend = require('d3-svg-legend'),
and my legend creation:
svg.append("g")
.attr("class", "legendLinear")
.attr("transform", `translate(80,${padding})`);
const legendLinear = d3_legend.legendColor()
.title("Noise Level (dB)")
.titleWidth(200)
.shapeWidth(40)
.cells(10)
.orient('horizontal')
.scale(colorRange);
svg.select(".legendLinear")
.call(legendLinear); // note, this is line 130
Here is the error I receive when I run my Node process:
return d.getBBox();
^
TypeError: d.getBBox is not a function
at node_modules\d3-svg-legend\indexRollup.js:298:16
at Array.map (native)
at legend (\node_modules\d3-svg-legend\indexRollup.js:297:33)
at Selection.selection_call [as call] (\node_modules\d3-selection\build\d3-selection.js:500:12)
at generateGraph (\ScatterPlot.js:130:8)
at Request._callback (\ScatterPlot.js:29:13)
at Request.self.callback (\node_modules\request\request.js:188:22)
at emitTwo (events.js:106:13)
at Request.emit (events.js:191:7)
at Request. (\node_modules\request\request.js:1171:10)
Thanks,
William
Thanks for such a useful package, I can tell I'm going to use this one a lot!
Would you be able to register d3-legend as a bower package? Many front-end developers (myself included) use bower to manage front-end packages. I have to jump through some kludgy hoops to utilize non-bower packages in my deployment workflow.
Thanks again.
Here's the bower docs for packaging and registering libraries:
Hi Susie,
First, thanks for creating and maintaining this plugin - it truly is great! I wanted to request a new feature that would allow you to create a nested legend for the size scales. An example of what I'm talking about is this circular legend.
It would be nice if you could specify the number of items in the legend, although that might be better suited to using the default .ticks()
on the scale itself.
Thanks for considering!
The doc says:
A value that determines how far the label is from the symbol in each legend item. Default set to 10px.
But I think there is a problem, here are screenshots:
The code used to generate the legend:
legendSize()
.title(legendTitle.charAt(0).toUpperCase() + legendTitle.slice(1))
.scale(reportScale) // a quantitative scale with two values
.shape('line')
.shapePadding(10)
.shapeWidth(2)
.labelOffset(5)
.labelFormat('.0f')
.labelAlign('start')
.labels(({ i, domain }) => domain[i])
.orient('horizontal')
Also I set the font size to 11px in css.
Am I missing something?
Great tool! Alas, when I use it with my log scale that contains a mid point like this
var color = d3.scale.log().range(colors).domain([colorlo, 100, colorhi]);
var legend = d3.legend.color().scale(color).cells(6);
map.call(legend);
The legend that is created runs from colorlo (3) to the midpoint (100), rather than up to colorhi (32004).
Hi Susie, I'm having some trouble with spacing between legend shapes and the title. For the legend in question I'm using:
It looks like it calculates the top spacing off of the top shape, because if I change it to '.ascending(false)' the space decreases (with smaller circle on top).
Is there any way to set the spacing manually via the translate values for the "legendCells"?
Hi~
Adding tags for repo could specify points in history and mark release points.
It's helpful for user to know the version and status of repository.
Would you please consider on using tags?
Thank you!
Piicksarn
If there are too many labels, cells can fall off the edge of the svg. I currently use a wrapper function to postprocess cells and wrap them to the next column/row when necessary. It would be nice to include this as part of d3-legend.
Thanks for the awesome library! Am trying to incorporate it into a project that uses d3 v. 4.2.6, using the CDN at <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.9.0/d3-legend.js"></script>
and am getting the following error:
Uncaught TypeError: d.getBBox is not a function
which points to function at d3-legend/2.9.0/src/color.js:53
. My guess was that perhaps my exact version of d3 v. 4 wasn't right, though on double-checking the project's bower.json
, it looks like the right version should be "d3": "~3.5.6"
, while the README says that the master branch should depend on v4.
Which version of d3 should I be using? If I am using the right version, any thoughts on what might be going?
Thanks!
Hi, thank you so much for this package, helped me a lot!
However, I have a small question, it seems that I'm missing some simple idea about creating custom legend labels. Right now I have labels that look like '40 to 102', but I would like to have something like '≤ 102'.
For example, I have:
var colorScale = d3.scale.quantile()
.domain([d3.min(values), d3.max(values)])
.range(colorbrewer.Blues[8]);
// I thought that I could just map the values to format I wanted:
var newLabels = colorScale.quantiles().map( function(i) {
return ' ≤ ' + i;
});
var legend = d3.legend.color()
.cells(newLabels)
.scale(colorScale);
svg.select(".legendQuant")
.call(legend);
However, when I do this, nothing changes, I still have the same '40 to 102' type of labels. Can you suggest how I can troubleshoot this?
Thank you!
I am using this version of d3 https://d3js.org/d3.v3.min.js. Which version from cdnjs
is the one that is usable with the suggested d3
version? When I use that one https://cdnjs.com/libraries/d3-legend and I try d3.legend.color()
it says that this is not defined. Looking into the code, its d3.legendColor()
. When I go onto this site http://d3-legend-v3.susielu.com/ and then click "Include file directly - All legends" I get directed to this URL https://raw.githubusercontent.com/susielu/d3-legend/master/d3-legend.min.js which again has d3.legendColor()
in it...
Its rather frustrating...
A possibly interesting feature would be to display a legend with a continuous color scale, and a number of ticks for reference. Something akin to this http://bl.ocks.org/mbostock/4573883
Whenever I try to pass an array with labels for a legend, the last item never appears for my pie charts. What results is my pie chart has more values than my legend, despite the data being present in the array.
Hey Susie,
thanks for this cool d3 legend plugin. Unfortunately, when I try to bundle the npm package with webpack. I got the following syntax error:
ERROR in ./~/d3-svg-legend/index.js
Module parse failed: /home/djan/dev/client/node_modules/d3-svg-legend/index.js Line 2: Unexpected token .
You may need an appropriate loader to handle this file type.
| var d3 = require('d3'),
| d3.legend = require('./no-extend')
|
| module.exports = d3;
@ ./app/components/Legend.jsx 19:0-24
Best regards
Jan
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.