UNPKG

dasf-web

Version:

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

269 lines (218 loc) 8.05 kB
import IHasFilter, { isFilterable } from "./HasFilter"; import VectorLayer from 'ol/layer/Vector'; import VectorSource from 'ol/source/Vector'; import Feature from 'ol/Feature'; import { Observable } from 'typescript-observable/dist/observable'; import { IObservableEvent } from 'typescript-observable/dist/interfaces/observable-event'; // TODO: the filter needs to be observable!!! export default class Filter extends Observable { private readonly minChangeEvent: IObservableEvent = { parent: null, name: 'changed:min' }; private readonly maxChangeEvent: IObservableEvent = { parent: null, name: 'changed:max' }; private readonly thresholdChangeEvent: IObservableEvent = { parent: null, name: 'changed:threshold' }; private readonly attributeChangeEvent: IObservableEvent = { parent: null, name: 'changed:attribute' }; private readonly useTimeseriesChangeEvent: IObservableEvent = { parent: null, name: 'changed:useTimeseries' }; private static readonly ORIGINAL_FEATURES_KEY = 'originalFeatures'; private static readonly FILTER_KEY = "filter"; public static readonly MEDIAN_TOP = "Median (top)"; public static readonly MEDIAN_BOTTOM = "Median (bottom)"; public static readonly QUARTILE_R1 = "Quartile (R1)"; public static readonly QUARTILE_R2 = "Quartile (R2)"; public static readonly QUARTILE_R3 = "Quartile (R3)"; public static readonly QUARTILE_R4 = "Quartile (R4)"; private _attribute: string; private _useTimeseries = true; private _threshold: [number, number] = [Number.NaN, Number.NaN]; private _min: number = 0; private _max: number = 0; private _median: number = NaN; private _quartileQ1: number = NaN; private _quartileQ3: number = NaN; private _protected: boolean = false; public get isProtected(): boolean { return this._protected; } public set isProtected(value: boolean) { this._protected = value; } public get attribute(): string { return this._attribute; } public set attribute(value: string) { let changed = value != this._attribute; this._attribute = value; if (changed) { this.notify(this.attributeChangeEvent, value); } } public get useTimeseries(): boolean { return this._useTimeseries; } public set useTimeseries(value: boolean) { let changed = value != this._useTimeseries; this._useTimeseries = value; if (changed) { this.notify(this.useTimeseriesChangeEvent, value); } } public set min(value: number) { let changed = value != this._min; this._min = value; if (changed) { this.notify(this.minChangeEvent, value); } } public get min(): number { return this._min; } public set max(value: number) { let changed = value != this._max; this._max = value; if (changed) { this.notify(this.maxChangeEvent, value); } } public get max(): number { return this._max; } public set median(value:number){ this._median = value; } public get median():number{ return this._median; } public set quartileQ1(value:number){ this._quartileQ1 = value; } public get quartileQ1():number{ return this._quartileQ1; } public set quartileQ3(value:number){ this._quartileQ3 = value; } public get quartileQ3():number{ return this._quartileQ3; } public setThreshold(lower: number, upper: number): void { let changed = lower != this.threshold[0] || upper != this.threshold[1]; this._threshold[0] = lower; this._threshold[1] = upper; if (changed) { this.notify(this.thresholdChangeEvent, this._threshold); } } public get threshold(): [number, number] { return this._threshold; } public reset(): void { this.setThreshold(this._min, this._max); } public isValid(): boolean { return this.attribute !== undefined && this.attribute.length > 0 && this.threshold !== undefined && this.threshold.length > 0; } /** * Applies this filter to the given object * @param obj */ public apply(obj: object): void { // console.log('applying filter', this, obj); if (isFilterable(obj)) { let filterable: IHasFilter = obj as IHasFilter; filterable.setFilter(this); } else if (Filter.isVectorLayerAndVectorSource(obj)) { // we can filter any vector layer with a vector source let vectorLayer: VectorLayer = obj as VectorLayer; let vectorSource: VectorSource = vectorLayer.getSource() as VectorSource; // store the filter in the layer vectorLayer.set(Filter.FILTER_KEY, this); // get original feature collection let origFeatures = vectorLayer.get(Filter.ORIGINAL_FEATURES_KEY); if (origFeatures === undefined) { // get original features from original source origFeatures = vectorSource.getFeatures(); // store original features in layer vectorLayer.set(Filter.ORIGINAL_FEATURES_KEY, origFeatures); } let filteredFeatures: Feature[] = []; for (let feature of origFeatures) { var featureValue = feature.get(this.attribute); if (this.filter(featureValue)) { filteredFeatures.push(feature); } } // clear the source and add features satisfying the filter vectorSource.clear(); vectorSource.addFeatures(filteredFeatures); } else { console.warn('ignoring attempt to apply a filter to an unsupported object', this, obj); } } /** * Extracts a filter from the given object * @param obj */ public static extract(obj: object): Filter | undefined { if (isFilterable(obj)) { return (obj as IHasFilter).getFilter(); } else if (Filter.isVectorLayerAndVectorSource(obj)) { return (obj as VectorLayer).get(Filter.FILTER_KEY); } else { console.warn('attempt to extract filter from unsupported object', obj); } return undefined; } public static canFilter(obj: object): boolean { return isFilterable(obj) || Filter.isVectorLayerAndVectorSource(obj); } private static isVectorLayerAndVectorSource(obj: object): boolean { return obj instanceof VectorLayer && (obj as VectorLayer).getSource() instanceof VectorSource; } /** * @param value -> check value * @returns automatic filter */ public filter(value: number): boolean { return value >= this._threshold[0] && value <= this._threshold[1]; } /** * @returns cql filter */ public getQuery(): string { if (this.attribute != "" && this._threshold[0] != NaN && this._threshold[1] != NaN) { if (this._threshold[0] == this._threshold[1]) { return this.attribute + " = " + this._threshold[0]; } else { return this.attribute + " BETWEEN " + this._threshold[0] + " AND " + this._threshold[1]; } } return ""; } public getPossibleThresholds(): string[]{ let options: string[] = []; if(this.median!=NaN){ options.push(Filter.MEDIAN_BOTTOM); options.push(Filter.MEDIAN_TOP); } if(this.quartileQ1!=NaN && this.quartileQ3!=NaN){ options.push(Filter.QUARTILE_R1); options.push(Filter.QUARTILE_R2); options.push(Filter.QUARTILE_R3); options.push(Filter.QUARTILE_R4); } return options; } }