Skip to content

Commit

Permalink
Merge ae1053e into b641189
Browse files Browse the repository at this point in the history
  • Loading branch information
eweitz committed May 9, 2021
2 parents b641189 + ae1053e commit b106ae9
Show file tree
Hide file tree
Showing 12 changed files with 141 additions and 68 deletions.
4 changes: 4 additions & 0 deletions api.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ var ideogram = new Ideogram({
* [histogramScaling](#histogramscaling)
* [heatmaps](#heatmaps)
* [filterable](#filterable)
* [fontFamily](#fontfamily)
* [fullChromosomeLabels](#fullchromosomelabels)
* [legend](#legend)
* [onBrushMove](#onbrushmove)
Expand Down Expand Up @@ -174,6 +175,9 @@ the heatmap. Threshold values are specified in ascending order. Example in [An
## filterable
Boolean. Optional. Whether annotations should be filterable. Example in [Annotations, histogram](https://eweitz.github.io/ideogram/annotations-histogram).

## fontFamily
String. Optional. The font family to use for text in the ideogram, e.g. `fontFamily: "'Montserrat', sans-serif"`.

## fullChromosomeLabels
Boolean. Optional. Whether to include abbreviation species name in chromosome label. Example in [Homology, interspecies](https://eweitz.github.io/ideogram/homology-interspecies).

Expand Down
9 changes: 7 additions & 2 deletions examples/vanilla/related-genes.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
<head>
<title>Related genes | Ideogram</title>
<style>
body {font: 14px Arial; line-height: 19.6px; padding: 0 15px;}
body {font: 14px Arial; line-height: 19.6px; padding: 0 15px;
/* font-family: 'Montserrat', sans-serif; */
}
a, a:visited {text-decoration: none;}
a:hover {text-decoration: underline;}
a, a:hover, a:visited, a:active {color: #0366d6;}
Expand Down Expand Up @@ -50,7 +52,9 @@

</style>
<script type="text/javascript" src="../../dist/js/ideogram.min.js"></script>
<link rel="icon" type="image/x-icon" href="img/ideogram_favicon.ico">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css?family=Montserrat%3A100%2C100italic%2C200%2C200italic%2C300%2C300italic%2C400%2C400italic%2C500%2C500italic%2C600%2C600italic%2C700%2C700italic%2C800%2C800italic%2C900%2C900italic&display=swap&ver=5.7.1" rel="stylesheet">
<link rel="icon" type="image/x-icon" href="img/ideogram_favicon.ico">
</head>
<body>
<h1>Related genes | Ideogram</h1>
Expand Down Expand Up @@ -242,6 +246,7 @@ <h1>Related genes | Ideogram</h1>
chrHeight: 100,
chrLabelSize: 12,
annotationHeight: 7,
// fontFamily: "'Montserrat', sans-serif",
onLoad: plotGeneFromUrl,
onPlotRelatedGenes: reportRelatedGenes,
showAnnotLabels: true,
Expand Down
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/js/annotations/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ function renderTooltip(tooltip, content, matrix, yOffset, ideo) {
.style('opacity', 1) // Make tooltip visible
.style('left', matrix.e + 'px')
.style('top', (matrix.f - yOffset) + 'px')
.style('font-family', ideo.config.fontFamily)
.style('pointer-events', null) // Prevent bug in clicking chromosome
.on('mouseover', function() {
clearTimeout(ideo.hideAnnotTooltipTimeout);
Expand Down
74 changes: 35 additions & 39 deletions src/js/annotations/labels.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {d3} from '../lib';
import {d3, getFont, getTextSize} from '../lib';

const allLabelStyle = `
<style>
Expand All @@ -21,6 +21,14 @@ const allLabelStyle = `
stroke: #D0D0DD !important;
stroke-width: 1.5px;
}
#_ideogram ._ideogramLabel {
stroke: white;
stroke-width: 5px;
stroke-linejoin: round;
paint-order: stroke fill;
text-align: center;
}
</style>
`;

Expand All @@ -29,6 +37,12 @@ function getAnnotDomLabelId(annot) {
return 'ideogramLabel_' + annot.domId;
}

function changeAnnotState(state, labelId, annotId) {
d3.selectAll('._ideoActive').classed('_ideoActive', false);
d3.select('#' + labelId).attr('class', '_ideogramLabel ' + state);
d3.select('#' + annotId + ' > path').attr('class', state);
}

function triggerAnnotEvent(event, ideo) {
let labelId, annotId;
const target = event.target;
Expand All @@ -50,13 +64,21 @@ function triggerAnnotEvent(event, ideo) {
ideo.time.prevTooltipAnnotDomId = annotId;
}

const state = (type === 'mouseover') ? '_ideoActive' : '';
d3.select('#' + labelId).attr('class', '_ideogramLabel ' + state);
d3.select('#' + annotId + ' > path').attr('class', state);
// On mouseover, activate immediately
// Otherwise, wait a moment (250 ms), then deactivate.
// Delayed deactivation mitigates flicker when moving from
// annot label to annot triangle.
if (type === 'mouseover') {
clearTimeout(window._ideoActiveTimeout);
changeAnnotState('_ideoActive', labelId, annotId);
} else {
window._ideoActiveTimeout = window.setTimeout(function() {
changeAnnotState('', labelId, annotId);
}, 250);
}
}

function renderLabel(annot, style, ideo) {
const config = ideo.config;

if (!ideo.didSetLabelStyle) {
document.querySelector('#_ideogramInnerWrap')
Expand All @@ -66,10 +88,7 @@ function renderLabel(annot, style, ideo) {

const id = getAnnotDomLabelId(annot);

// TODO: De-duplicate with code in getTextWidth and elsewhere
// perhaps set config.annotLabelSize and config.annotLabelFont upstream.
const labelSize = config.annotLabelSize ? config.annotLabelSize : 13;
const font = labelSize + 'px sans-serif';
const font = getFont(ideo);

const fill = annot.color === 'pink' ? '#CF406B' : annot.color;

Expand All @@ -78,38 +97,12 @@ function renderLabel(annot, style, ideo) {
.attr('class', '_ideogramLabel')
.attr('x', style.left)
.attr('y', style.top)
.style('text-align', 'center')
.style('font', font)
.style('fill', fill)
.style('pointer-events', null) // Prevent bug in clicking chromosome
.style('stroke', 'white')
.style('stroke-width', '5px')
.style('stroke-linejoin', 'round')
.style('paint-order', 'stroke fill')
.html(annot.name);
}

/**
* Compute and return the width of the given text of given font in pixels.
*
* @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
*/
function getTextWidth(text, ideo) {
var config = ideo.config;
var labelSize = config.annotLabelSize ? config.annotLabelSize : 13;

var font = labelSize + 'px sans-serif';

// re-use canvas object for better performance
var canvas =
getTextWidth.canvas ||
(getTextWidth.canvas = document.createElement('canvas'));
var context = canvas.getContext('2d');
context.font = font;
var metrics = context.measureText(text);
return metrics.width;
}

/** Get annotation object by name, e.g. "BRCA1" */
function getAnnotByName(annotName, ideo) {
var annot;
Expand Down Expand Up @@ -143,14 +136,17 @@ function getAnnotLabelLayout(annot, ideo) {
ideoRect =
document.querySelector('#_ideogram').getBoundingClientRect();

width = getTextWidth(annot.name, ideo);
const textSize = getTextSize(annot.name, ideo);
width = textSize.width;

// Accounts for:
// `pad` is a heuristic that accounts for:
// 1px left pad, 1px right pad, 1px right border, 1px left border
// as set in renderLabel
width = width + 7;
// as set in renderLabel
const pad = (config.fontFamily) ? 9 : 7;
width += pad;

const labelSize = config.annotLabelSize ? config.annotLabelSize : 13;
// console.log('height, labelSize', height, labelSize);

// Accounts for 1px top border, 1px bottom border as set in renderLabel
height = labelSize;
Expand Down
28 changes: 20 additions & 8 deletions src/js/annotations/legend.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
* Icons may have different shapes. A legend may also have a name.
*/

import {d3} from '../lib';

var lineHeight = 19;
import {d3, getTextSize} from '../lib';

var legendStyle =
'#_ideogramLegend {font: 12px Arial; line-height: 19px; overflow: auto;} ' +
Expand Down Expand Up @@ -46,7 +44,8 @@ function getIcon(row, ideo) {
}

function getListItems(labels, svg, list, nameHeight, ideo) {
var i, icon, y, row;
var i, icon, y, row,
lineHeight = getLineHeight(ideo);

for (i = 0; i < list.rows.length; i++) {
row = list.rows[i];
Expand All @@ -61,15 +60,21 @@ function getListItems(labels, svg, list, nameHeight, ideo) {
return [labels, svg];
}

function getLineHeight(ideo) {
return getTextSize('I', ideo).height + 10.5;
}

/**
* Display a legend for genome annotations, using `legend` configuration option
*/
function writeLegend(ideo) {
var i, legend, svg, labels, list, content;
var i, legend, svg, labels, list, content,
config = ideo.config,
lineHeight = getLineHeight(ideo);

d3.select(ideo.config.container + ' #_ideogramLegend').remove();
d3.select(config.container + ' #_ideogramLegend').remove();

legend = ideo.config.legend;
legend = config.legend;
content = '';

for (i = 0; i < legend.length; i++) {
Expand All @@ -84,7 +89,14 @@ function writeLegend(ideo) {
content += svg + '<ul>' + labels + '</ul>';
}

var target = d3.select(ideo.config.container + ' #_ideogramOuterWrap');
if (config.fontFamily) {
var fontFamily = `font-family: ${config.fontFamily};`;
var lineHeightCss = `line-height: ${getLineHeight(ideo)}px;`;
legendStyle +=
`#_ideogramLegend {${fontFamily}} ${lineHeightCss}}`;
}

var target = d3.select(config.container + ' #_ideogramOuterWrap');
target.append('style').html(legendStyle);
target.append('div').attr('id', '_ideogramLegend').html(content);
}
Expand Down
5 changes: 4 additions & 1 deletion src/js/init/configure.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,13 @@ function configureTextStyle(ideo) {
const config = ideo.config;
if (!config.chrLabelSize) ideo.config.chrLabelSize = 9;
if (!config.chrLabelColor) ideo.config.chrLabelColor = '#000';
if (!config.fontFamily) ideo.config.fontFamily = '';

const size = `font-size: ${config.chrLabelSize}px`;
const color = `fill: ${config.chrLabelColor}`;
configuredCss += `#_ideogram text {${size}; ${color};}`;
const fontFamily = `font-family: ${config.fontFamily}`;
configuredCss += `#_ideogram text {${fontFamily}; ${size}; ${color};}`;
configuredCss += `#_ideogramLabel text {${fontFamily};}`;
}

/**
Expand Down
24 changes: 12 additions & 12 deletions src/js/kit/related-genes.js
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ function parseAnnotFromMgiGene(gene, ideo, color='red') {

function moveLegend() {
const ideoInnerDom = document.querySelector('#_ideogramInnerWrap');
const decorPad = setRelatedDecorPad({}).annotDecorPad;
const decorPad = setRelatedDecorPad({}).legendPad;
const left = decorPad + 20;
const legendStyle = `position: absolute; top: 15px; left: ${left}px`;
const legend = document.querySelector('#_ideogramLegend');
Expand Down Expand Up @@ -583,25 +583,25 @@ function adjustPlaceAndVisibility(ideo) {
ideoInnerDom.style.overflowY = 'hidden';
document.querySelector('#_ideogramMiddleWrap').style.overflowY = 'hidden';

const annotDecorPad = ideo.config.annotDecorPad;
const legendPad = ideo.config.legendPad;

if (typeof ideo.didAdjustIdeogramLegend === 'undefined') {
// Accounts for moving legend when external content at left or right
// is variable upon first rendering plotted genes

var ideoDom = document.querySelector('#_ideogram');
const legendWidth = 150;
const legendWidth = 160;
ideoInnerDom.style.maxWidth =
(
parseInt(ideoInnerDom.style.maxWidth) +
legendWidth +
annotDecorPad
legendPad
) + 'px';

ideoDom.style.minWidth =
(parseInt(ideoDom.style.minWidth) + annotDecorPad) + 'px';
(parseInt(ideoDom.style.minWidth) + legendPad) + 'px';
ideoDom.style.maxWidth =
(parseInt(ideoDom.style.minWidth) + annotDecorPad) + 'px';
(parseInt(ideoDom.style.minWidth) + legendPad) + 'px';
ideoDom.style.position = 'relative';
ideoDom.style.left = legendWidth + 'px';

Expand Down Expand Up @@ -738,7 +738,7 @@ function decorateRelatedGene(annot) {
const shape = 'triangle';

const legendHeaderStyle =
'font-size: 14px; font-weight: bold; font-color: #333';
`font-size: 14px; font-weight: bold; font-color: #333;`;
const relatedLegend = [{
name: `
<div style="position: relative; left: -15px; padding-bottom: 10px;">
Expand All @@ -765,12 +765,12 @@ const citedLegend = [{
rows: []
}];

/** Sets annotDecorPad for related genes view */
/** Sets legendPad for related genes view */
function setRelatedDecorPad(kitConfig) {
if (kitConfig.showAnnotLabels) {
kitConfig.annotDecorPad = 60;
kitConfig.legendPad = 70;
} else {
kitConfig.annotDecorPad = 30;
kitConfig.legendPad = 30;
}
return kitConfig;
}
Expand Down Expand Up @@ -930,9 +930,9 @@ function _initGeneHints(config, annotsInList) {
const kitConfig = Object.assign(kitDefaults, config);

if (kitConfig.showAnnotLabels) {
kitConfig.annotDecorPad = 80;
kitConfig.legendPad = 80;
} else {
kitConfig.annotDecorPad = 30;
kitConfig.legendPad = 30;
}

const ideogram = new Ideogram(kitConfig);
Expand Down
2 changes: 1 addition & 1 deletion src/js/layouts/vertical-layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ class VerticalLayout extends Layout {
return margin + setIndex * (margin + width + 3) + barWidth * 2;
} else {
const decorPad =
'annotDecorPad' in config ? config.annotDecorPad : 0;
'legendPad' in config ? config.legendPad : 0;
translate = width + setIndex * (margin + width) + pad * 2 + decorPad;
if (pad > 0) {
return translate;
Expand Down
Loading

0 comments on commit b106ae9

Please sign in to comment.