diff --git a/apps/heatmap/uicallbacks.js b/apps/heatmap/uicallbacks.js
index 39de17cc1..6eddd0d79 100644
--- a/apps/heatmap/uicallbacks.js
+++ b/apps/heatmap/uicallbacks.js
@@ -171,17 +171,19 @@ function closeSecondaryViewer(){
function goHome(data){
redirect($D.pages.home,`GO Home Page`, 0);
}
-
+// function to update and repaint the heatmap
function heatmapSettingChanged(data){
switch (data.mode) {
case 'binal':
- data.fields.forEach( f=> {
- $CAMIC.viewer.heatmap.setThresholdsByName(f.name,f.range[0],f.range[1],false);
- });
+ data.fields.forEach( f=> {
+ $CAMIC.viewer.heatmap.setThresholdsByName(f.name,f.range[0],f.range[1],false);
+ });
+ if(data.color) $CAMIC.viewer.heatmap.setColor(data.color)
break;
case 'gradient':
$CAMIC.viewer.heatmap.setThresholdsByName(data.field.name,data.field.range[0],data.field.range[1],false);
$CAMIC.viewer.heatmap.setCurrentField(data.field.name,false);
+ if(data.colors) $CAMIC.viewer.heatmap.setColors(data.colors)
break;
default:
// statements_def
diff --git a/components/heatmapcontrol/heatmapcontrol.css b/components/heatmapcontrol/heatmapcontrol.css
index 48b08f72d..5d3721994 100644
--- a/components/heatmapcontrol/heatmapcontrol.css
+++ b/components/heatmapcontrol/heatmapcontrol.css
@@ -8,17 +8,21 @@
color: #365f9c;
}
-.hmc-container > label,.hmc-container .mode-panel > label{
+.hmc-container > label,.hmc-container .mode-panel > label, .color-panel > label, .colors-legend-panel > label{
font-size: 14px;
}
-.mode-panel {
+.mode-panel, .color-panel, .colors-legend-panel, .colors-legend-panel > .legends {
margin-top:.5rem;
margin-bottom:.5rem;
}
+.color-input-container {
+ margin-top:.5rem;
+ margin-bottom:.5rem;
+}
.fields-panel, .opacity-panel {
padding:10px;
}
.sel-field-panel > label, .fields-panel label, .opacity-panel label {
font-size:13px;
-}
\ No newline at end of file
+}
diff --git a/components/heatmapcontrol/heatmapcontrol.js b/components/heatmapcontrol/heatmapcontrol.js
index fa2c3045b..ac18ec60a 100644
--- a/components/heatmapcontrol/heatmapcontrol.js
+++ b/components/heatmapcontrol/heatmapcontrol.js
@@ -1,5 +1,10 @@
// heatmapcontrol.js
//
+
+//Default Color List for gradient view
+const defaultColorList = ["#EAF2F8", "#D4E6F1", "#A9CCE3", "#7FB3D5", "#5499C7", "#2980B9", "#2471A3", "#1F618D", "#1A5276", "#154360"];
+//Regular Expression for testing valid color values
+const cssHexRegExp = new RegExp('^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$');
function HeatmapControl(options){
this.name = 'HeatmapControl';
/*
@@ -50,25 +55,36 @@ HeatmapControl.prototype.__refresh = function(){
const template = `
-
+
-
+
-
+
-
+
-
-
+
+
+
`;
this.elt.innerHTML = template;
const checkbox = this.elt.querySelector('.mode-panel input[type=checkbox]');
checkbox.addEventListener('change', this._modeChanged.bind(this));
//
+ const color = this.elt.querySelector('.color-panel input[type=color]');
+ color.addEventListener('input', this._colorChanged.bind(this));
+
this.rangeSliders = {};
createSelect(this.elt.querySelector('.sel-field-panel select') ,this.setting.fields,this.setting.currentField);
this.elt.querySelector('.sel-field-panel select').addEventListener('change', this._selChanged.bind(this));
@@ -86,6 +102,34 @@ HeatmapControl.prototype.__refresh = function(){
this.opacitySliders[f.name] = createOpacities(opacitiesPanel,f,this.__opacityChange.bind(this));
},this);
+ const colorsLegendPanel = this.elt.querySelector('.colors-legend-panel');
+ //min max logic for valid number of intervals
+ $(colorsLegendPanel).find('#legendIntervals').on('change', function(e){
+ var min=parseFloat($(this).attr('min'));
+ var max=parseFloat($(this).attr('max'));
+ var curr=parseFloat($(this).val());
+ if (curr > max) { $(this).val(max); var changed=true; }
+ if (curr < min) { $(this).val(min); var changed=true; }
+ if (changed) {
+ $warning = $(colorsLegendPanel).find('.warning')
+ $warning.text('Only values in ' + min + ' through ' + max + ' allowed.');
+ $warning.show()
+ $warning.fadeOut(4500);
+ }
+ });
+
+ const legendIntervalsInput = colorsLegendPanel.querySelector("#legendIntervals");
+ //Setting default value of intervals
+ legendIntervalsInput.value = 5;
+ const noOfIntervals = legendIntervalsInput.value;
+
+ const legendsContainer = colorsLegendPanel.querySelector('.legends');
+ createIntervalInputs(legendsContainer, noOfIntervals, this._legendColorsChanged.bind(this) );
+ legendIntervalsInput.addEventListener('change', ()=>{
+ createIntervalInputs(legendsContainer, legendIntervalsInput.value, this._legendColorsChanged.bind(this))
+ this._legendColorsChanged();
+ });
+
this._modeChanged(false);
}
@@ -93,6 +137,8 @@ HeatmapControl.prototype._modeChanged = function(flag = true){
const mode = this.elt.querySelector(`.mode-panel input[type=checkbox]`).checked;
if(!mode){// binal
+ this.elt.querySelector('.color-panel').style.display='';
+ this.elt.querySelector('.colors-legend-panel').style.display='none';
this.elt.querySelector('.sel-field-panel').style.display='none';
this.setting.fields.forEach( f=> {
// statements
@@ -100,6 +146,8 @@ HeatmapControl.prototype._modeChanged = function(flag = true){
this.rangeSliders[f.name].disabled(false);
},this);
}else{ // gradient
+ this.elt.querySelector('.color-panel').style.display='none';
+ this.elt.querySelector('.colors-legend-panel').style.display='';
this.elt.querySelector('.sel-field-panel').style.display='';
const selectedField = this.elt.querySelector('.sel-field-panel select').value;
this.rangeSliders[selectedField].slider.parentNode.style.display='';
@@ -115,6 +163,27 @@ HeatmapControl.prototype._modeChanged = function(flag = true){
}
if(flag)this.__change.call(this);
}
+//To validate and update heatmap color in binal mode
+HeatmapControl.prototype._colorChanged = function(){
+ const color = this.elt.querySelector("#heatMapColor").value;
+ if(cssHexRegExp.test(color)){
+ this.__change.call(this);
+ }
+}
+//To validate and update heatmap colors in gradient mode
+HeatmapControl.prototype._legendColorsChanged = function(){
+ let valid = true;
+ const colorLegendPanel = this.elt.querySelector('.colors-legend-panel');
+ $(colorLegendPanel.querySelector('.legends'))
+ .children()
+ .each(function (index,colorDiv) {
+ if(cssHexRegExp.test(colorDiv.querySelector('input').value)===false){
+ valid = false;
+ return;
+ }
+ });
+ if(valid) this.__change.call(this);
+}
HeatmapControl.prototype._selChanged = function(e){
const selectedField = this.elt.querySelector('.sel-field-panel select').value;
@@ -142,12 +211,19 @@ HeatmapControl.prototype.resize = function(){
}
HeatmapControl.prototype.__change = function(){
if(this.setting.onChange && typeof this.setting.onChange === 'function'){
+
const mode = this.elt.querySelector(`.mode-panel input[type=checkbox]`).checked;
+ const color = this.elt.querySelector("#heatMapColor").value
+ const colorLegendPanel = this.elt.querySelector('.colors-legend-panel');
+ const colors = getColors(colorLegendPanel.querySelector('.legends'));
const fields = [];
const field = {};
const data = {
- mode:mode?'gradient':'binal'
+ mode:mode?'gradient':'binal',
+ color:color,
+ colors:colors
}
+
if(!mode){
this.setting.fields.forEach( f=> {
fields.push({name:f.name,range:this.rangeSliders[f.name].getValue()});
@@ -240,3 +316,37 @@ function createOpacities(container, field, changeFunc){
container.appendChild(div);
return rs;
}
+//Create HTML Color inputs for given noOfIntervals
+function createIntervalInputs(container, noOfIntervals, changeFunc){
+ //Empty the container
+ while ( container.firstChild ) container.removeChild( container.firstChild );
+ for (let i = 1; i <= noOfIntervals; i++) {
+
+ const div = document.createElement('div');
+ div.className = 'color-input-container';
+ const label = document.createElement('label');
+ label.textContent = `Interval ${i} `;
+ label.className = 'color-input'
+ const color = document.createElement('input');
+ color.type = 'color';
+ color.value = defaultColorList[getGradientColorIndex(i, noOfIntervals)];
+ color.oninput = changeFunc
+ //Input for color legends.
+ div.appendChild(label);
+ div.appendChild(color);
+
+ container.appendChild(div);
+ }
+}
+// returns selected colors for intervals
+function getColors(container){
+ const rs = [];
+ $(container).children().each(function (index,colorDiv) {
+ rs.push(colorDiv.querySelector('input').value)
+ });
+ return rs;
+}
+// returns index of color in defaultColorList for given position and no of intervals
+function getGradientColorIndex(position, noOfIntervals){
+ return parseInt((position * (10 / noOfIntervals)) - 1)
+}
diff --git a/core/extension/osd-heatmap-overlay.js b/core/extension/osd-heatmap-overlay.js
index bbf9b1850..d054c7894 100644
--- a/core/extension/osd-heatmap-overlay.js
+++ b/core/extension/osd-heatmap-overlay.js
@@ -1,4 +1,5 @@
//osd-heatmap-overlay.js
+
/**
* @constructor
* OpenSeadragon heatmap Plugin 0.0.1.
@@ -482,6 +483,7 @@
setColors: function(colors) {
if (!Array.isArray(colors) || colors.length < 2) return;
this._colors = colors;
+ this._steps = colors.length + 1;
// refresh view/heatmap/ui if the heatmap is in 'gradient' mode
if (this.mode == "gradient") {
this.drawOnCanvas();
@@ -805,25 +807,34 @@
//
// const colorList = interpolateColors(hexToRgb(colors[0]),hexToRgb(colors[1]),steps);
- const colorList = ["#2b83ba", "#abdda4", "#ffffbf", "#fdae61", "#d7191c"];
+ // Default preset of colors
+ const defaultColorList = ["#2b83ba", "#abdda4", "#ffffbf", "#fdae61", "#d7191c"];
+
+ if(colors.length + 1 != steps){
+ console.log(`Number of colors ${colors.length + 1 } required for steps ${steps} are not right. Switch to default colors`)
+ colors = defaultColorList;
+ steps = defaultColorList.length + 1;
+ }
+
+
//const colorList = ['#f2f0f7','#cbc9e2','#9e9ac8','#756bb1','#54278f'];
//const colorList = ['#eff3ff','#bdd7e7','#6baed6','#3182bd','#08519c'];
//const colorList = ['#fee5d9','#fcae91','#fb6a4a','#de2d26','#a50f15'];
// const colorList = ['#feedde','#fdbe85','#fd8d3c','#e6550d','#a63603'];
//const colorList = ['#edf8e9','#bae4b3','#74c476','#31a354','#006d2c'];
- steps = 5;
+
// get a boundary list of intervals
const threstholds = field.value;
const boundaries = interpolateNums(
threstholds[0],
threstholds[1],
- steps + 1
+ steps
);
const rs = [];
- for (let i = 0; i < colorList.length; i++) {
+ for (let i = 0; i < colors.length; i++) {
// create a new interval
rs.push({
- color: colorList[i],
+ color: colors[i],
range: [boundaries[i], boundaries[i + 1]],
data: []
});
@@ -1128,7 +1139,7 @@
};
function createLegend(container, intervals) {
container.innerHTML = "";
- container.innerHTML = intervals.reverse()
+ container.innerHTML = intervals
.map(
item =>
`