Skip to content

Commit

Permalink
Fix class break
Browse files Browse the repository at this point in the history
  • Loading branch information
jolevesq committed Nov 20, 2024
1 parent ae16442 commit d0c0261
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
"labeled": true
},
"listOfGeoviewLayerConfig": [
{
"geoviewLayerId": "472ef86d-7f7c-423b-a7d2-b6f92b79fd6d",
"geoviewLayerType": "geoCore"
},
{
"geoviewLayerId": "historical-flood",
"geoviewLayerName": "Historical Flood Events (HFE)",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { ILayerState, TypeLegend, TypeLegendResultSetEntry } from '@/core/stores
import { AbstractEventProcessor } from '@/api/event-processors/abstract-event-processor';
import { AbstractBaseLayerEntryConfig } from '@/core/utils/config/validation-classes/abstract-base-layer-entry-config';
import {
TypeClassBreakStyleConfig,
TypeClassBreakStyleInfo,
TypeFeatureInfoEntry,
TypeStyleGeometry,
TypeUniqueValueStyleConfig,
Expand Down Expand Up @@ -593,7 +595,7 @@ export class LegendEventProcessor extends AbstractEventProcessor {
}

/**
* Filters features based on their visibility settings defined in the layer's unique value style configuration.
* Filters features based on their visibility settings defined in the layer's unique value or class break style configuration.
*
* @static
* @param {string} mapId - The unique identifier of the map instance
Expand All @@ -604,8 +606,7 @@ export class LegendEventProcessor extends AbstractEventProcessor {
*
* @description
* This function processes features based on the layer's unique value style configuration:
* - If the layer doesn't use unique value styling, returns all features unchanged
* - For unique value styling, filters features based on the style's visibility rules
* - If the layer doesn't use unique value or class break styling, returns all features unchanged
* - Features matching visible styles are included
* - Features matching invisible styles are excluded
* - Features with no matching style follow the defaultVisible setting
Expand All @@ -617,11 +618,38 @@ export class LegendEventProcessor extends AbstractEventProcessor {

// Get the style
const layerStyle = layerConfig.style?.[geometryType];
if (!layerStyle?.styleType || layerStyle.styleType !== 'uniqueValue') {
return features;
let filteredFeatures = features;
if (layerStyle!.styleType === 'uniqueValue') {
filteredFeatures = this.#processClassVisibilityUniqueValue(layerStyle as TypeUniqueValueStyleConfig, features);
} else if (layerStyle!.styleType === 'classBreaks') {
filteredFeatures = this.#processClassVisibilityClassBreak(layerStyle as TypeClassBreakStyleConfig, features);
}

const uniqueValueStyle = layerStyle as TypeUniqueValueStyleConfig;
return filteredFeatures!;
}

/**
* Processes features based on unique value style configuration to determine their visibility.
*
* @param {TypeUniqueValueStyleConfig} uniqueValueStyle - The unique value style configuration
* @param {TypeFeatureInfoEntry[]} features - Array of features to process
* @returns {TypeFeatureInfoEntry[]} Filtered array of features based on visibility rules
*
* @description
* This function filters features based on their field values and the unique value style configuration:
* - Creates sets of visible and invisible values for efficient lookup
* - Combines multiple field values using semicolon separator
* - Determines feature visibility based on:
* - Explicit visibility rules in the style configuration
* - Default visibility for values not matching any style rule
*
* @static
* @private
*/
static #processClassVisibilityUniqueValue(
uniqueValueStyle: TypeUniqueValueStyleConfig,
features: TypeFeatureInfoEntry[]
): TypeFeatureInfoEntry[] {
const styleUnique = uniqueValueStyle.uniqueValueStyleInfo as TypeUniqueValueStyleInfo[];

// Create sets for visible and invisible values for faster lookup
Expand All @@ -635,4 +663,97 @@ export class LegendEventProcessor extends AbstractEventProcessor {
return visibleValues.has(fieldValues.toString()) || (uniqueValueStyle.defaultVisible && !unvisibleValues.has(fieldValues.toString()));
});
}

/**
* Processes features based on class break style configuration to determine their visibility.
*
* @private
*
* @param {TypeClassBreakStyleConfig} classBreakStyle - The class break style configuration
* @param {TypeFeatureInfoEntry[]} features - Array of features to process
* @returns {TypeFeatureInfoEntry[]} Filtered array of features based on class break visibility rules
*
* @description
* This function filters features based on numeric values falling within defined class breaks:
* - Sorts class breaks by minimum value for efficient binary search
* - Creates optimized lookup structure for break points
* - Uses binary search to find the appropriate class break for each feature
* - Determines feature visibility based on:
* - Whether the feature's value falls within a class break range
* - The visibility setting of the matching class break
* - Default visibility for values not matching any class break
*
* @static
* @private
*/
static #processClassVisibilityClassBreak(
classBreakStyle: TypeClassBreakStyleConfig,
features: TypeFeatureInfoEntry[]
): TypeFeatureInfoEntry[] {
const classBreaks = classBreakStyle.classBreakStyleInfo as TypeClassBreakStyleInfo[];

// Sort class breaks by minValue for binary search
// GV: Vlaues can be number, date, string, null or undefined. Should it be only Date or Number
// GV: undefine or null should not be allowed in class break style
const sortedBreaks = [...classBreaks].sort((a, b) => (a.minValue as number) - (b.minValue as number));

// Create an optimized lookup structure
interface ClassBreakPoint {
minValue: number;
maxValue: number;
visible: boolean;
}
const breakPoints = sortedBreaks.map(
(brk): ClassBreakPoint => ({
minValue: brk.minValue as number,
maxValue: brk.maxValue as number,
visible: brk.visible as boolean,
})
);

// Binary search function to find the appropriate class break
const findClassBreak = (value: number): ClassBreakPoint | null => {
let left = 0;
let right = breakPoints.length - 1;

// Binary search through sorted break points to find matching class break
while (left <= right) {
// Calculate middle index to divide search space
const mid = Math.floor((left + right) / 2);
const breakPoint = breakPoints[mid];

// Check if value falls within current break point's range
if (value >= breakPoint!.minValue && value <= breakPoint!.maxValue) {
// Found matching break point, return it
return breakPoint;
}

// If value is less than current break point's minimum,
// search in lower half of remaining range
if (value < breakPoint.minValue) {
right = mid - 1;
} else {
// If value is greater than current break point's maximum,
// search in upper half of remaining range
left = mid + 1;
}
}

return null;
};

// Filter features using binary search
return features.filter((feature) => {
const val = feature.fieldInfo[String(classBreakStyle.field)]?.value;
const fieldValue = val != null ? parseFloat(String(val)) : 0;

// eslint-disable-next-line no-restricted-globals
if (isNaN(fieldValue)) {
return classBreakStyle.defaultVisible;
}

const matchingBreak = findClassBreak(fieldValue);
return matchingBreak ? matchingBreak.visible : classBreakStyle.defaultVisible;
});
}
}

0 comments on commit d0c0261

Please sign in to comment.