initial functional implementation
MODIFY
examples/legend2d/main.js
examples/legend2d/main.js
@@ -43,42 +43,87 @@ $(function () {
// * fill: (bool) Turn on or off the fill
// * fillColor: (string|object) The fill color
// * fillOpacity: (number) The fill opacity
+ // legend.categories([
+ // {
+ // name: 'test1',
+ // type: 'discrete',
+ // scale: {
+ // type: 'ordinal',
+ // domain: ['beijing', 'new york', 'london'],
+ // range: ['red', 'green', 'blue']
+ // }
+ // },
+ // {
+ // name: 'test2',
+ // type: 'discrete',
+ // scale: {
+ // type: 'quantize',
+ // domain: [100, 1000],
+ // range: colorbrewer.YlGnBu["9"]
+ // }
+ // },
+ // {
+ // name: 'test3',
+ // type: 'continuous',
+ // scale: {
+ // type: 'pow',
+ // domain: [100, 10000],
+ // range: ['red', 'blue']
+ // }
+ // },
+ // {
+ // name: 'test4',
+ // type: 'continuous',
+ // scale: {
+ // type: 'log',
+ // domain: [100, 10000],
+ // range: ['purple', 'orange']
+ // }
+ // }
+ // ]);
+
legend.categories([
{
- name: 'test1',
+ name: 'ordinal 1',
type: 'discrete',
- scale: {
- type: 'ordinal',
- domain: ['beijing', 'new york', 'london'],
- range: ['red', 'green', 'blue']
- }
+ scale: 'ordinal',
+ domain: ['beijing', 'new york', 'london', 'paris'],
+ colors: ['red', 'green', 'blue', 'orange']
},
{
- name: 'test2',
+ name: 'discrete countinous 1',
type: 'discrete',
- scale: {
- type: 'quantize',
- domain: [20000, 120000],
- range: colorbrewer.YlGnBu["9"]
- }
+ scale: 'linear',
+ domain: [100, 1000],
+ colors: colorbrewer.YlGnBu["9"]
+ },
+ {
+ name: 'discrete countinous 2',
+ type: 'discrete',
+ scale: 'sqrt',
+ domain: [100, 1000],
+ colors: colorbrewer.PRGn['8']
},
{
- name: 'test3',
+ name: 'discrete countinous 3',
+ type: 'discrete',
+ scale: 'linear',
+ domain: [0.001, 0.00001],
+ colors: colorbrewer.RdBu["8"]
+ },
+ {
+ name: 'continuous continuous 1',
type: 'continuous',
- scale: {
- type: 'pow',
+ scale: 'pow',
domain: [100, 10000],
- range: ['red', 'blue']
- }
+ colors: ['red', 'blue']
},
{
- name: 'test4',
+ name: 'continuous continuous 2',
type: 'continuous',
- scale: {
- type: 'log',
+ scale: 'sqrt',
domain: [100, 10000],
- range: ['purple', 'orange']
- }
+ colors: ['purple', 'orange']
}
]);

MODIFY
src/ui/legend2dWidget.js
src/ui/legend2dWidget.js
var domWidget = require('./domWidget');
var inherit = require('../inherit');
var registerWidget = require('../registry').registerWidget;
-require('./legend2dWidget.styl');
var legend2dWidget = function (arg) {
'use strict';
@@ -48,125 +47,151 @@ var legend2dWidget = function (arg) {
var container = d3.select(m_this.canvas()).append('div');
var width = 700;
+ var margin = 15;
+ // this.width = width;
m_categories.forEach(function (category, index) {
var legendSvg = container
.append('div')
- .style({
- 'width': width + 'px',
- 'height': '50px'
- })
.append('svg')
.attr({
- 'width': '100%',
- 'height': '100%'
+ 'width': width,
+ 'height': '50px',
+ 'viewBox': -margin + ' 0 ' + width + ' 50'
})
if (category.type === 'discrete') {
- var steps;
- var colorScale = d3.scale[category.scale.type]().domain(category.scale.domain).range(category.scale.range);
- var axisScale;
- if (category.scale.type === 'ordinal') {
- steps = category.scale.domain;
+ m_this._drawDiscrete(legendSvg, width - 2 * margin, category);
+ }
- axisScale = d3.scale.ordinal()
- .domain(category.scale.domain)
- .rangePoints([0, width]);
- var axis = d3.svg.axis()
- .scale(axisScale);
+ else if (category.type === 'continuous') {
+ m_this.drawContinous(legendSvg, width - 2 * margin, category);
}
- else if (category.scale.type === 'quantize') {
- steps = category.scale.range.map(function (color) {
- var range = colorScale.invertExtent(color);
- // console.log(color, range);
- return (range[1] - range[0]) / 2 + range[0];
});
- var steps2 = category.scale.range.map(function (color) {
- return colorScale.invertExtent(color)[0];
- });
+ }
+
+ this.categories = function (categories) {
+ m_categories = categories;
+ this.draw();
+ }
- var ticks = steps2.slice();
- ticks.push(colorScale.invertExtent(
- category.scale.range[category.scale.range.length - 1])[1]
- );
+ this._drawDiscrete = function (svg, width, category) {
+ if (category.scale === 'ordinal') {
+ var colorScale = d3.scale.ordinal().domain(category.domain).range(category.colors);
+ m_this._renderDiscreteColors(svg, category.domain, colorScale, width);
axisScale = d3.scale.ordinal()
- .domain(ticks)
+ .domain(category.domain)
+ .rangeRoundBands([0, width]);
+ var axis = d3.svg.axis()
+ .scale(axisScale);
+ m_this._renderAxis(svg, axis);
+
+ }
+ else if (['linear', 'log', 'sqrt', 'pow'].indexOf(category.scale) != -1) {
+ var valueRange = [0, category.colors.length];
+ var valueScale = d3.scale[category.scale]().domain(category.domain).range(valueRange).nice();
+ var colorScale = d3.scale.quantize().domain(valueRange).range(category.colors);
+ var steps = range(0, category.colors.length - 1);
+ m_this._renderDiscreteColors(svg, steps, colorScale, width);
+
+
+ var ticks = steps.slice();
+ ticks.push(category.colors.length);
+ var axisScale = d3.scale.ordinal()
+ .domain(ticks.map(function (tick) {
+ return valueScale.invert(tick);
+ }))
.rangePoints([0, width]);
var axis = d3.svg.axis()
.scale(axisScale)
- .tickFormat(d3.format(".2s"));
+ .tickFormat(d3.format(".1s"));
+ m_this._renderAxis(svg, axis);
+ }
}
- legendSvg.selectAll('rect')
+ this._renderDiscreteColors = function (svg, steps, colorScale, width) {
+ svg.selectAll('rect')
.data(steps)
.enter()
.append('rect')
.attr('width', width / steps.length)
.attr('height', '20px')
.attr('fill', function (d) {
- // console.log(d, colorScale(d));
return colorScale(d);
})
.attr('transform', function (d, i) {
return 'translate(' + i * width / steps.length + ' ,0)';
- })
+ });
+ }
+ this._renderAxis = function (svg, axis) {
+ svg.append('g')
+ .attr('class', 'x axis')
+ .attr('transform', 'translate(0, 20)')
+ .call(function (g) {
+ g.call(axis);
+ g.selectAll('path.domain, line')
+ .style({
+ 'fill': 'none',
+ 'stroke': 'black',
+ 'stroke-width': '0.7'
+ });
+ g.selectAll('text')
+ .style({
+ 'font-size': '12px'
+ });
+ });
}
- else if (category.type === 'continuous') {
- var axisScale = d3.scale[category.scale.type]().domain(category.scale.domain).range([0, width]);
- if (category.scale.type === 'log') {
- axisScale.base(Math.E);
+ this.drawContinous = function (svg, width, category) {
+ if (['linear', 'log', 'sqrt', 'pow'].indexOf(category.scale) == -1) {
+ throw new Error('unsupported scale');
+ }
+ var axisScale = d3.scale[category.scale]().domain(category.domain).range([0, width]).nice();
+ if (category.scale === 'log' && category.base) {
+ axisScale.base(category.base);
}
- if (category.scale.type === 'pow') {
- axisScale.exponent(1.5);
+ if (category.scale === 'pow' && category.exponent) {
+ axisScale.exponent(category.exponent);
}
- var gradient = legendSvg
+ var randomString = Math.random().toString(36).substring(5);
+
+ var gradient = svg
.append('defs')
.append('linearGradient')
- .attr('id', 'gradient' + index);
+ .attr('id', 'gradient' + randomString);
gradient.append('stop')
.attr('offset', '0%')
- .attr('stop-color', category.scale.range[0]);
+ .attr('stop-color', category.colors[0]);
gradient.append('stop')
.attr('offset', '100%')
- .attr('stop-color', category.scale.range[1]);
- legendSvg.append('rect')
- .attr('fill', 'url(#gradient' + index + ')')
- .attr('width', '100%')
+ .attr('stop-color', category.colors[1]);
+ svg.append('rect')
+ .attr('fill', 'url(#gradient' + randomString + ')')
+ .attr('width', width)
.attr('height', '20px');
var axis = d3.svg.axis()
.scale(axisScale)
- .tickFormat(d3.format(".2s"))
- }
-
- legendSvg.append('g')
- .attr('class', 'x axis')
- .attr('transform', 'translate(0, 20)')
- .call(function (g) {
- g.call(axis);
- g.selectAll('path.domain, line')
- .attr({
- 'fill': 'none',
- 'stroke': 'black'
- });
- });
- });
-
- }
+ .tickFormat(d3.format(".1s"));
- this.categories = function (categories) {
- m_categories = categories;
- this.draw();
+ this._renderAxis(svg, axis);
}
return this;
};
+function range(start, end) {
+ var foo = [];
+ for (var i = start; i <= end; i++) {
+ foo.push(i);
+ }
+ return foo;
+}
+
inherit(legend2dWidget, domWidget);
registerWidget('dom', 'legend2d', legend2dWidget);

initial functional implementation