dasf-web
Version:
Web frontend components for the data analytics software framework (DASF)
202 lines (162 loc) • 6.2 kB
text/typescript
import createLinearGradient from 'color-mapper/lib/index';
import LinearGradient from 'color-mapper/lib/index';
import { Observable } from 'typescript-observable/dist/observable'
import { IObservableEvent } from 'typescript-observable/dist/interfaces/observable-event';
import ColorUtils from './ColorUtils';
export class ColorRamp {
public constructor(public readonly name: string, public readonly colors: string[]) {
}
}
// ['red', 'orange', 'yellow', 'lime', 'aqua', 'blue'] ;
// const SpectralColors: string[] = ['#d7191c', '#fdae61', '#ffffbf', '#abdda4', '#2b83ba'];
// const TravelTest: string[] = ['#feebe2', '#fbb4b9', '#f768a1', '#c51b8a', '#7a0177'];
const Spectral: ColorRamp = new ColorRamp('Spectral', ['#d7191c', '#fdae61', '#ffffbf', '#abdda4', '#2b83ba']);
const YellowOrRed: ColorRamp = new ColorRamp('YellowOrRed', ['#ffffb2', '#fecc5c', '#fd8d3c', '#f03b20', '#bd0026']);
const Magma: ColorRamp = new ColorRamp('Magma', ['#000004', '#4a1079', '#a1307e', '#f1605d', '#feaa74', '#fcfdbf']);
const RedOrBlue: ColorRamp = new ColorRamp('RedOrBlue', ['#ca0020', '#f4a582', '#f7f7f7', '#92c5de', '#0571b0']);
const ColdToHot: ColorRamp = new ColorRamp('ColdToHot', ['#313695', '#4575b4', '#74add1', '#abd9e9', '#e0f3f8', '#ffffbf', '#fee090', '#fdae61', '#f46d43', '#d73027', '#a50026']);
const Precipitation: ColorRamp = new ColorRamp('Pr', ['#313695', '#4575b4', '#74add1', '#abd9e9', '#e0f3f8', '#ffffbf', '#fee090', '#fdae61', '#f46d43', '#d73027', '#a50026']);
// pre-defined color ramps
const PredefinedColorRamps: ColorRamp[] = [
Spectral,
ColdToHot,
YellowOrRed,
Magma,
RedOrBlue,
Precipitation
];
export { PredefinedColorRamps, Spectral, YellowOrRed, Magma, RedOrBlue, ColdToHot, Precipitation }
class SingleColor {
public constructor(private color: string) {
}
public getColor(value: number): string {
return this.color;
}
}
export interface HasGradient {
getGradient(): Gradient | undefined;
setGradient(gradient: Gradient): void;
}
/**
* Simple wrapper around the gradient provided by color-mapper
*/
export default class Gradient extends Observable {
private static DEFAULT_COLOR = "#0000ff";
private colorRamp: ColorRamp;
private gradient: LinearGradient;
private min: number;
private max: number;
private minColorRgba: number[];
private maxColorRgba: number[];
private readonly changeEvent: IObservableEvent = {
parent: null,
name: 'changed'
};
public constructor(colorRamp: ColorRamp, min: number, max: number) {
super();
this.colorRamp = colorRamp;
this.updateMinMaxColors();
this.min = min;
this.max = max;
this.updateMinMax(min, max);
}
private updateMinMaxColors(): void {
this.minColorRgba = ColorUtils.hex2Rgb(this.colorRamp.colors[0]);
this.minColorRgba.push(255);
this.maxColorRgba = ColorUtils.hex2Rgb(this.colorRamp.colors[this.colorRamp.colors.length - 1]);
this.maxColorRgba.push(255);
}
public setColorRamp(colorRamp: ColorRamp): void {
let changed = colorRamp !== this.colorRamp;
this.colorRamp = colorRamp;
this.updateMinMaxColors();
// update the underlying linear gradient
this.updateMinMax(this.min, this.max);
if (changed) {
this.changed();
}
}
public updateMinMax(min?: number, max?: number): void {
let minChanged = false;
let maxChanged = false;
if (min !== undefined && this.min != min) {
this.min = min;
minChanged = true;
}
if (max !== undefined && this.max != max) {
this.max = max;
maxChanged = true;
}
if (this.min !== this.max) {
this.gradient = createLinearGradient(min, max);
// equal steps between colors
let step = 1.0 / (this.colorRamp.colors.length - 1);
let value = 0.0;
// set color stops
for (let color of this.colorRamp.colors) {
this.gradient.addColorStop(value, color);
value += step;
}
} else {
// this is no gradient because the range is zero (min == max)
this.gradient = new SingleColor(this.colorRamp.colors[0]);
}
if (minChanged || maxChanged) {
this.changed();
}
}
private changed(): void {
this.notify(this.changeEvent, this);
}
public getColor(value: number): string {
if (value !== undefined) {
// auto tune min max values
if (value < this.min) {
this.updateMinMax(value, this.max);
}
if (value > this.max) {
this.updateMinMax(this.min, value);
}
return this.gradient.getColor(value);
} else if (this.min == this.max) {
// we have no real gradient anyway - return single color
return this.gradient.getColor(value);
} else {
return Gradient.DEFAULT_COLOR;
}
}
public getColorRGBA(value: number): number[] {
if (value !== undefined) {
// auto tune min max values
if (value < this.min) {
this.updateMinMax(value, this.max);
}
if (value > this.max) {
this.updateMinMax(this.min, value);
}
if (value === this.max) {
return this.maxColorRgba;
} else if (value === this.min) {
return this.minColorRgba;
}
try {
let rgba = this.gradient.getColor(value);
rgba[3] = 255;
return rgba;
} catch (e) {
console.error('error in gradient', this.gradient, value);
// console.log(e);
}
}
return [0, 0, 0, 255];
}
public getColorRamp(): ColorRamp {
return this.colorRamp;
}
public getMin(): number {
return this.min;
}
public getMax(): number {
return this.max;
}
}