Last active
August 29, 2015 14:00
-
-
Save florin-chelaru/11279449 to your computer and use it in GitHub Desktop.
Exon Track
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
epiviz.EpiViz.SETTINGS.chartTypes.push('epiviz.plugins.charts.ExonTrackType'); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Created by Florin Chelaru ( florinc [at] umd [dot] edu ) | |
* Date: 4/25/14 | |
* Time: 12:36 AM | |
*/ | |
goog.provide('epiviz.plugins.charts.ExonTrackType'); | |
goog.require('epiviz.ui.charts.Chart'); | |
/** | |
* @param {epiviz.Config} config | |
* @extends {epiviz.ui.charts.TrackType} | |
* @constructor | |
*/ | |
epiviz.plugins.charts.ExonTrackType = function(config) { | |
// Call superclass constructor | |
epiviz.ui.charts.TrackType.call(this, config); | |
}; | |
/* | |
* Copy methods from upper class | |
*/ | |
epiviz.plugins.charts.ExonTrackType.prototype = epiviz.utils.mapCopy(epiviz.ui.charts.TrackType.prototype); | |
epiviz.plugins.charts.ExonTrackType.constructor = epiviz.plugins.charts.ExonTrackType; | |
/** | |
* @param {string} id | |
* @param {jQuery} container The div where the chart will be drawn | |
* @param {epiviz.ui.charts.ChartProperties} properties | |
* @returns {epiviz.plugins.charts.ExonTrack} | |
*/ | |
epiviz.plugins.charts.ExonTrackType.prototype.createNew = function(id, container, properties) { | |
return new epiviz.plugins.charts.ExonTrack(id, container, properties); | |
}; | |
/** | |
* @returns {string} | |
*/ | |
epiviz.plugins.charts.ExonTrackType.prototype.typeName = function() { | |
return 'epiviz.plugins.charts.ExonTrack'; | |
}; | |
/** | |
* @returns {string} | |
*/ | |
epiviz.plugins.charts.ExonTrackType.prototype.chartName = function() { | |
return 'Exon Track'; | |
}; | |
/** | |
* @returns {string} | |
*/ | |
epiviz.plugins.charts.ExonTrackType.prototype.chartHtmlAttributeName = function() { | |
return 'exons'; | |
}; | |
/** | |
* @returns {epiviz.measurements.Measurement.Type} | |
*/ | |
epiviz.plugins.charts.ExonTrackType.prototype.chartContentType = function() { | |
return epiviz.measurements.Measurement.Type.FEATURE; | |
}; | |
/** | |
* @returns {Array.<epiviz.ui.charts.CustomSetting>} | |
*/ | |
epiviz.plugins.charts.ExonTrackType.prototype.customSettingsDefs = function() { | |
return epiviz.ui.charts.TrackType.prototype.customSettingsDefs.call(this).concat([ | |
new epiviz.ui.charts.CustomSetting( | |
epiviz.plugins.charts.ExonTrackType.CustomSettings.GROUP_LABEL, | |
epiviz.ui.charts.CustomSetting.Type.STRING, | |
'symbol', | |
'Group label'), | |
new epiviz.ui.charts.CustomSetting( | |
epiviz.plugins.charts.ExonTrackType.CustomSettings.SHOW_POINTS, | |
epiviz.ui.charts.CustomSetting.Type.BOOLEAN, | |
false, | |
'Show points'), | |
new epiviz.ui.charts.CustomSetting( | |
epiviz.plugins.charts.ExonTrackType.CustomSettings.SHOW_LINES, | |
epiviz.ui.charts.CustomSetting.Type.BOOLEAN, | |
true, | |
'Show lines'), | |
new epiviz.ui.charts.CustomSetting( | |
epiviz.plugins.charts.ExonTrackType.CustomSettings.POINT_RADIUS, | |
epiviz.ui.charts.CustomSetting.Type.NUMBER, | |
1, | |
'Point radius'), | |
new epiviz.ui.charts.CustomSetting( | |
epiviz.plugins.charts.ExonTrackType.CustomSettings.LINE_THICKNESS, | |
epiviz.ui.charts.CustomSetting.Type.NUMBER, | |
1, | |
'Line thickness'), | |
new epiviz.ui.charts.CustomSetting( | |
epiviz.ui.charts.ChartType.CustomSettings.Y_MIN, | |
epiviz.ui.charts.CustomSetting.Type.NUMBER, | |
epiviz.ui.charts.CustomSetting.DEFAULT, | |
'Min Y'), | |
new epiviz.ui.charts.CustomSetting( | |
epiviz.ui.charts.ChartType.CustomSettings.Y_MAX, | |
epiviz.ui.charts.CustomSetting.Type.NUMBER, | |
epiviz.ui.charts.CustomSetting.DEFAULT, | |
'Max Y'), | |
new epiviz.ui.charts.CustomSetting( | |
epiviz.plugins.charts.ExonTrackType.CustomSettings.INTERPOLATION, | |
epiviz.ui.charts.CustomSetting.Type.CATEGORICAL, | |
'step-after', | |
'Interpolation', | |
['linear', 'step-before', 'step-after', 'basis', 'basis-open', 'basis-closed', 'bundle', 'cardinal', 'cardinal-open', 'monotone']) | |
]); | |
}; | |
/** | |
* @enum {string} | |
*/ | |
epiviz.plugins.charts.ExonTrackType.CustomSettings = { | |
GROUP_LABEL: 'groupLabel', | |
SHOW_POINTS: 'showPoints', | |
SHOW_LINES: 'showLines', | |
POINT_RADIUS: 'pointRadius', | |
LINE_THICKNESS: 'lineThickness', | |
INTERPOLATION: 'interpolation' | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Created by Florin Chelaru ( florinc [at] umd [dot] edu ) | |
* Date: 4/25/14 | |
* Time: 12:34 AM | |
*/ | |
goog.provide('epiviz.plugins.charts.ExonTrack'); | |
/** | |
* @param {string} id | |
* @param {jQuery} container | |
* @param {epiviz.ui.charts.ChartProperties} properties | |
* @extends {epiviz.ui.charts.Track} | |
* @constructor | |
*/ | |
epiviz.plugins.charts.ExonTrack = function(id, container, properties) { | |
// Call superclass constructor | |
epiviz.ui.charts.Track.call(this, id, container, properties); | |
this._initialize(); | |
}; | |
/* | |
* Copy methods from upper class | |
*/ | |
epiviz.plugins.charts.ExonTrack.prototype = epiviz.utils.mapCopy(epiviz.ui.charts.Track.prototype); | |
epiviz.plugins.charts.ExonTrack.constructor = epiviz.plugins.charts.ExonTrack; | |
/** | |
* @protected | |
*/ | |
epiviz.plugins.charts.ExonTrack.prototype._initialize = function() { | |
// Call super | |
epiviz.ui.charts.Track.prototype._initialize.call(this); | |
}; | |
/** | |
* @param {epiviz.datatypes.GenomicRange} [range] | |
* @param {?epiviz.measurements.MeasurementHashtable.<epiviz.datatypes.GenomicDataMeasurementWrapper>} [data] | |
* @param {number} [slide] | |
* @param {number} [zoom] | |
* @returns {Array.<epiviz.ui.charts.UiObject>} The objects drawn | |
*/ | |
epiviz.plugins.charts.ExonTrack.prototype.draw = function(range, data, slide, zoom) { | |
var lastRange = this._lastRange; | |
epiviz.ui.charts.Track.prototype.draw.call(this, range, data, slide, zoom); | |
// If data is defined, then the base class sets this._lastData to data. | |
// If it isn't, then we'll use the data from the last draw call | |
data = this._lastData; | |
range = this._lastRange; | |
if (lastRange && range && lastRange.overlapsWith(range) && lastRange.width() == range.width()) { | |
slide = range.start() - lastRange.start(); | |
} | |
// If data is not defined, there is nothing to draw | |
if (!data || !range) { return []; } | |
var CustomSetting = epiviz.ui.charts.CustomSetting; | |
var minY = this._customSettingsValues[epiviz.ui.charts.ChartType.CustomSettings.Y_MIN]; | |
var maxY = this._customSettingsValues[epiviz.ui.charts.ChartType.CustomSettings.Y_MAX]; | |
if (minY == CustomSetting.DEFAULT) { | |
minY = null; | |
this.measurements().foreach(function(m) { | |
if (m === null) { return; } | |
if (minY === null || m.minValue() < minY) { minY = m.minValue(); } | |
}); | |
} | |
if (maxY == CustomSetting.DEFAULT) { | |
maxY = null; | |
this.measurements().foreach(function(m) { | |
if (m === null) { return; } | |
if (maxY === null || m.maxValue() > maxY) { maxY = m.maxValue(); } | |
}); | |
} | |
if (minY === null && maxY === null) { minY = -1; maxY = 1; } | |
if (minY === null) { minY = maxY - 1; } | |
if (maxY === null) { maxY = minY + 1; } | |
var Axis = epiviz.ui.charts.Axis; | |
var xScale = d3.scale.linear() | |
.domain([range.start(), range.end()]) | |
.range([0, this.width() - this.margins().sumAxis(Axis.X)]); | |
var yScale = d3.scale.linear() | |
.domain([minY, maxY]) | |
.range([this.height() - this.margins().sumAxis(Axis.Y), 0]); | |
this._clearAxes(); | |
this._drawAxes(xScale, yScale, 10, 5); | |
slide = slide || 0; | |
var delta = slide * (this.width() - this.margins().sumAxis(Axis.X)) / range.width(); | |
var linesGroup = this._svg.selectAll('.lines'); | |
if (linesGroup.empty()) { | |
var graph = this._svg.append('g') | |
.attr('class', 'lines') | |
.attr('transform', 'translate(' + this.margins().left() + ', ' + this.margins().top() + ')'); | |
this.measurements().foreach(function(m, i) { | |
graph.append('g').attr('class', 'line-series-index-' + i); | |
graph.append('g').attr('class', 'point-series-index-' + i); | |
}); | |
} | |
return this._drawLines(range, data, delta, zoom || 1, xScale, yScale); | |
}; | |
/** | |
* @param {epiviz.datatypes.GenomicRange} range | |
* @param {epiviz.measurements.MeasurementHashtable.<epiviz.datatypes.GenomicDataMeasurementWrapper>} data | |
* @param {number} delta | |
* @param {number} zoom | |
* @param {function} xScale D3 linear scale | |
* @param {function} yScale D3 linear scale | |
* @returns {Array.<epiviz.ui.charts.UiObject>} The objects drawn | |
* @private | |
*/ | |
epiviz.plugins.charts.ExonTrack.prototype._drawLines = function(range, data, delta, zoom, xScale, yScale) { | |
var self = this; | |
var invXScale = d3.scale.linear() | |
.domain([0, this.width() - this.margins().sumAxis(epiviz.ui.charts.Axis.X)]) | |
.range([range.start(), range.end()]); | |
var deltaInBp = invXScale(delta) - range.start(); | |
var extendedRange = epiviz.datatypes.GenomicRange.fromStartEnd( | |
range.seqName(), | |
Math.min(range.start(), range.start() + deltaInBp), | |
Math.max(range.end(), range.end() + deltaInBp)); | |
var graph = this._svg.select('.lines'); | |
/** @type {Array.<epiviz.ui.charts.UiObject>} */ | |
var items = []; | |
/** @type {string} */ | |
var groupLabel = this._customSettingsValues[epiviz.plugins.charts.ExonTrackType.CustomSettings.GROUP_LABEL]; | |
this.measurements().foreach(function(m, i) { | |
/** @type {epiviz.datatypes.GenomicDataMeasurementWrapper} */ | |
var series = data.get(m); | |
/** @type {{index: ?number, length: number}} */ | |
var drawBoundaries = series.binarySearchStarts(extendedRange); | |
if (drawBoundaries.length == 0) { return; } | |
// Also take the last point that won't be displayed on the left side | |
if (drawBoundaries.index > 0) { | |
--drawBoundaries.index; | |
++drawBoundaries.length; | |
} | |
// And the first point on the right side that won't be displayed | |
if (drawBoundaries.index + drawBoundaries.length < series.size()) { | |
++drawBoundaries.length; | |
} | |
var indices = epiviz.utils.range(drawBoundaries.length, drawBoundaries.index); | |
var itemIndicesMap = {}; | |
for (var k = 0; k < indices.length; ++k) { | |
var cell = series.get(indices[k]); | |
var group = cell.rowItem.metadata(groupLabel); | |
if (group == undefined) { continue; } | |
var o; | |
if (itemIndicesMap[group] == undefined) { | |
o = new epiviz.ui.charts.UiObject(sprintf('line_%s_%s', i, group), cell.rowItem.start(), cell.rowItem.end(), [cell.value], i, [[cell]], [m], sprintf('item data-series-%s', i)); | |
itemIndicesMap[group] = items.length; | |
items.push(o); | |
continue; | |
} | |
o = items[itemIndicesMap[group]]; | |
o.start = Math.min(o.start, cell.rowItem.start()); | |
o.end = Math.max(o.end, cell.rowItem.end()); | |
o.values[0] = (o.values[0] * o.valueItems[0].length + cell.value) / (o.valueItems[0].length + 1); | |
o.valueItems[0].push(cell); | |
} | |
var itemsGroup = graph.select('.line-series-index-' + i); | |
var selection = itemsGroup.selectAll('.item') | |
.data(items, function(d) { return d.id; }); | |
selection | |
.enter() | |
.append('g') | |
.on('mouseout', function (d) { | |
self._unhover.notify(); | |
}) | |
.on('mouseover', function (d) { | |
self._hover.notify(d); | |
}) | |
.on('click', function(d) { | |
self._deselect.notify(); | |
self._select.notify(d); | |
d3.event.stopPropagation(); | |
}); | |
selection | |
.each(function(d) { | |
self._drawGroup(this, d, xScale, yScale); | |
}) | |
.attr('transform', 'translate(' + (+delta) + ')') | |
.transition() | |
.duration(500) | |
.attr('transform', 'translate(' + (0) + ')'); | |
selection | |
.exit() | |
.remove(); | |
}); | |
return items; | |
}; | |
/** | |
* @param elem | |
* @param {epiviz.ui.charts.UiObject} d | |
* @param {function(number): number} xScale | |
* @param {function(number): number} yScale | |
* @private | |
*/ | |
epiviz.plugins.charts.ExonTrack.prototype._drawGroup = function(elem, d, xScale, yScale) { | |
var interpolation = this._customSettingsValues[epiviz.plugins.charts.ExonTrackType.CustomSettings.INTERPOLATION]; | |
/** @type {number} */ | |
var lineThickness = this._customSettingsValues[epiviz.plugins.charts.ExonTrackType.CustomSettings.LINE_THICKNESS]; | |
/** @type {number} */ | |
var pointRadius = this._customSettingsValues[epiviz.plugins.charts.ExonTrackType.CustomSettings.POINT_RADIUS]; | |
/** @type {boolean} */ | |
var showPoints = this._customSettingsValues[epiviz.plugins.charts.ExonTrackType.CustomSettings.SHOW_POINTS]; | |
/** @type {boolean} */ | |
var showLines = this._customSettingsValues[epiviz.plugins.charts.ExonTrackType.CustomSettings.SHOW_LINES]; | |
var group = d3.select(elem); | |
group.attr('class', d.cssClasses); | |
var x = function(i) { | |
var index = Math.floor(i / 2); | |
if (i % 2 == 0) { return xScale(d.valueItems[0][index].rowItem.start()); } | |
else { return xScale(d.valueItems[0][index].rowItem.end());} | |
}; | |
var y = function(i) { | |
var index = Math.floor(i / 2); | |
return yScale(d.valueItems[0][index].value); | |
}; | |
var line = d3.svg.line() | |
.x(x).y(y) | |
.interpolate(interpolation); | |
if (showLines) { | |
var lineSelection = group | |
.selectAll('path') | |
.data([epiviz.utils.range(d.valueItems[0].length * 2)]); | |
lineSelection | |
.enter() | |
.append('path') | |
.style('shape-rendering', 'auto') | |
.style('stroke-opacity', '0.7'); | |
lineSelection | |
.attr('d', line) | |
.style('stroke', this.colors().get(d.seriesIndex)) | |
.style('stroke-width', lineThickness); | |
} else { | |
group.selectAll('path').remove(); | |
} | |
if (showPoints) { | |
var pointSelection = group | |
.selectAll('circle') | |
.data(epiviz.utils.range(d.valueItems[0].length * 2)); | |
pointSelection | |
.enter() | |
.append('circle'); | |
pointSelection | |
.attr('r', pointRadius) | |
.attr('cx', x) | |
.attr('cy', y) | |
.attr('fill', this.colors().get(d.seriesIndex)) | |
.attr('fill-opacity', 0.1) | |
.attr('stroke', this.colors().get(d.seriesIndex)); | |
} else { | |
group.selectAll('circle').remove(); | |
} | |
}; | |
/** | |
* @returns {string} | |
*/ | |
epiviz.plugins.charts.ExonTrack.prototype.chartTypeName = function() { return 'epiviz.plugins.charts.ExonTrack'; }; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment