Add color mapping for pie chart (#3305)
diff --git a/ui/src/app/data-explorer/components/widgets/pie/config/pie-chart-widget-config.component.html b/ui/src/app/data-explorer/components/widgets/pie/config/pie-chart-widget-config.component.html
index b6b4b44..71afe09 100644
--- a/ui/src/app/data-explorer/components/widgets/pie/config/pie-chart-widget-config.component.html
+++ b/ui/src/app/data-explorer/components/widgets/pie/config/pie-chart-widget-config.component.html
@@ -31,19 +31,18 @@
>
</sp-select-property>
</sp-configuration-box>
- <sp-configuration-box
- title="Settings"
- *ngIf="
- currentlyConfiguredWidget.visualizationConfig.selectedProperty
- ?.fieldCharacteristics.numeric
- "
- >
+
+ <sp-configuration-box title="Settings">
<div fxLayout="column" fxLayoutGap="10px">
<div
fxFlex="100"
fxLayout="row"
fxLayoutAlign="start center"
fxLayoutGap="10px"
+ *ngIf="
+ currentlyConfiguredWidget.visualizationConfig
+ .selectedProperty?.fieldCharacteristics.numeric
+ "
>
<small>Rounding</small>
<mat-form-field
@@ -67,6 +66,7 @@
</mat-select>
</mat-form-field>
</div>
+
<div
fxFlex="100"
fxLayout="row"
@@ -87,6 +87,98 @@
</mat-slider>
<small>{{ slider.value }}% </small>
</div>
+ <div
+ fxLayout="row"
+ fxLayoutGap="10px"
+ fxLayoutAlign="start center"
+ fxFlex="100"
+ class="checkbox-container"
+ >
+ <mat-checkbox
+ color="accent"
+ [(ngModel)]="
+ currentlyConfiguredWidget.visualizationConfig
+ .showCustomColorMapping
+ "
+ (ngModelChange)="showCustomColorMapping($event)"
+ >
+ </mat-checkbox>
+ <small>Add custom color mapping</small>
+ </div>
+
+ <div
+ *ngIf="
+ currentlyConfiguredWidget.visualizationConfig
+ .showCustomColorMapping
+ "
+ >
+ <button mat-raised-button color="accent" (click)="addMapping()">
+ <i class="material-icons">add</i
+ ><span> Add Mapping</span>
+ </button>
+ </div>
+
+ <div
+ *ngIf="
+ currentlyConfiguredWidget.visualizationConfig
+ .showCustomColorMapping
+ "
+ >
+ <div fxLayout="column" fxLayoutGap="10px">
+ <div
+ *ngFor="
+ let mapping of currentlyConfiguredWidget
+ .visualizationConfig.colorMappings;
+ let i = index
+ "
+ fxLayout="row"
+ fxLayoutGap="10px"
+ fxLayoutAlign="start center"
+ fxFlex="100"
+ style="margin-top: 10px; align-items: center"
+ >
+ <div fxFlex>
+ <mat-form-field
+ class="w-100"
+ color="accent"
+ appearance="outline"
+ >
+ <mat-label>Value</mat-label>
+ <input
+ matInput
+ [(ngModel)]="mapping.value"
+ (ngModelChange)="updateMapping()"
+ />
+ </mat-form-field>
+ </div>
+ <div fxFlex="70px">
+ <input
+ [(colorPicker)]="mapping.color"
+ [style.background]="mapping.color"
+ style="
+ height: 50%;
+ width: 100%;
+ border: none;
+ border-radius: 10%;
+ cursor: pointer;
+ "
+ (colorPickerChange)="updateColor(i, $event)"
+ readonly
+ />
+ </div>
+ <div fxLayoutAlign="end center">
+ <button
+ mat-icon-button
+ matTooltip="Remove Mapping"
+ color="accent"
+ (click)="removeMapping(i)"
+ >
+ <i class="material-icons">delete</i>
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
</div>
</sp-configuration-box>
</sp-visualization-config-outer>
diff --git a/ui/src/app/data-explorer/components/widgets/pie/config/pie-chart-widget-config.component.ts b/ui/src/app/data-explorer/components/widgets/pie/config/pie-chart-widget-config.component.ts
index e93dd85..f801556 100644
--- a/ui/src/app/data-explorer/components/widgets/pie/config/pie-chart-widget-config.component.ts
+++ b/ui/src/app/data-explorer/components/widgets/pie/config/pie-chart-widget-config.component.ts
@@ -23,6 +23,9 @@
PieChartWidgetModel,
} from '../model/pie-chart-widget.model';
import { DataExplorerField } from '@streampipes/platform-services';
+import { ColorMappingService } from '../../../../services/color-mapping.service';
+import { WidgetConfigurationService } from 'src/app/data-explorer/services/widget-configuration.service';
+import { DataExplorerFieldProviderService } from 'src/app/data-explorer/services/data-explorer-field-provider-service';
@Component({
selector: 'sp-pie-chart-widget-config',
@@ -32,6 +35,14 @@
PieChartWidgetModel,
PieChartVisConfig
> {
+ constructor(
+ private colorMappingService: ColorMappingService,
+ widgetConfigurationService: WidgetConfigurationService,
+ fieldService: DataExplorerFieldProviderService,
+ ) {
+ super(widgetConfigurationService, fieldService);
+ }
+
setSelectedProperty(field: DataExplorerField) {
this.currentlyConfiguredWidget.visualizationConfig.selectedProperty =
field;
@@ -46,6 +57,8 @@
);
config.roundingValue ??= 0.1;
config.selectedRadius ??= 0;
+ config.showCustomColorMapping ??= false;
+ config.colorMappings ??= [];
}
updateRoundingValue(selectedType: number) {
@@ -60,6 +73,52 @@
this.triggerViewRefresh();
}
+ showCustomColorMapping(showCustomColorMapping: boolean) {
+ this.currentlyConfiguredWidget.visualizationConfig.showCustomColorMapping =
+ showCustomColorMapping;
+
+ if (!showCustomColorMapping) {
+ this.resetColorMappings();
+ }
+
+ this.triggerViewRefresh();
+ }
+
+ resetColorMappings(): void {
+ this.currentlyConfiguredWidget.visualizationConfig.colorMappings = [];
+ this.triggerViewRefresh();
+ }
+
+ addMapping() {
+ this.colorMappingService.addMapping(
+ this.currentlyConfiguredWidget.visualizationConfig.colorMappings,
+ );
+ this.triggerViewRefresh();
+ }
+
+ removeMapping(index: number) {
+ this.currentlyConfiguredWidget.visualizationConfig.colorMappings =
+ this.colorMappingService.removeMapping(
+ this.currentlyConfiguredWidget.visualizationConfig
+ .colorMappings,
+ index,
+ );
+ this.triggerViewRefresh();
+ }
+
+ updateColor(index: number, newColor: string) {
+ this.colorMappingService.updateColor(
+ this.currentlyConfiguredWidget.visualizationConfig.colorMappings,
+ index,
+ newColor,
+ );
+ this.triggerViewRefresh();
+ }
+
+ updateMapping() {
+ this.triggerViewRefresh();
+ }
+
protected requiredFieldsForChartPresent(): boolean {
return this.fieldProvider.allFields.length > 0;
}
diff --git a/ui/src/app/data-explorer/components/widgets/pie/model/pie-chart-widget.model.ts b/ui/src/app/data-explorer/components/widgets/pie/model/pie-chart-widget.model.ts
index a846ad0..ad73397 100644
--- a/ui/src/app/data-explorer/components/widgets/pie/model/pie-chart-widget.model.ts
+++ b/ui/src/app/data-explorer/components/widgets/pie/model/pie-chart-widget.model.ts
@@ -27,6 +27,8 @@
selectedProperty: DataExplorerField;
roundingValue: number;
selectedRadius: number;
+ showCustomColorMapping: boolean;
+ colorMappings: { value: string; color: string }[];
}
export interface PieChartWidgetModel extends DataExplorerWidgetModel {
diff --git a/ui/src/app/data-explorer/components/widgets/pie/pie-renderer.service.ts b/ui/src/app/data-explorer/components/widgets/pie/pie-renderer.service.ts
index be48f98..20a74de 100644
--- a/ui/src/app/data-explorer/components/widgets/pie/pie-renderer.service.ts
+++ b/ui/src/app/data-explorer/components/widgets/pie/pie-renderer.service.ts
@@ -22,12 +22,18 @@
import { 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';
+import { ColorMappingService } from '../../../services/color-mapping.service';
@Injectable({ providedIn: 'root' })
export class SpPieRendererService extends SpBaseSingleFieldEchartsRenderer<
PieChartWidgetModel,
PieSeriesOption
> {
+ constructor(private colorMappingService: ColorMappingService) {
+ super();
+ }
+
addDatasetTransform(
widgetConfig: PieChartWidgetModel,
): DataTransformOption {
@@ -65,6 +71,7 @@
_widgetConfig: PieChartWidgetModel,
): PieSeriesOption {
const innerRadius = _widgetConfig.visualizationConfig.selectedRadius;
+ const colorMapping = _widgetConfig.visualizationConfig.colorMappings;
return {
name,
type: 'pie',
@@ -82,6 +89,18 @@
},
encode: { itemName: 'name', value: 'value' },
radius: [innerRadius + '%', '90%'],
+ itemStyle: {
+ color: params => {
+ const category = params.data[0];
+ const color =
+ colorMapping.find(c => c.value === category.toString())
+ ?.color ||
+ this.colorMappingService.getDefaultColor(
+ params.dataIndex,
+ );
+ return color as ZRColor;
+ },
+ },
};
}
diff --git a/ui/src/app/data-explorer/services/color-mapping.service.ts b/ui/src/app/data-explorer/services/color-mapping.service.ts
new file mode 100644
index 0000000..9b878a3
--- /dev/null
+++ b/ui/src/app/data-explorer/services/color-mapping.service.ts
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { Injectable } from '@angular/core';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class ColorMappingService {
+ private colorPalette = [
+ '#5470c6',
+ '#91cc75',
+ '#fac858',
+ '#ee6666',
+ '#73c0de',
+ '#3ba272',
+ '#fc8452',
+ '#9a60b4',
+ '#ea7ccc',
+ ];
+ constructor() {}
+
+ addMapping(colorMappings: { value: string; color: string }[]): void {
+ colorMappings.push({
+ value: '',
+ color: this.getDefaultColor(colorMappings.length),
+ });
+ }
+
+ removeMapping(
+ colorMappings: { value: string; color: string }[],
+ index: number,
+ ): { value: string; color: string }[] {
+ return colorMappings.filter((_, i) => i !== index);
+ }
+
+ updateColor(
+ currentMappings: { value: string; color: string }[],
+ index: number,
+ newColor: string,
+ ): void {
+ currentMappings[index].color = newColor;
+ }
+
+ getDefaultColor(index: number): string {
+ return this.colorPalette[index % this.colorPalette.length];
+ }
+}