This is an automated email from the ASF dual-hosted git repository.
zehnder pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git
The following commit(s) were added to refs/heads/dev by this push:
new bb8429e2d1 fix: resolve color mapping issue (#3549)
bb8429e2d1 is described below
commit bb8429e2d19e3111473437ce26b316e6b622e261
Author: Marcel Früholz <[email protected]>
AuthorDate: Tue Apr 1 07:34:38 2025 +0200
fix: resolve color mapping issue (#3549)
---
.../color-mapping-options-config.component.ts | 21 +--
.../config/pie-chart-widget-config.component.html | 4 +
.../config/pie-chart-widget-config.component.ts | 2 +-
.../charts/pie/model/pie-chart-widget.model.ts | 2 +-
.../components/charts/pie/pie-renderer.service.ts | 17 +-
.../status-heatmap-widget-config.component.html | 4 +
.../status-heatmap-widget-config.component.ts | 4 +-
.../model/status-heatmap-widget.model.ts | 2 +-
.../status-heatmap-renderer.service.ts | 172 ++++++++++-----------
.../services/color-mapping.service.ts | 30 ++--
10 files changed, 133 insertions(+), 125 deletions(-)
diff --git
a/ui/src/app/data-explorer-shared/components/chart-config/color-mapping-options-config/color-mapping-options-config.component.ts
b/ui/src/app/data-explorer-shared/components/chart-config/color-mapping-options-config/color-mapping-options-config.component.ts
index f31dececc7..f52bf378ce 100644
---
a/ui/src/app/data-explorer-shared/components/chart-config/color-mapping-options-config/color-mapping-options-config.component.ts
+++
b/ui/src/app/data-explorer-shared/components/chart-config/color-mapping-options-config/color-mapping-options-config.component.ts
@@ -37,6 +37,8 @@ export class ColorMappingOptionsConfigComponent implements
OnInit, OnChanges {
@Input() selectedProperty: DataExplorerField;
+ @Input() showCustomColorMapping: boolean;
+
@Output()
viewRefreshEmitter: EventEmitter<void> = new EventEmitter<void>();
@@ -45,15 +47,15 @@ export class ColorMappingOptionsConfigComponent implements
OnInit, OnChanges {
{ value: string; label: string; color: string }[]
> = new EventEmitter();
+ @Output() showCustomColorMappingChange = new EventEmitter<boolean>();
+
protected isSelectedPropertyBoolean: boolean;
- protected showCustomColorMapping: boolean;
private wasPreviousFieldBoolean: boolean;
constructor(private colorMappingService: ColorMappingService) {}
ngOnInit(): void {
this.isSelectedPropertyBoolean = this.isBooleanPropertySelected();
- this.showCustomColorMapping ??= false;
this.resetColorMappings();
}
@@ -68,10 +70,8 @@ export class ColorMappingOptionsConfigComponent implements
OnInit, OnChanges {
}
resetColorMappings(): void {
- const isNowBoolean = this.isBooleanPropertySelected();
-
if (!this.showCustomColorMapping) {
- if (isNowBoolean) {
+ if (this.isBooleanPropertySelected()) {
this.colorMapping = [
{ value: 'true', label: '', color: '#66BB66' },
{ value: 'false', label: '', color: '#BB6666' },
@@ -80,7 +80,7 @@ export class ColorMappingOptionsConfigComponent implements
OnInit, OnChanges {
this.colorMapping = [];
}
}
- if (isNowBoolean) {
+ if (this.isBooleanPropertySelected()) {
if (
!(this.colorMapping ?? []).some(
mapping =>
@@ -97,9 +97,11 @@ export class ColorMappingOptionsConfigComponent implements
OnInit, OnChanges {
this.colorMapping = [];
}
}
- this.wasPreviousFieldBoolean = isNowBoolean;
- this.colorMappingChange.emit(this.colorMapping);
- this.viewRefreshEmitter.emit();
+ this.wasPreviousFieldBoolean = this.isBooleanPropertySelected();
+ setTimeout(() => {
+ this.colorMappingChange.emit(this.colorMapping);
+ this.viewRefreshEmitter.emit();
+ });
}
addMapping() {
@@ -138,6 +140,7 @@ export class ColorMappingOptionsConfigComponent implements
OnInit, OnChanges {
setCustomColorMapping(showCustomColorMapping: boolean) {
this.showCustomColorMapping = showCustomColorMapping;
+ this.showCustomColorMappingChange.emit(showCustomColorMapping);
if (!showCustomColorMapping) {
this.resetColorMappings();
diff --git
a/ui/src/app/data-explorer-shared/components/charts/pie/config/pie-chart-widget-config.component.html
b/ui/src/app/data-explorer-shared/components/charts/pie/config/pie-chart-widget-config.component.html
index 2fc82d5913..7d02b690c2 100644
---
a/ui/src/app/data-explorer-shared/components/charts/pie/config/pie-chart-widget-config.component.html
+++
b/ui/src/app/data-explorer-shared/components/charts/pie/config/pie-chart-widget-config.component.html
@@ -97,6 +97,10 @@
currentlyConfiguredWidget.visualizationConfig
.selectedProperty
"
+ [(showCustomColorMapping)]="
+ currentlyConfiguredWidget.visualizationConfig
+ .showCustomColorMappingPieChart
+ "
(viewRefreshEmitter)="triggerViewUpdate()"
>
</sp-color-mapping-options-config>
diff --git
a/ui/src/app/data-explorer-shared/components/charts/pie/config/pie-chart-widget-config.component.ts
b/ui/src/app/data-explorer-shared/components/charts/pie/config/pie-chart-widget-config.component.ts
index 7404a0c04d..e5f39247c7 100644
---
a/ui/src/app/data-explorer-shared/components/charts/pie/config/pie-chart-widget-config.component.ts
+++
b/ui/src/app/data-explorer-shared/components/charts/pie/config/pie-chart-widget-config.component.ts
@@ -44,7 +44,7 @@ export class SpPieChartWidgetConfigComponent extends
BaseWidgetConfig<
setSelectedProperty(field: DataExplorerField) {
this.currentlyConfiguredWidget.visualizationConfig.selectedProperty =
field;
- this.triggerDataRefresh();
+ this.triggerViewRefresh();
}
protected applyWidgetConfig(config: PieChartVisConfig): void {
diff --git
a/ui/src/app/data-explorer-shared/components/charts/pie/model/pie-chart-widget.model.ts
b/ui/src/app/data-explorer-shared/components/charts/pie/model/pie-chart-widget.model.ts
index 2dfbe624db..59a22a0245 100644
---
a/ui/src/app/data-explorer-shared/components/charts/pie/model/pie-chart-widget.model.ts
+++
b/ui/src/app/data-explorer-shared/components/charts/pie/model/pie-chart-widget.model.ts
@@ -27,7 +27,7 @@ export interface PieChartVisConfig extends
DataExplorerVisConfig {
selectedProperty: DataExplorerField;
roundingValue: number;
selectedRadius: number;
- showCustomColorMapping: boolean;
+ showCustomColorMappingPieChart: boolean;
isSelectedPropertyBoolean: boolean;
colorMappingsPieChart: { value: string; label: string; color: string }[];
}
diff --git
a/ui/src/app/data-explorer-shared/components/charts/pie/pie-renderer.service.ts
b/ui/src/app/data-explorer-shared/components/charts/pie/pie-renderer.service.ts
index 4428968c0e..abf7821794 100644
---
a/ui/src/app/data-explorer-shared/components/charts/pie/pie-renderer.service.ts
+++
b/ui/src/app/data-explorer-shared/components/charts/pie/pie-renderer.service.ts
@@ -19,7 +19,7 @@
import { EChartsOption, PieSeriesOption } from 'echarts';
import { DataTransformOption } from 'echarts/types/src/data/helper/transform';
import { SpBaseSingleFieldEchartsRenderer } from
'../../../echarts-renderer/base-single-field-echarts-renderer';
-import { Injectable } from '@angular/core';
+import { inject, Injectable } from '@angular/core';
import { PieChartWidgetModel } from './model/pie-chart-widget.model';
import { FieldUpdateInfo } from '../../../models/field-update.model';
import { ZRColor } from 'echarts/types/dist/shared';
@@ -30,9 +30,7 @@ export class SpPieRendererService extends
SpBaseSingleFieldEchartsRenderer<
PieChartWidgetModel,
PieSeriesOption
> {
- constructor(private colorMappingService: ColorMappingService) {
- super();
- }
+ colorMappingService = inject(ColorMappingService);
addDatasetTransform(
widgetConfig: PieChartWidgetModel,
@@ -121,13 +119,12 @@ export class SpPieRendererService extends
SpBaseSingleFieldEchartsRenderer<
itemStyle: {
color: params => {
const category = params.data[0];
- const color =
- colorMapping.find(c => c.value === category.toString())
- ?.color ||
+ return (colorMapping.find(
+ c => c.value === category.toString(),
+ )?.color ||
this.colorMappingService.getDefaultColor(
- params.dataIndex,
- );
- return color as ZRColor;
+ params.data[0],
+ )) as ZRColor;
},
},
};
diff --git
a/ui/src/app/data-explorer-shared/components/charts/status-heatmap/config/status-heatmap-widget-config.component.html
b/ui/src/app/data-explorer-shared/components/charts/status-heatmap/config/status-heatmap-widget-config.component.html
index ea6cec6e5a..f400c83a8a 100644
---
a/ui/src/app/data-explorer-shared/components/charts/status-heatmap/config/status-heatmap-widget-config.component.html
+++
b/ui/src/app/data-explorer-shared/components/charts/status-heatmap/config/status-heatmap-widget-config.component.html
@@ -41,6 +41,10 @@
[selectedProperty]="
currentlyConfiguredWidget.visualizationConfig.selectedProperty
"
+ [(showCustomColorMapping)]="
+ currentlyConfiguredWidget.visualizationConfig
+ .showCustomColorMappingStatusHeatmap
+ "
(viewRefreshEmitter)="triggerViewUpdate()"
>
</sp-color-mapping-options-config>
diff --git
a/ui/src/app/data-explorer-shared/components/charts/status-heatmap/config/status-heatmap-widget-config.component.ts
b/ui/src/app/data-explorer-shared/components/charts/status-heatmap/config/status-heatmap-widget-config.component.ts
index 96dc0f1d31..f0ae41830d 100644
---
a/ui/src/app/data-explorer-shared/components/charts/status-heatmap/config/status-heatmap-widget-config.component.ts
+++
b/ui/src/app/data-explorer-shared/components/charts/status-heatmap/config/status-heatmap-widget-config.component.ts
@@ -44,7 +44,7 @@ export class StatusHeatmapWidgetConfigComponent extends
BaseWidgetConfig<
setSelectedProperty(field: DataExplorerField) {
this.currentlyConfiguredWidget.visualizationConfig.selectedProperty =
field;
- this.triggerDataRefresh();
+ this.triggerViewRefresh();
}
protected applyWidgetConfig(config: StatusHeatmapVisConfig): void {
@@ -62,7 +62,7 @@ export class StatusHeatmapWidgetConfigComponent extends
BaseWidgetConfig<
triggerViewUpdate() {
this.widgetConfigurationService.notify({
refreshView: true,
- refreshData: true,
+ refreshData: false,
});
}
}
diff --git
a/ui/src/app/data-explorer-shared/components/charts/status-heatmap/model/status-heatmap-widget.model.ts
b/ui/src/app/data-explorer-shared/components/charts/status-heatmap/model/status-heatmap-widget.model.ts
index 719633bebf..a2de46fe73 100644
---
a/ui/src/app/data-explorer-shared/components/charts/status-heatmap/model/status-heatmap-widget.model.ts
+++
b/ui/src/app/data-explorer-shared/components/charts/status-heatmap/model/status-heatmap-widget.model.ts
@@ -26,7 +26,7 @@ import { DataExplorerVisConfig } from
'../../../../models/dataview-dashboard.mod
export interface StatusHeatmapVisConfig extends DataExplorerVisConfig {
selectedProperty: DataExplorerField;
isSelectedPropertyBoolean: boolean;
- showCustomColorMapping: boolean;
+ showCustomColorMappingStatusHeatmap: boolean;
colorMappingsStatusHeatmap: {
value: string;
label: string;
diff --git
a/ui/src/app/data-explorer-shared/components/charts/status-heatmap/status-heatmap-renderer.service.ts
b/ui/src/app/data-explorer-shared/components/charts/status-heatmap/status-heatmap-renderer.service.ts
index 1c35910d37..73d26543b4 100644
---
a/ui/src/app/data-explorer-shared/components/charts/status-heatmap/status-heatmap-renderer.service.ts
+++
b/ui/src/app/data-explorer-shared/components/charts/status-heatmap/status-heatmap-renderer.service.ts
@@ -25,22 +25,20 @@ import {
OptionDataValue,
OptionSourceDataArrayRows,
} from 'echarts/types/src/util/types';
-import { Injectable } from '@angular/core';
+import { Injectable, inject } from '@angular/core';
import { FieldUpdateInfo } from '../../../models/field-update.model';
import { ColorMappingService } from '../../../services/color-mapping.service';
@Injectable({ providedIn: 'root' })
export class SpStatusHeatmapRendererService extends
SpBaseEchartsRenderer<StatusHeatmapWidgetModel> {
- constructor(private colorMappingService: ColorMappingService) {
- super();
- }
+ colorMappingService = inject(ColorMappingService);
+
applyOptions(
generatedDataset: GeneratedDataset,
options: EChartsOption,
widgetConfig: StatusHeatmapWidgetModel,
): void {
this.basicOptions(options);
-
const field = widgetConfig.visualizationConfig.selectedProperty;
const sourceIndex = field.sourceIndex;
@@ -63,91 +61,91 @@ export class SpStatusHeatmapRendererService extends
SpBaseEchartsRenderer<Status
return new Date(a[0]).getTime() - new Date(b[0]).getTime();
});
- const uniqueValues = [
- ...new Set(rawDatasetSource.map(row => row[statusIndex])),
- ];
- const valueMapping = new Map(
- uniqueValues.map((val, index) => [val, index]),
- );
-
- const transformedDataset = rawDatasetSource.map((row, index) => {
- let statusValue = row[statusIndex];
-
- if (typeof statusValue === 'boolean') {
- statusValue = statusValue ? 1 : 0;
- } else if (
- typeof statusValue === 'string' ||
- typeof statusValue === 'number'
- ) {
- statusValue = valueMapping.get(statusValue) ?? null;
- }
-
- return [
- index,
- this.makeTag(rawDataset.rawDataset.dimensions, tags, row),
- statusValue,
- ];
- });
-
- options.dataset = { source: transformedDataset };
-
- (options.xAxis as any).data = rawDatasetSource.map(s => {
- return new Date(s[0]).toLocaleString();
- });
-
- options.tooltip = {
- formatter: params => {
- const timestamp = rawDatasetSource[params.data[0]][0];
- const statusValue = params.value[2];
- const originalValue = uniqueValues[statusValue];
-
- const statusLabel =
- colorMapping.find(c => c.value ===
originalValue.toString())
- ?.label || originalValue;
-
- return `${params.marker} ${new
Date(timestamp).toLocaleString()}<br/>Status: <b>${statusLabel}</b>`;
- },
- };
-
- const dynamicPieces = uniqueValues.map((val, index) => ({
- value: index,
- label:
- colorMapping.find(c => c.value === val.toString())?.label ||
- val.toString(),
- color:
- colorMapping.find(c => c.value === val.toString())?.color ||
- this.colorMappingService.getDefaultColor(index),
- }));
-
- options.visualMap = {
- type: 'piecewise',
- pieces: dynamicPieces,
- orient: 'horizontal',
- right: '5%',
- top: '20',
- };
-
- options.legend = {
- type: 'scroll',
- };
-
- options.series = [
- {
- name: '',
- type: 'heatmap',
- datasetIndex: 0,
- encode: {
- itemId: 0,
- value: 2,
+ if (statusIndex >= 0) {
+ const mappedRawDataset = rawDatasetSource?.map(
+ row => row[statusIndex],
+ );
+ const uniqueValuesSet =
+ mappedRawDataset !== undefined
+ ? new Set(mappedRawDataset)
+ : new Set();
+ const uniqueValues = [...uniqueValuesSet];
+ const uniqueValueSorted = uniqueValues.sort();
+
+ const valueMapping = new Map(
+ uniqueValueSorted.map((val, index) => [val, index]),
+ );
+
+ const transformedDataset = rawDatasetSource.map((row, index) => {
+ const statusValue = valueMapping.get(row[statusIndex]) ?? null;
+ return [
+ index,
+ this.makeTag(rawDataset.rawDataset.dimensions, tags, row),
+ statusValue,
+ ];
+ });
+
+ options.dataset = { source: transformedDataset };
+
+ (options.xAxis as any).data = rawDatasetSource.map(s => {
+ return new Date(s[0]).toLocaleString();
+ });
+
+ options.tooltip = {
+ formatter: params => {
+ const timestamp = rawDatasetSource[params.data[0]][0];
+ const statusValue = params.value[2];
+ const originalValue = uniqueValueSorted[statusValue];
+
+ const statusLabel =
+ colorMapping.find(
+ c => c.value === originalValue.toString(),
+ )?.label || originalValue.toString();
+
+ return `${params.marker} ${new
Date(timestamp).toLocaleString()}<br/>Status: <b>${statusLabel}</b>`;
},
- emphasis: {
- itemStyle: {
- shadowBlur: 10,
- shadowColor: 'rgba(0, 0, 0, 0.5)',
+ };
+
+ const dynamicPieces = uniqueValueSorted.map((val, index) => ({
+ value: index,
+ label:
+ colorMapping.find(c => c.value === val.toString())?.label
||
+ val.toString(),
+ color:
+ colorMapping.find(c => c.value === val.toString())?.color
||
+ this.colorMappingService.getDefaultColor(val.toString()),
+ }));
+
+ options.visualMap = {
+ type: 'piecewise',
+ pieces: dynamicPieces,
+ orient: 'horizontal',
+ right: '5%',
+ top: '20',
+ };
+
+ options.legend = {
+ type: 'scroll',
+ };
+
+ options.series = [
+ {
+ name: '',
+ type: 'heatmap',
+ datasetIndex: 0,
+ encode: {
+ itemId: 0,
+ value: 2,
+ },
+ emphasis: {
+ itemStyle: {
+ shadowBlur: 10,
+ shadowColor: 'rgba(0, 0, 0, 0.5)',
+ },
},
},
- },
- ];
+ ];
+ }
}
public handleUpdatedFields(
diff --git a/ui/src/app/data-explorer-shared/services/color-mapping.service.ts
b/ui/src/app/data-explorer-shared/services/color-mapping.service.ts
index 73cc9d6dcc..e63b995160 100644
--- a/ui/src/app/data-explorer-shared/services/color-mapping.service.ts
+++ b/ui/src/app/data-explorer-shared/services/color-mapping.service.ts
@@ -22,17 +22,6 @@ import { Injectable } from '@angular/core';
providedIn: 'root',
})
export class ColorMappingService {
- private colorPalette = [
- '#5470c6',
- '#91cc75',
- '#fac858',
- '#ee6666',
- '#73c0de',
- '#3ba272',
- '#fc8452',
- '#9a60b4',
- '#ea7ccc',
- ];
constructor() {}
addMapping(
@@ -41,7 +30,7 @@ export class ColorMappingService {
colorMappings.push({
value: '',
label: '',
- color: this.getDefaultColor(colorMappings.length),
+ color: this.getDefaultColor(Math.random() * 1000),
});
}
@@ -60,7 +49,20 @@ export class ColorMappingService {
currentMappings[index].color = newColor;
}
- getDefaultColor(index: number): string {
- return this.colorPalette[index % this.colorPalette.length];
+ getDefaultColor(value: string | number): string {
+ let hash = 0x811c9dc5;
+ const input = String(value);
+
+ for (let i = 0; i < input.length; i++) {
+ hash ^= input.charCodeAt(i);
+ hash = (hash * 0x5bd1e995) & 0xffffffff;
+ hash ^= hash >> 15;
+ }
+
+ const hue = Math.abs(hash) % 360;
+ const saturation = 50 + (Math.abs(hash >> 8) % 20);
+ const lightness = 45 + (Math.abs(hash >> 16) % 20);
+
+ return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
}
}