Skip to content

Commit

Permalink
Added more customization options in heatmap library (#25)
Browse files Browse the repository at this point in the history
* Added ability to customize default margins

* Filter out labels

* Added Ability to set the offset for svg

* Added support for typed arrays

* Bumped up version and fixed a bug

* Updated readme.md

* Bump up version

* Bump up lib version
  • Loading branch information
OssamaRafique authored Nov 15, 2023
1 parent 2ec82e6 commit 84dd21b
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 42 deletions.
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,11 @@ This method allows you to customize the label options for your visualization. Al
- `labelOptions.columnLabelSlintAngle` (`number`, optional): slant angle for column labels (default: 0)
- `labelOptions.rowLabelFontSize` (`string | number`, optional): font size for row labels (default: 7px)
- `labelOptions.columnLabelFontSize` (`string | number`, optional): font size for column labels (default: 7px)

**Example**:
- `labelOptions.rowLabelsSvgXOffset` (`number`, optional): x offset for row labels (default: -1.05)
- `labelOptions.rowLabelsSvgYOffset` (`number`, optional): y offset for row labels (default: -1.02)
- `labelOptions.columnLabelsSvgXOffset` (`number`, optional): x offset for column labels (default: -1.02)
- `labelOptions.columnLabelsSvgYOffset` (`number`, optional): y offset for column labels (default: 1.05)
**Example**:

```javascript
plot.setLabelOptions({
Expand All @@ -134,6 +137,29 @@ plot.setLabelOptions({
});
```

#### Customizing Margins with `setMargins`

This method allows you to customize the margins for your visualization. All parameters are optional, providing you the flexibility to specify the options that best suit your needs.

**Parameters**:

- `margins` (`object`): an object containing the margin options
- `margins.top` (`number`, optional): top margin (default: 25px)
- `margins.bottom` (`number`, optional): bottom margin (default: 50px)
- `margins.left` (`number`, optional): left margin (default: 50px)
- `margins.right` (`number`, optional): right margin (default: 10px)

**Example**:

```javascript
plot.setMargins({
top: 10,
bottom: 10,
left: 10,
right: 10,
});
```

#### Grouping Bars and Labels for Rows and Columns

This feature allows users to add grouping bars and labels to both rows and columns of the heatmap. It provides a convenient way to visually represent different groups in your heatmap data.
Expand Down
73 changes: 64 additions & 9 deletions src/BaseGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
INTENSITY_LEGEND_GRADIENT_SIZE_IN_PX,
INTENSITY_LEGEND_SIZE_IN_PX,
GROUPING_LEGEND_SIZE_IN_PX,
DEFAULT_MARGINS,
} from "./constants";

/**
Expand Down Expand Up @@ -75,8 +76,14 @@ class BaseGL {
ygap: 0.3,
};

this.margins = DEFAULT_MARGINS;

//Default Data for labelOptions
this.labelOptions = {
rowLabelsSvgXOffset: -1.05,
rowLabelsSvgYOffset: -1.02,
columnLabelsSvgXOffset: -1.02,
columnLabelsSvgYOffset: 1.05,
rowLabelMaxCharacters: DEFAULT_ROW_MAX_LABEL_LENGTH_ALLOWED,
columnLabelMaxCharacters: DEFAULT_COLUMN_MAX_LABEL_LENGTH_ALLOWED,
rowLabelSlintAngle: DEFAULT_ROW_LABEL_SLINT_ANGLE,
Expand Down Expand Up @@ -183,6 +190,10 @@ class BaseGL {

_generateSpecForLabels(spec) {
const {
rowLabelsSvgXOffset,
rowLabelsSvgYOffset,
columnLabelsSvgXOffset,
columnLabelsSvgYOffset,
rowLabelMaxCharacters,
columnLabelMaxCharacters,
rowLabelSlintAngle,
Expand Down Expand Up @@ -212,8 +223,8 @@ class BaseGL {

maxWidth = Math.max(maxWidth, truncatedLabelWidth);
labels.push({
x: -1.02 + (2 * ilx + 1) / xlabels_len,
y: 1.05,
x: columnLabelsSvgXOffset + (2 * ilx + 1) / xlabels_len,
y: columnLabelsSvgYOffset,
type: "row",
index: ilx,
text: truncatedLabel,
Expand Down Expand Up @@ -246,8 +257,8 @@ class BaseGL {
);
maxWidth = Math.max(maxWidth, truncatedLabelWidth);
labels.push({
x: -1.05,
y: -1.02 + (2 * ily + 1) / ylabels_len,
x: rowLabelsSvgXOffset,
y: rowLabelsSvgYOffset + (2 * ily + 1) / ylabels_len,
type: "column",
index: ily,
text: truncatedLabel,
Expand All @@ -269,7 +280,7 @@ class BaseGL {
...spec["margins"],
top: `${topMarginToAccountForLabels}px`,
left: `${leftMarginToAccountForLabels}px`,
right: "20px",
right: `${GROUPING_LEGEND_SIZE_IN_PX}px`,
};
}

Expand Down Expand Up @@ -498,6 +509,10 @@ class BaseGL {
* @memberof BaseGL
* @example
* this.labelOptions = {
* rowLabelsSvgXOffset: 0,
* rowLabelsSvgYOffset: 0,
* columnLabelsSvgXOffset: 0,
* columnLabelsSvgYOffset: 0,
* rowLabelMaxCharacters: 10,
* columnLabelMaxCharacters: 10,
* rowLabelSlintAngle: 0,
Expand All @@ -507,8 +522,12 @@ class BaseGL {
* }
* @example
* this.setLabelOptions({
* rowLabelsSvgXOffset: 0,
* rowLabelsSvgYOffset: 0,
* columnLabelsSvgXOffset: 0,
* columnLabelsSvgYOffset: 0,
* rowLabelMaxCharacters: 10,
* columnLabelMaxCharacters: 10,
* columnLabelMaxCharacters: 10,
* rowLabelSlintAngle: 0,
* columnLabelSlintAngle: 0,
* rowLabelFontSize: "7px",
Expand All @@ -522,6 +541,30 @@ class BaseGL {
};
}

/**
* Set the margins for the visualization.
* all properties are optional, if not provided, the default values will be used.
* @param {object} margins, an object containing the margins
* @param {number} margins.top, top margin
* @param {number} margins.bottom, bottom margin
* @param {number} margins.left, left margin
* @param {number} margins.right, right margin
* @memberof BaseGL
* @example
* this.setMargins({
* top: '10px',
* bottom: '10px',
* left: '10px',
* right: '10px',
* })
**/
setMargins(margins) {
this.margins = {
...this.margins,
...margins,
};
}

/**
* resize the plot, without having to send the data to the GPU.
*
Expand Down Expand Up @@ -1023,22 +1066,34 @@ class BaseGL {
* @param {string} orientation - The orientation of the grouping labels
* @returns {void}
**/
renderGroupingLabels(parentElement, groupingRowData, orientation) {
renderGroupingLabels(parentElement, groupingData, orientation) {
// Filter out duplicate labels in the grouping data
groupingData = groupingData.reduce(
(acc, obj) => {
if (!acc.seen[obj.label]) {
acc.seen[obj.label] = true;
acc.result.push(obj);
}
return acc;
},
{ seen: {}, result: [] }
).result;

const parent = select(parentElement);
const svg = parent.append("svg");

svg.attr("width", "100%").style("overflow", "inherit");
if (orientation === "horizontal") {
svg.attr("height", 25);
} else {
svg.attr("height", groupingRowData.length * 25);
svg.attr("height", groupingData.length * 25);
}

const labelHeight = 25;
let xOffset = 0;
let yOffset = 0;

groupingRowData.forEach((data) => {
groupingData.forEach((data) => {
const group = svg.append("g");

group
Expand Down
18 changes: 10 additions & 8 deletions src/DotplotGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
getMinMax,
getScaledRadiusForDotplot,
parseMargins,
mapArrayOrTypedArray,
} from "./utils";

/**
Expand Down Expand Up @@ -98,16 +99,17 @@ class DotplotGL extends BaseGL {
const [, maxY] = getMinMax(this.input.y);
let xlen = maxX + 1,
ylen = maxY + 1;
spec_inputs.x = this.input.x.map((e, i) => -1 + (2 * e + 1) / xlen);
spec_inputs.y = this.input.y.map((e, i) => -1 + (2 * e + 1) / ylen);
spec_inputs.x = mapArrayOrTypedArray(
this.input.x,
(e, i) => -1 + (2 * e + 1) / xlen
);
spec_inputs.y = mapArrayOrTypedArray(
this.input.y,
(e, i) => -1 + (2 * e + 1) / ylen
);

let spec = {
margins: {
top: "25px",
bottom: "50px",
left: "50px",
right: "10px",
},
margins: this.margins,
defaultData: {
x: spec_inputs.x,
y: spec_inputs.y,
Expand Down
23 changes: 12 additions & 11 deletions src/RectplotGL.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import BaseGL from "./BaseGL";
import { getMinMax } from "./utils";
import { getMinMax, mapArrayOrTypedArray } from "./utils";

/**
* Class to create traditional heatmap plots
Expand Down Expand Up @@ -53,22 +53,23 @@ class RectplotGL extends BaseGL {
};

let spec_inputs = {};
spec_inputs.x = this.input.x.map((e, i) => String(e));
spec_inputs.y = this.input.y.map((e, i) => String(e));
spec_inputs.x = mapArrayOrTypedArray(this.input.x, (e, i) => String(e));
spec_inputs.y = mapArrayOrTypedArray(this.input.y, (e, i) => String(e));

let default_width = 198 / (getMinMax(this.input.x)[1] + 1);
let default_height = 198 / (getMinMax(this.input.y)[1] + 1);

spec_inputs.width = this.input.x.map((e, i) => default_width - xGaps(i));
spec_inputs.height = this.input.y.map((e, i) => default_height - yGaps(i));
spec_inputs.width = mapArrayOrTypedArray(
this.input.x,
(e, i) => default_width - xGaps(i)
);
spec_inputs.height = mapArrayOrTypedArray(
this.input.y,
(e, i) => default_height - yGaps(i)
);

let spec = {
margins: {
top: "25px",
bottom: "50px",
left: "50px",
right: "10px",
},
margins: this.margins,
defaultData: {
x: spec_inputs.x,
y: spec_inputs.y,
Expand Down
7 changes: 1 addition & 6 deletions src/TickplotGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,7 @@ class TickplotGL extends BaseGL {
}

let spec = {
margins: {
top: "25px",
bottom: "50px",
left: "50px",
right: "10px",
},
margins: this.margins,
defaultData: {
x: this.input.x,
y: this.input.y,
Expand Down
7 changes: 7 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,10 @@ export const DEFAULT_SIZE_LEGEND_CIRCLE_TEXT_GAP = 10;

export const DEFAULT_MIN_RADIUS_FOR_DOTPLOT = 3;
export const DEFAULT_MARGIN_BETWEEN_DOTS = 2;

export const DEFAULT_MARGINS = {
top: "25px",
bottom: "50px",
left: "50px",
right: "10px",
};
Loading

0 comments on commit 84dd21b

Please sign in to comment.