UNPKG

dasf-web

Version:

Web frontend components for the data analytics software framework (DASF)

255 lines (215 loc) 8.32 kB
import Gradient, { Spectral, ColorRamp } from '../../colors/Gradient'; import NumericalParameter, { NoParam } from '../../map/model/Parameter'; import Layer from 'ol/layer/Layer'; // import VectorLayer from 'ol/layer/Vector'; import ImageLayer from 'ol/layer/Image'; import BaseVectorLayer from 'ol/layer/BaseVector'; import { FeatureLike } from 'ol/Feature'; import Style, { StyleFunction } from 'ol/style/Style' import Fill from 'ol/style/Fill'; import CircleStyle from 'ol/style/Circle'; import Stroke from 'ol/style/Stroke'; import GeometryType from 'ol/geom/GeometryType'; import { Observable } from 'typescript-observable/dist/observable'; import { IObservableEvent } from 'typescript-observable/dist/interfaces/observable-event'; import ColorUtils from '../../colors/ColorUtils'; import TemporalImageLayer from './TemporalImageLayer'; export interface HasColorScale { setLayerColorScale(colorScale: LayerColorScale): void; getLayerColorScale(): LayerColorScale; updateLayerColorScale(parameters: NumericalParameter[]): void; hasLayerColorScale(): boolean; getAvailableParameters(): NumericalParameter[]; isAutoLayerColorScale(): boolean; setAutoLayerColorScale(auto: boolean): void; } export default class LayerColorScale extends Observable { public static KEY: string = 'colorScale'; public static AUTO_COLOR_SCALE_KEY: string = 'autoColorScale'; public static AUTO_COLOR_SCALE_DISABLE_VALUE: string = 'disable'; private _selectedParameter: NumericalParameter = NoParam; private availableParameters: NumericalParameter[]; private colorGradient: Gradient = new Gradient(Spectral, 0, 1); private registeredLayer: Layer[] = []; private readonly parameterChangeEvent: IObservableEvent = { parent: null, name: 'parameterChanged' }; private readonly availableParametersChangeEvent: IObservableEvent = { parent: null, name: 'availableParametersChanged' }; private customStyle!: ((feature: FeatureLike, colorScale: LayerColorScale) => Style[]); public constructor(availableParameters: NumericalParameter[]) { super(); this.availableParameters = availableParameters; this.selectedParameter = availableParameters[0]; } public registerImageLayer(layer: ImageLayer): void { this.registeredLayer.push(layer); // set color scale to layer layer.set(LayerColorScale.KEY, this); layer.dispatchEvent(LayerColorScale.KEY); } /** * Assoziates the given vector layer with this color scale.<br> * @param layer */ public registerVectorLayer(layer: BaseVectorLayer): void { let self: LayerColorScale = this; this.registeredLayer.push(layer); // set color scale to layer and set style function layer.set(LayerColorScale.KEY, this); layer.setStyle(this.getStyleFunction()); layer.dispatchEvent(LayerColorScale.KEY); // register a post render hook, to capture the case that color gradient was updated during the last run layer.on('postcompose', () => { if (self.colorGradient.getMin() != self.selectedParameter.getMin() || self.colorGradient.getMax() != self.selectedParameter.getMax()) { // the color gradient adapted it self - update the parameter bounds self.selectedParameter.setMin(self.colorGradient.getMin()); self.selectedParameter.setMax(self.colorGradient.getMax()); // we need to redraw since the bounds of the gradient changed on the fly self.triggerLayerChanged(); } }); } public getSelectedParameter(): NumericalParameter { return this.selectedParameter; } public getAvailableParameters(): NumericalParameter[] { return this.availableParameters; } public getAvailableParameter(name: string): NumericalParameter | undefined { return this.availableParameters.find(parameter => parameter.getName() === name); } public getGradient(): Gradient { return this.colorGradient; } public get colorRamp(): ColorRamp { return this.colorGradient.getColorRamp(); } public set colorRamp(colorRamp: ColorRamp) { this.colorGradient.setColorRamp(colorRamp); // trigger redraw of the layers this.triggerLayerChanged(); } public get selectedParameter(): NumericalParameter { return this._selectedParameter; } public set selectedParameter(param: NumericalParameter) { this._selectedParameter = param; // the parameter changed - update the gradient with the new min max values this.colorGradient.updateMinMax(param.getMin(), param.getMax()); // trigger a redraw of the layers this.triggerLayerChanged(); // notify observers this.notify(this.parameterChangeEvent, param); } public updateParameters(parameters: NumericalParameter[]): void { this.availableParameters = parameters; // notify observers this.notify(this.availableParametersChangeEvent, this.availableParameters); // exchange selected parameter with same name let parameterFound = false; for (let p of parameters) { if (p.getName() === this.selectedParameter.getName()) { parameterFound = true; this.selectedParameter = p; break; } } if (!parameterFound) { // the previously selected parameter doesn't exist anymore - select first this.selectedParameter = parameters[0]; } } private triggerLayerChanged(): void { for (let layer of this.registeredLayer) { // console.log('LayerColorScale::trigger layer changed: ' + layer.get('title')); if (layer instanceof TemporalImageLayer) { layer.updateSourceColorScale() } else { layer.changed(); } layer.dispatchEvent(LayerColorScale.KEY); } } public setCustomStyleFunction(customStyle: (feature: FeatureLike, colorScale: LayerColorScale) => Style[]): void { this.customStyle = customStyle; if (this.registeredLayer.length > 0) { // update style functions for already registered layers for (let layer of this.registeredLayer) { if (layer instanceof BaseVectorLayer) { layer.setStyle(this.getStyleFunction()); layer.dispatchEvent(LayerColorScale.KEY); } } } } /** * Returns the style function (custom or default) for this color scale */ public getStyleFunction(): StyleFunction { let self = this; // return custom style if any if (this.customStyle) { return (feature: FeatureLike): Style[] => { return self.customStyle(feature, self); } } else { // return default style return (feature: FeatureLike): Style[] => { // get value of the currently selected parameter let value: number = feature.get(self.selectedParameter.getName()); // get color for the value let color: string = isNaN(value) ? '#77777777' : self.colorGradient.getColor(value); switch (feature.getGeometry().getType()) { case GeometryType.POINT: case GeometryType.MULTI_POINT: let circle = new CircleStyle({ radius: 4, fill: new Fill({ color: color }) }); // fixme: workaround for missing function exception circle['getPixelRatio'] = function (arg) { return 1; }; circle['getScaleArray'] = function () { return [1, 1]; } return [ new Style({ image: circle })]; case GeometryType.LINE_STRING: case GeometryType.MULTI_LINE_STRING: return [ new Style({ stroke: new Stroke({ color: color, width: 4 }) })]; case GeometryType.LINEAR_RING: case GeometryType.POLYGON: case GeometryType.MULTI_POLYGON: return [ new Style({ stroke: new Stroke({ color: color, width: 4 }), fill: new Fill({ color: ColorUtils.setAlpha(color, 0.4) }) })]; default: console.error('unsupported geometry', feature.getGeometry().getType()); return []; } }; } } }