Skip to content
This repository has been archived by the owner on Nov 30, 2021. It is now read-only.

Commit

Permalink
Merge pull request #45 from phovea/sgratzl/feature_heatmap_labels
Browse files Browse the repository at this point in the history
if #26 first version of labels
  • Loading branch information
Holger Stitz committed Mar 28, 2017
2 parents 0b7cc5b + 812289b commit d77933b
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 13 deletions.
79 changes: 73 additions & 6 deletions src/heatmap/HeatMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@

import '../style.scss';
import * as d3 from 'd3';
import {Range} from 'phovea_core/src/range';
import Range from 'phovea_core/src/range/Range';
import {AVisInstance, IVisInstance, assignVis} from 'phovea_core/src/vis';
import {rect} from 'phovea_core/src/geom';
import {mixin} from 'phovea_core/src';
import {INumericalMatrix, ICategoricalMatrix} from 'phovea_core/src/matrix';
import {mixin, onDOMNodeRemoved} from 'phovea_core/src';
import {toSelectOperation} from 'phovea_core/src/idtype';
import {INumericalMatrix, ICategoricalMatrix, DIM_ROW, DIM_COL} from 'phovea_core/src/matrix/IMatrix';
import {defaultColor, defaultDomain, toScale, IScale, ICommonHeatMapOptions} from './internal';
import {IHeatMapRenderer, ESelectOption} from './IHeatMapRenderer';
import HeatMapDOMRenderer from './HeatMapDOMRenderer';
Expand Down Expand Up @@ -51,6 +52,12 @@ export interface IHeatMapOptions extends ICommonHeatMapOptions {
* @default false
*/
forceThumbnails?: boolean;

/**
* render optional labels,
* @default NONE
*/
labels?: ESelectOption;
}

export default class HeatMap extends AVisInstance implements IVisInstance {
Expand All @@ -65,7 +72,8 @@ export default class HeatMap extends AVisInstance implements IVisInstance {
selectAble: true,
forceThumbnails: false,
scale: [1, 1],
rotate: 0
rotate: 0,
labels: ESelectOption.NONE
};

constructor(public data: IHeatMapAbleMatrix, public parent: Element, options: IHeatMapOptions = {}) {
Expand All @@ -83,7 +91,12 @@ export default class HeatMap extends AVisInstance implements IVisInstance {
this.options.rotate = 0;
this.colorer = toScale(value).domain(this.options.domain).range(this.options.color);

this.renderer = createRenderer(data, typeof this.options.selectAble === 'boolean' ? (this.options.selectAble ? ESelectOption.CELL : ESelectOption.NONE) : ESelectOption[<string>this.options.selectAble], this.options.forceThumbnails);
// handle string case
this.options.labels = typeof this.options.labels === 'string' ? ESelectOption[<string>this.options.labels]: this.options.labels;

const selection = typeof this.options.selectAble === 'boolean' ? (this.options.selectAble ? ESelectOption.CELL : ESelectOption.NONE) : ESelectOption[<string>this.options.selectAble];

this.renderer = createRenderer(data, selection, this.options.forceThumbnails);

this.$node = this.build(d3.select(parent));
this.$node.datum(data);
Expand Down Expand Up @@ -144,6 +157,17 @@ export default class HeatMap extends AVisInstance implements IVisInstance {
this.$node.style('transform', 'rotate(' + rotate + 'deg)');
if (bak.scale[0] !== scale[0] || bak.scale[1] !== scale[1]) {
this.renderer.rescale(this.$node, dims, scale);


if (this.options.labels === ESelectOption.CELL || this.options.labels === ESelectOption.ROW) {
this.$node.select('div.row-labels')
.style('height', dims[0] * scale[1] + 'px')
.style('right', dims[1] * scale[0] + 'px');
}
if (this.options.labels === ESelectOption.CELL || this.options.labels === ESelectOption.COLUMN) {
this.$node.select('div.column-labels')
.style('height', dims[1] * scale[0] + 'px');
}
}
const act = {scale, rotate};
this.fire('transform', act, bak);
Expand All @@ -159,10 +183,53 @@ export default class HeatMap extends AVisInstance implements IVisInstance {
}

private build($parent: d3.Selection<any>) {
return this.renderer.build(this.data, $parent, this.options.scale, this.colorer, () => {
const $node = this.renderer.build(this.data, $parent, this.options.scale, this.colorer, () => {
this.renderer.redraw(this.$node, this.options.scale);
this.markReady();
});

if (this.options.labels === ESelectOption.CELL || this.options.labels === ESelectOption.ROW) {
this.renderLabels($node, ESelectOption.ROW, this.data.rows())
.style('height', this.size[1] + 'px')
.style('right', this.size[0] + 'px');
}
if (this.options.labels === ESelectOption.CELL || this.options.labels === ESelectOption.COLUMN) {
this.renderLabels($node, ESelectOption.COLUMN, this.data.cols())
.style('height', this.size[0] + 'px');
}

return $node;
}

private renderLabels($node: d3.Selection<any>, mode: ESelectOption, names: Promise<string[]>) {
const dim = mode === ESelectOption.ROW ? DIM_ROW : DIM_COL;
const $group = $node.append('div').attr('class', 'phovea-heatmap-labels ' + (mode === ESelectOption.ROW ? 'row-labels' : 'column-labels'));
names.then((data) => {
const $names = $group.selectAll('div').data(data);
$names.enter().append('div').on('click', (d, i) => {
this.data.select(dim, [i], toSelectOperation(<MouseEvent>d3.event));
});
$names.text(String);
$names.exit().remove();
});

const l = function (event: any, type: string, selected: Range) {
const all = $group.selectAll('div');
all.classed('phovea-select-' + type, false);
const sub = selected.dim(dim).filter(all[0]);
if (sub.length > 0) {
d3.selectAll(sub).classed('phovea-select-' + type, true);
}
};
this.data.on('select', l);
onDOMNodeRemoved(<Element>$group.node(), function () {
this.data.off('select', l);
});
this.data.selections().then(function (selected) {
l(null, 'selected', selected);
});

return $group;
}

update() {
Expand Down
14 changes: 7 additions & 7 deletions src/heatmap/HeatMapDOMRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default class HeatMapDOMRenderer implements IHeatMapRenderer {
}

rescale($node: d3.Selection<any>, dim: number[], scale: number[]) {
$node.attr({
$node.select('svg').attr({
width: dim[1] * scale[0],
height: dim[0] * scale[1]
});
Expand All @@ -28,21 +28,21 @@ export default class HeatMapDOMRenderer implements IHeatMapRenderer {

recolor($node: d3.Selection<any>, data: IHeatMapAbleMatrix, color: IScale, scale: number[]) {
this.color = color;
$node.selectAll('rect').attr('fill', (d) => color(d));
$node.select('svg').selectAll('rect').attr('fill', (d) => color(d));
}

redraw($node: d3.Selection<any>, scale: number[]) {
$node.selectAll('rect').attr('fill', (d) => this.color(d));
$node.select('svg').selectAll('rect').attr('fill', (d) => this.color(d));
}

build(data: IHeatMapAbleMatrix, $parent: d3.Selection<any>, scale: [number, number], c: IScale, onReady: () => void) {
const dims = data.dim, that = this;
const width = dims[1], height = dims[0];

const $svg = $parent.append('svg').attr({
const $node = $parent.append('div').attr('class', 'phovea-heatmap');
const $svg = $node.append('svg').attr({
width: width * scale[0],
height: height * scale[1],
'class': 'phovea-heatmap'
height: height * scale[1]
});
const $g = $svg.append('g').attr('transform', 'scale(' + scale[0] + ',' + scale[1] + ')');
this.color = c;
Expand Down Expand Up @@ -89,6 +89,6 @@ export default class HeatMapDOMRenderer implements IHeatMapRenderer {
});
}

return $svg;
return $node;
}
}
24 changes: 24 additions & 0 deletions src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,30 @@ div.phovea-heatmap {
z-index: 2;
}
}

.phovea-heatmap-labels {
line-height: 100%;
position: absolute;
display: flex;
flex-direction: column;
justify-content: space-around;
overflow: hidden;

div.phovea-select-selected {
background-color: $select-color;
}

&.row-labels {
top: 0;
text-align: right;
}

&.column-labels {
top: 0;
transform: rotate(-90deg);
transform-origin: top left;
}
}
}


Expand Down

0 comments on commit d77933b

Please sign in to comment.