@giro3d/giro3d
Version:
A JS/WebGL framework for 3D geospatial data visualization
124 lines (111 loc) • 3.56 kB
text/typescript
/*
* Copyright (c) 2015-2018, IGN France.
* Copyright (c) 2018-2026, Giro3D team.
* SPDX-License-Identifier: MIT
*/
import type { View } from 'copc';
import type { DimensionName } from './dimension';
// A dimension filter wrapped into an index accessor.
/** @internal */
export type FilterByIndex = (index: number) => boolean;
export type FilterOperator =
| 'equal'
| 'less'
| 'lessequal'
| 'greater'
| 'greaterequal'
| 'not'
| 'in'
| 'not_in';
/**
* A filter that can be applied to dimensions to filter out unwanted points during processing.
*/
export type DimensionFilter =
| {
/**
* The dimension this filter applies to.
* If this dimension is not present in the source, the filter is ignored.
*/
dimension: DimensionName;
/**
* The operator of the predicate to apply to a specific dimension value.
*/
operator: Exclude<FilterOperator, 'in' | 'not_in'>;
/**
* The value to apply the predicate to.
*/
value: number;
}
| {
/**
* The dimension this filter applies to.
* If this dimension is not present in the source, the filter is ignored.
*/
dimension: DimensionName;
/**
* The operator of the predicate to apply to a specific dimension value.
*/
operator: Extract<FilterOperator, 'in' | 'not_in'>;
/**
* The values to apply the predicate to.
*/
values: Set<number>;
};
/**
* For a given point index, evaluate all filters in series. Returns `true` if all filters return
* `true`, otherwise returns `false`.
* @internal
*/
export function evaluateFilters(filters: FilterByIndex[] | null, pointIndex: number): boolean {
if (filters == null || filters.length === 0) {
return true;
}
return filters.every(f => f(pointIndex));
}
/** @internal */
export function createPredicateFromFilter(filter: DimensionFilter): (value: number) => boolean {
const operator = filter.operator;
switch (operator) {
case 'equal':
return x => x === filter.value;
case 'less':
return x => x < filter.value;
case 'lessequal':
return x => x <= filter.value;
case 'greater':
return x => x > filter.value;
case 'greaterequal':
return x => x >= filter.value;
case 'not':
return x => x !== filter.value;
case 'in':
return x => filter.values.has(x);
case 'not_in':
return x => !filter.values.has(x);
default:
throw new Error(`invalid filter operator: '${operator}'`);
}
}
/**
* For a given set of dimension filters, return an array of ready-to-use functions to apply to each
* point being read.
* @internal
*/
export function getPerPointFilters(filters: DimensionFilter[], view: View): FilterByIndex[] | null {
if (filters.length === 0) {
return null;
}
const result: FilterByIndex[] = [];
for (const filter of filters) {
const predicate = createPredicateFromFilter(filter);
if (view.dimensions[filter.dimension] != null) {
const getter = view.getter(filter.dimension);
const filterFn = (i: number): boolean => predicate(getter(i));
result.push(filterFn);
}
}
if (result.length > 0) {
return result;
}
return null;
}