diff --git a/src/color_legend.js b/src/color_legend.js index d771737..67c31f2 100644 --- a/src/color_legend.js +++ b/src/color_legend.js @@ -76,7 +76,7 @@ mod.directive('statusHeatmapLegend', function() { drawOpacityLegend(elem, colorOptions, rangeFrom, rangeTo, maxValue, minValue); } else if (panel.color.mode === 'discrete') { let colorOptions = panel.color; - drawDiscreteColorLegend(elem, colorOptions, rangeFrom, rangeTo, maxValue, minValue); + drawDiscreteColorLegend(elem, colorOptions, ctrl.discreteHelper); } } } @@ -150,7 +150,7 @@ function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue drawLegendValues(elem, opacityScale, rangeFrom, rangeTo, maxValue, minValue, legendWidth); } -function drawDiscreteColorLegend(elem, colorOptions, rangeFrom, rangeTo, maxValue, minValue) { +function drawDiscreteColorLegend(elem, colorOptions, discreteHelper) { let legendElem = $(elem).find('svg'); let legend = d3.select(legendElem.get(0)); clearLegend(elem); @@ -185,19 +185,18 @@ function drawDiscreteColorLegend(elem, colorOptions, rangeFrom, rangeTo, maxValu let legendHeight = legendElem.attr("height"); - let rangeStep = Math.floor(legendWidth / valuesNumber); - let valuesRange = d3.range(0, legendWidth, rangeStep); + let itemWidth = Math.floor(legendWidth / valuesNumber); + let valuesRange = d3.range(valuesNumber); // from 0 to valuesNumber-1 - let colorScale = getDiscreteColorScale(colorOptions, legendWidth); legend.selectAll(".status-heatmap-color-legend-rect") .data(valuesRange) .enter().append("rect") - .attr("x", d => d) + .attr("x", d => d*itemWidth) .attr("y", 0) - .attr("width", rangeStep + 1) // Overlap rectangles to prevent gaps + .attr("width", itemWidth + 1) // Overlap rectangles to prevent gaps .attr("height", legendHeight) .attr("stroke-width", 0) - .attr("fill", d => colorScale(d)); + .attr("fill", d => discreteHelper.getDiscreteColor(d)); drawDiscreteLegendValues(elem, colorOptions, legendWidth); } @@ -288,31 +287,6 @@ function drawDiscreteLegendValues(elem, colorOptions, legendWidth) { legend.select(".axis").select(".domain").remove(); } -function drawSimpleColorLegend(elem, colorScale) { - let legendElem = $(elem).find('svg'); - clearLegend(elem); - - let legendWidth = Math.floor(legendElem.outerWidth()); - let legendHeight = legendElem.attr("height"); - - if (legendWidth) { - let valuesNumber = Math.floor(legendWidth / 2); - let rangeStep = Math.floor(legendWidth / valuesNumber); - let valuesRange = d3.range(0, legendWidth, rangeStep); - - let legend = d3.select(legendElem.get(0)); - var legendRects = legend.selectAll(".status-heatmap-color-legend-rect").data(valuesRange); - - legendRects.enter().append("rect") - .attr("x", d => d) - .attr("y", 0) - .attr("width", rangeStep + 1) // Overlap rectangles to prevent gaps - .attr("height", legendHeight) - .attr("stroke-width", 0) - .attr("fill", d => colorScale(d)); - } -} - function drawSimpleOpacityLegend(elem, options) { let legendElem = $(elem).find('svg'); let graphElem = $(elem); @@ -366,36 +340,6 @@ function getColorScale(colorScheme, maxValue, minValue = 0) { return d3.scaleSequential(colorInterpolator).domain([start, end]); } -// scale input range to discrete colors to draw a legend -function getDiscreteColorScale(colorOptions, maxValue, minValue = 0) { - let start = minValue; - let end = maxValue; - - let thresholdValues = []; - let thresholdColors = []; - for (let i = 0; i < colorOptions.thresholds.length; i++) { - thresholdColors.push(colorOptions.thresholds[i].color); - thresholdValues.push(colorOptions.thresholds[i].value); - } - - // TODO sort colors by value and index? - - let thresholdScaler = (d) => { - let color = thresholdColors[Math.floor(d)]; - if (color != undefined) { - return color - } - return 'rgba(0,0,0,0)'; - }; - - let inputRangeScaler = d3.scaleLinear().domain([start, end]).range([0, thresholdColors.length+1]); - - // scale min-max to 0 - max-thrs-value - return function(d) { - return thresholdScaler(inputRangeScaler(d)); - } -} - function getOpacityScale(options, maxValue, minValue = 0) { let legendOpacityScale; if (options.colorScale === 'linear') { diff --git a/src/color_mode_discrete.js b/src/color_mode_discrete.js index 6adb033..a68b7fb 100644 --- a/src/color_mode_discrete.js +++ b/src/color_mode_discrete.js @@ -17,7 +17,7 @@ export class ColorModeDiscrete { for (let j = 0; j < values.length; j++) { if (values[j] == thresholds[i].value) { tooltips.push({ - "tooltip": thresholds[i].tooltip, + "tooltip": thresholds[i].tooltip?thresholds[i].tooltip:values[j], "color": thresholds[i].color }); } @@ -37,25 +37,49 @@ export class ColorModeDiscrete { return notMatched; } - getDiscreteColorScale() { - return (d) => { - let threshold = this.getMatchedThreshold(d); - if (!threshold) { - // Error if value not in thresholds - return 'rgba(0,0,0,1)'; + getNotColoredValues(values) { + let notMatched = []; + for (let j = 0; j < values.length; j++) { + let threshold = this.getMatchedThreshold(values[j]); + if (!threshold || !threshold.color || threshold.color == "") { + notMatched.push(values[j]); } - else { - return threshold.color; + } + return notMatched; + } + + + getDiscreteColor(index) { + let color = this.getThreshold(index).color; + if (!color || color == "") { + return 'rgba(0,0,0,1)'; + } + return color; + } + + // returns color from first matched thresold in order from 0 to thresholds.length + getBucketColor(values) { + let thresholds = this.panel.color.thresholds; + + for (let i = 0; i < thresholds.length; i++) { + for (let j = 0; j < values.length; j++) { + if (values[j] == thresholds[i].value) { + return this.getDiscreteColor(i); + } } - }; + } + return 'rgba(0,0,0,1)'; } updateCardsValuesHasColorInfo() { + this.panelCtrl.cardsData.noColorDefined = false; let cards = this.panelCtrl.cardsData.cards; for (let i=0; i= thresholds.length == null) { + return { + "color": "rgba(0,0,0,0)", + "value": "null", + "tooltip": "null", + } + } + return thresholds[index]; + } + roundIntervalCeil(interval) { switch (true) { case interval <= 10: diff --git a/src/rendering.js b/src/rendering.js index eac7ece..9f5ddfd 100644 --- a/src/rendering.js +++ b/src/rendering.js @@ -266,9 +266,7 @@ export default function link(scope, elem, attrs, ctrl) { let maxValue = panel.color.max || cardsData.maxValue; let minValue = panel.color.min || cardsData.minValue; - if (panel.color.mode === 'discrete') { - colorScale = ctrl.discreteHelper.getDiscreteColorScale(); - } else { + if (panel.color.mode !== 'discrete') { colorScale = getColorScale(maxValue, minValue); } setOpacityScale(maxValue); @@ -421,7 +419,7 @@ export default function link(scope, elem, attrs, ctrl) { } else if (panel.color.mode === 'spectrum') { return colorScale(d.value); } else if (panel.color.mode === 'discrete') { - return colorScale(d.value); + return ctrl.discreteHelper.getBucketColor(d.values); } } diff --git a/src/tooltip.js b/src/tooltip.js index 29dd4b1..571c8b0 100644 --- a/src/tooltip.js +++ b/src/tooltip.js @@ -85,18 +85,17 @@ export class StatusHeatmapTooltip { let statuses = this.panelCtrl.discreteHelper.convertValuesToTooltips(values); let statusesHtml = ''; if (statuses.length === 1) { - statusesHtml = ` - status: ${statuses[0].tooltip}`; + statusesHtml = "status:"; } else if (statuses.length > 1) { - statusesHtml = ` - statuses: - `; + statusesHtml = "statuses:"; } - tooltipHtml += `
- name: ${y}
- ${statusesHtml} + tooltipHtml += ` +
+ name: ${y}
+ ${statusesHtml} +
    + ${_.join(_.map(statuses, v => `
  • ${v.tooltip}
  • `), "")} +
`; } else { if (values.length === 1) { @@ -123,9 +122,9 @@ export class StatusHeatmapTooltip { // Discrete mode errors if (this.panel.color.mode === 'discrete') { if (card.noColorDefined) { - let badValues = this.panelCtrl.discreteHelper.getNotMatchedValues(values); + let badValues = this.panelCtrl.discreteHelper.getNotColoredValues(values); tooltipHtml += `
Error: ${this.panelCtrl.dataWarnings.noColorDefined.title} -
bad values: +
not colored values:
    ${_.join(_.map(badValues, v => `
  • ${v}
  • `), "")}