Skip to content

Commit

Permalink
Merge pull request #495 from Kitware/update-api
Browse files Browse the repository at this point in the history
Add update / destroy API for VisComponent
  • Loading branch information
Roni Choudhury committed May 24, 2017
2 parents 961a9ab + aa0e2aa commit 58fa5ad
Show file tree
Hide file tree
Showing 12 changed files with 269 additions and 80 deletions.
16 changes: 16 additions & 0 deletions VisComponent/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { select } from 'd3-selection';

export default class VisComponent {
constructor (el) {
if (!el) {
Expand All @@ -11,6 +13,20 @@ export default class VisComponent {
throw new Error('"render() is pure abstract"');
}

update () {
return Promise.resolve(this);
}

destroy () {
this.empty();
}

empty () {
select(this.el)
.selectAll('*')
.remove();
}

get serializationFormats () {
return [];
}
Expand Down
31 changes: 30 additions & 1 deletion VisComponent/mixin/VegaChart.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ let VegaChart = (Base, spec) => class extends Base {
}

render () {
this.chart.then(chart => {
return this.chart.then(chart => {
if (this.width) {
chart = chart.width(this.width);
}
Expand All @@ -21,6 +21,35 @@ let VegaChart = (Base, spec) => class extends Base {
});
}

update (options) {
let promise = this.chart;

Object.assign(this.options, options);

if (this.options.data) {
promise = promise.then(chart => {
return chart.data('data')
.remove(() => true)
.insert(this.options.data);
});
}

if (this.options.width) {
this.width = this.options.width;
}

if (this.options.height) {
this.height = this.options.height;
}

return promise;
}

destroy () {
this.empty();
delete this.chart;
}

get serializationFormats () {
return ['png', 'svg'];
}
Expand Down
14 changes: 1 addition & 13 deletions app/examples/dynamic-linechart/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,6 @@ class DynamicLineChart extends LineChart {
super.render();
this.emit('render');
}

data (data) {
this.options.data = data;

this.chart.then(chart => {
let data = chart.data('data');
data.remove(() => true);
data.insert(this.options.data);

chart.update();
});
}
}

let data = [];
Expand Down Expand Up @@ -58,7 +46,7 @@ window.onload = () => {

counter++;

vis.data(data);
vis.update({data});
vis.render();
}, 1000);

Expand Down
17 changes: 9 additions & 8 deletions app/examples/geodots/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ window.onload = () => {
longitude: 'longitude',
latitude: 'latitude',
data: [
{id: 0, longitude: -87.578, latitude: 41.838, c: 3},
{id: 1, longitude: -87.644, latitude: 41.702, c: 3},
{id: 2, longitude: -87.693, latitude: 41.617, c: 3},
{id: 3, longitude: -87.708, latitude: 41.953, c: 3},
{id: 4, longitude: -87.700, latitude: 41.747, c: 3},
{id: 5, longitude: -87.555, latitude: 41.806, c: 3},
{id: 6, longitude: -87.335, latitude: 42.039, c: 3},
{id: 7, longitude: -87.313, latitude: 41.615, c: 3}
{id: 10, longitude: -20, latitude: 10, c: 'A'},
{id: 0, longitude: -87.578, latitude: 41.838, c: 'B'},
{id: 1, longitude: -87.644, latitude: 41.702, c: 'A'},
{id: 2, longitude: -87.693, latitude: 41.617, c: 'B'},
{id: 3, longitude: -87.708, latitude: 41.953, c: 'A'},
{id: 4, longitude: -87.700, latitude: 41.747, c: 'B'},
{id: 5, longitude: -87.555, latitude: 41.806, c: 'A'},
{id: 6, longitude: -87.335, latitude: 42.039, c: 'B'},
{id: 7, longitude: -87.313, latitude: 41.615, c: 'C'}
]
});
};
68 changes: 68 additions & 0 deletions components/BarChart/test/barchart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { select } from 'd3-selection';
import test from 'tape-catch';

import BarChart from '..';

test('BarChart component', t => {
t.plan(7);

const data = [
{id: 0, a: 1, b: 3, c: 3},
{id: 1, a: 10, b: 4, c: 3},
{id: 2, a: 7, b: 6, c: 3},
{id: 3, a: 4, b: 2, c: 3},
{id: 4, a: 5, b: 5, c: 3},
{id: 5, a: 7, b: 6, c: 3},
{id: 6, a: 2, b: 9, c: 3},
{id: 7, a: 5, b: 7, c: 3}
];

let el = document.createElement('div');
let vis = new BarChart(el, {
data: data,
x: 'id',
y: 'a',
color: 'b',
width: 625,
height: 540,
padding: {
left: 45,
right: 130,
top: 20,
bottom: 40
},
renderer: 'svg'
});

vis.chart.then(() => {
t.equal(el.childNodes.length, 1, 'VegaCharts should have a single element under the top-level div');

let container = el.childNodes[0];
t.equal(container.nodeName, 'DIV', 'The single element should be a div');
t.equal(container.childNodes.length, 1, 'The div should have a single child element.');

let svg = container.childNodes[0];
t.equal(svg.nodeName, 'svg', 'The single child should be an svg.');

let bars = select(svg)
.select('g.mark-rect')
.selectAll('rect');
t.equal(bars.size(), data.length, 'The number of bars in the chart should equal the number of data items');

vis.update({
data: data.concat([{id: 8, a: 10, b: 6, c: 3}])
}).then(() => vis.render())
.then(() => {
vis.render();

bars = select(svg)
.select('g.mark-rect')
.selectAll('rect');
t.equal(bars.size(), data.length + 1, 'After data update, the number of bars in the chart should equal the original number of data items, plus one');

vis.destroy();
let contents = select(vis.el).selectAll('*');
t.equal(contents.size(), 0, 'After destroy(), container element should have no children');
});
});
});
5 changes: 4 additions & 1 deletion components/Geo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ export default class Geo extends VisComponent {
}, map));

// Process the requested layers.
this.layers = [];
layers.forEach(layer => {
switch (layer.type) {
case 'osm':
this.plot.createLayer('osm', layer);
this.layers.push(this.plot.createLayer('osm', layer));
break;

case 'feature':
Expand All @@ -41,6 +42,8 @@ export default class Geo extends VisComponent {
}, spec.style);

feature.style(style);

this.layers.push(feature);
});
break;
}
Expand Down
138 changes: 94 additions & 44 deletions components/GeoDots/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,63 @@
import d3 from 'd3';
import d3 from 'geojs/node_modules/d3';
import Geo from '../Geo';
import VisComponent from '../../VisComponent';
import { minmax } from '../../util';

const computeSizeTransform = (data, sizeField) => {
let sizeTransform = 5;
if (sizeField) {
const range = minmax(data.map(d => d[sizeField]));
const scale = d3.scale.linear()
.domain([range.min, range.max])
.range([3, 19]);

sizeTransform = d => scale(d[sizeField]);
}

return sizeTransform;
};

const computeColorTransforms = (data, color) => {
let fillTransform = 'red';
let strokeTransform = 'darkred';
if (color && data.length > 0) {
let fillScale, strokeScale;

const type = typeof data[0][color];
if (type === undefined || type === 'string') {
fillScale = d3.scale.category10();
strokeScale = 'black';
} else {
const range = minmax(data.map(d => d[color]));

const red = d3.rgb('#ef6a62');
const blue = d3.rgb('#67a9cf');
const darkred = red.darker();
const darkblue = blue.darker();

fillScale = d3.scale.linear()
.domain([range.min, range.max])
.range([red, blue]);

strokeScale = d3.scale.linear()
.domain([range.min, range.max])
.range([darkred, darkblue]);
}

fillTransform = d => fillScale(d[color]);
if (strokeScale === 'black') {
strokeTransform = 'black';
} else {
strokeTransform = d => strokeScale(d[color]);
}
}

return {
fillTransform,
strokeTransform
};
};

export default class GeoDots extends VisComponent {
static get options () {
return [
Expand Down Expand Up @@ -65,49 +120,8 @@ export default class GeoDots extends VisComponent {
el.style.width = width + 'px';
el.style.height = height + 'px';

let sizeTransform = 5;
if (options.size) {
const range = minmax(options.data.map(d => d[options.size]));
const scale = d3.scale.linear()
.domain([range.min, range.max])
.range([3, 19]);

sizeTransform = d => scale(d[options.size]);
}

let fillTransform = 'red';
let strokeTransform = 'darkred';
if (options.color && options.data.length > 0) {
let fillScale, strokeScale;

const type = typeof options.data[0][options.color];
if (type === undefined || type === 'string') {
fillScale = d3.scale.category10();
strokeScale = 'black';
} else {
const range = minmax(options.data.map(d => d[options.color]));

const red = d3.rgb('#ef6a62');
const blue = d3.rgb('#67a9cf');
const darkred = red.darker();
const darkblue = blue.darker();

fillScale = d3.scale.linear()
.domain([range.min, range.max])
.range([red, blue]);

strokeScale = d3.scale.linear()
.domain([range.min, range.max])
.range([darkred, darkblue]);
}

fillTransform = d => fillScale(d[options.color]);
if (strokeScale === 'black') {
strokeTransform = 'black';
} else {
strokeTransform = d => strokeScale(d[options.color]);
}
}
const sizeTransform = computeSizeTransform(options.data, options.size);
const { fillTransform, strokeTransform } = computeColorTransforms(options.data, options.color);

// TODO(choudhury): don't mutate the options object directly.
options.layers = [];
Expand Down Expand Up @@ -150,9 +164,45 @@ export default class GeoDots extends VisComponent {
}, options);

this.geojs = new Geo(this.el, map_options);
this.options = options;
}

render () {
this.geojs.render();
}

update (options) {
let points = this.geojs.layers[1];

let changed = new Set();
['longitude', 'latitude', 'color', 'size'].forEach(opt => {
if (options[opt]) {
changed.add(opt);
this.options[opt] = options[opt];
}
});

if (changed.has('longitude') || changed.has('latitude')) {
points.position(d => ({
x: d[this.options.longitude],
y: d[this.options.latitude]
}));
}

if (changed.has('size')) {
points.style('radius', computeSizeTransform(this.options.data, this.options.size));
}

if (changed.has('color')) {
const { fillTransform, strokeTransform } = computeColorTransforms(this.options.data, this.options.color);
points.style('fillColor', fillTransform)
.style('strokeColor', strokeTransform);
}

if (changed.size > 0) {
points.modified();
}

return Promise.resolve(this);
}
}
6 changes: 1 addition & 5 deletions components/SentenTree/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { select } from 'd3-selection';

import VisComponent from '../../VisComponent';
import { SentenTreeBuilder,
SentenTreeVis } from 'sententree/dist/SentenTree';
Expand Down Expand Up @@ -57,9 +55,7 @@ export default class SentenTree extends VisComponent {
super(el);

// Empty element.
select(el)
.selectAll('*')
.remove();
this.empty();

// Transform input data into correct form.
this.data = data.map((d, i) => ({
Expand Down

0 comments on commit 58fa5ad

Please sign in to comment.