This is an automated email from the ASF dual-hosted git repository.

mfholz pushed a commit to branch status-heatmap-fix
in repository https://gitbox.apache.org/repos/asf/streampipes.git

commit 13ce1314a8105a4f298efd399061764c25e3c3e8
Author: Marcelfrueh <[email protected]>
AuthorDate: Mon Mar 31 13:08:53 2025 +0200

    fix: resolve color mapping issue
---
 .../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}%)`;
     }
 }

Reply via email to