Skip to content

Commit

Permalink
Removing a layer now removes it from the layerdataarray, the hoverdat…
Browse files Browse the repository at this point in the history
…aarray and the allfeaturesdataarray

Added subscriptions to better synch the featureinfo-geochart stores
  • Loading branch information
Alex-NRCan committed Feb 20, 2024
1 parent ea0d5fb commit e86338e
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { GeoviewStoreType, IFeatureInfoState } from '@/core/stores';
import { TypeFeatureInfoResultsSet, EventType, TypeLayerData, TypeArrayOfLayerData } from '@/api/events/payloads/get-feature-info-payload';
import { IFeatureInfoState } from '@/core/stores';
import { logger } from '@/core/utils/logger';

import { GeochartEventProcessor } from './geochart-event-processor';
import { AbstractEventProcessor, BatchedPropagationLayerDataArrayByMap } from '../abstract-event-processor';
import { UIEventProcessor } from './ui-event-processor';

Expand All @@ -25,6 +25,45 @@ export class FeatureInfoEventProcessor extends AbstractEventProcessor {
// The longer the delay, the longer it'll take to update the UI. The delay can be bypassed using the layer path bypass method.
private static timeDelayBetweenPropagationsForBatch = 1000;

/**
* Overrides initialization of the Feature Info Event Processor
* @param {GeoviewStoreType} store The store associated with the Feature Info Event Processor
* @returns An array of the subscriptions callbacks which were created
*/
protected onInitialize(store: GeoviewStoreType): Array<() => void> | void {
// Checks for udpated layers in layer order
const unsubLayerRemoved = store.subscribe(
(state) => state.mapState.layerOrder,
(cur, prev) => {
// Log
logger.logTraceCoreStoreSubscription('FEATUREINFO EVENT PROCESSOR - layerOrder', cur);

// For each layer path in the layer data array
store
.getState()
.detailsState.layerDataArray.map((layerInfo) => layerInfo.layerPath)
.forEach((layerPath) => {
// If it was in the layer data array and is not anymore
if (prev.includes(layerPath) && !cur.includes(layerPath)) {
// Remove it from feature info array
FeatureInfoEventProcessor.deleteFeatureInfo(store.getState().mapId, layerPath);

// Remove it from hover array
FeatureInfoEventProcessor.deleteFeatureHoverInfo(store.getState().mapId, layerPath);

// Remove it from all features array
FeatureInfoEventProcessor.deleteFeatureAllInfo(store.getState().mapId, layerPath);

// Log
logger.logDebug('Removed Feature Info in stores for layer path:', layerPath);
}
});
}
);

return [unsubLayerRemoved];
}

/**
* Shortcut to get the Feature Info state for a given map id
* @param {string} mapId The mapId
Expand All @@ -36,15 +75,93 @@ export class FeatureInfoEventProcessor extends AbstractEventProcessor {
}

/**
* Static method used to propagate feature info layer sets to the store
* Deletes the specified layer path from the layer sets in the store
* @param {string} mapId The map identifier
* @param {string} layerPath The layer path to delete
*/
private static deleteFeatureInfo(mapId: string, layerPath: string) {
// The feature info state
const featureInfoState = this.getFeatureInfoState(mapId);

// Redirect to helper function
this.deleteFromArray(featureInfoState.layerDataArray, layerPath, (layerArrayResult) => {
// Update the layer data array in the store
featureInfoState.actions.setLayerDataArray(layerArrayResult);

// Also propagate in the batched array
FeatureInfoEventProcessor.propagateFeatureInfoToStoreBatch(mapId, layerArrayResult);
});
}

/**
* Deletes the specified layer path from the hover layers sets in the store
* @param {string} mapId The map identifier
* @param {string} layerPath The layer path to delete
*/
private static deleteFeatureHoverInfo(mapId: string, layerPath: string) {
// The feature info state
const featureInfoState = this.getFeatureInfoState(mapId);

// Redirect to helper function
this.deleteFromArray(featureInfoState.hoverDataArray, layerPath, (layerArrayResult) => {
// Update the layer data array in the store
featureInfoState.actions.setHoverDataArray(layerArrayResult);
});
}

/**
* Deletes the specified layer path from the all features layers sets in the store
* @param {string} mapId The map identifier
* @param {string} layerPath The layer path to delete
*/
private static deleteFeatureAllInfo(mapId: string, layerPath: string) {
// The feature info state
const featureInfoState = this.getFeatureInfoState(mapId);

// Redirect to helper function
this.deleteFromArray(featureInfoState.allFeaturesDataArray, layerPath, (layerArrayResult) => {
// Update the layer data array in the store
featureInfoState.actions.setAllFeaturesDataArray(layerArrayResult);
});
}

/**
* Helper function to delete a layer information from an array when found
* @param {TypeArrayOfLayerData} layerArray The layer array to work with
* @param {string} layerPath The layer path to delete
* @param {(layerArray: TypeArrayOfLayerData) => void} onDeleteCallback The callback executed when the array is updated
*/
private static deleteFromArray(
layerArray: TypeArrayOfLayerData,
layerPath: string,
onDeleteCallback: (layerArray: TypeArrayOfLayerData) => void
) {
// Find the layer data info to delete from the array
const layerDataInfoToDelIndex = layerArray.findIndex((layerInfo) => layerInfo.layerPath === layerPath);

// If found
if (layerDataInfoToDelIndex >= 0) {
// Remove from the array
layerArray.splice(layerDataInfoToDelIndex, 1);

// Callback with updated array
onDeleteCallback(layerArray);
}
}

/**
* Propagates feature info layer sets to the store
*
* @param {string} mapId The map identifier of the resul set modified.
* @param {string} mapId The map identifier of the modified result set.
* @param {string} layerPath The layer path that has changed.
* @param {EventType} eventType The event type that triggered the layer set update.
* @param {TypeFeatureInfoResultsSet} resultsSet The resul sets associated to the map.
*/
static propagateFeatureInfoToStore(mapId: string, layerPath: string, eventType: EventType, resultsSet: TypeFeatureInfoResultsSet) {
// The feature info state
const featureInfoState = this.getFeatureInfoState(mapId);

// Depending on the event type
if (eventType === 'click') {
/**
* Create a details object for each layer which is then used to render layers in details panel.
Expand All @@ -66,9 +183,6 @@ export class FeatureInfoEventProcessor extends AbstractEventProcessor {

// Also propagate in the batched array
FeatureInfoEventProcessor.propagateFeatureInfoToStoreBatch(mapId, layerDataArray);

// Also propagate in the geochart arrays
GeochartEventProcessor.propagateArrayDataToStore(mapId, layerDataArray);
} else if (eventType === 'hover') {
/**
* Create a hover object for each layer which is then used to render layers
Expand All @@ -91,7 +205,7 @@ export class FeatureInfoEventProcessor extends AbstractEventProcessor {
}

/**
* Propagate feature info layer sets to the store in a batched manner, every 'timeDelayBetweenPropagationsForBatch' millisecond.
* Propagates feature info layer sets to the store in a batched manner, every 'timeDelayBetweenPropagationsForBatch' millisecond.
* This is used to provide another 'layerDataArray', in the store, which updates less often so that we save a couple 'layerDataArray'
* update triggers in the components that are listening to the store array.
* The propagation can be bypassed using the store 'layerDataArrayBatchLayerPathBypass' state which tells the process to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,40 @@ export class GeochartEventProcessor extends AbstractEventProcessor {
* @returns An array of the subscriptions callbacks which were created
*/
protected onInitialize(store: GeoviewStoreType): Array<() => void> | void {
// Checks for added and removed layers with time dimension
// Checks for udpated layers in layer order
const unsubLayerRemoved = store.subscribe(
(state) => state.mapState.layerOrder,
(cur, prev) => {
// Log
logger.logTraceCoreStoreSubscription('GEOCHART EVENT PROCESSOR - layerOrder', cur);

// For each chart config keys
Object.keys(store.getState().geochartState.geochartChartsConfig).forEach((chartLayerPath: string) => {
Object.keys(store.getState().geochartState.geochartChartsConfig).forEach((layerPath: string) => {
// If it was in the layerdata array and is not anymore
if (prev.includes(chartLayerPath) && !cur.includes(chartLayerPath)) {
if (prev.includes(layerPath) && !cur.includes(layerPath)) {
// Remove it
GeochartEventProcessor.removeGeochartChart(store.getState().mapId, chartLayerPath);
GeochartEventProcessor.removeGeochartChart(store.getState().mapId, layerPath);

// Log
logger.logDebug('Removed GeoChart configs for layer path:', chartLayerPath);
logger.logDebug('Removed GeoChart configs for layer path:', layerPath);
}
});
}
);

return [unsubLayerRemoved];
// Checks for updated layers in layer data array
const layerDataArrayUpdate = store.subscribe(
(state) => state.detailsState.layerDataArray,
(cur) => {
// Log
logger.logTraceCoreStoreSubscription('GEOCHART EVENT PROCESSOR - layerDataArray', cur);

// Also propagate in the geochart arrays
GeochartEventProcessor.propagateArrayDataToStore(store.getState().mapId, cur);
}
);

return [unsubLayerRemoved, layerDataArrayUpdate];
}

/**
Expand Down Expand Up @@ -93,7 +105,7 @@ export class GeochartEventProcessor extends AbstractEventProcessor {
// set store charts config
this.getGeochartState(mapId)?.actions.setGeochartCharts(chartData);

// TODO: Also update the layer array in other store state to inform the later has a geochart attached to it (when code is done over there)
// TODO: Also update the layer array in other store state to inform the later has a geochart attached to it (when code is done over there)?
}

/**
Expand All @@ -114,7 +126,7 @@ export class GeochartEventProcessor extends AbstractEventProcessor {
// Update the layer data array in the store
this.getGeochartState(mapId)!.actions.setGeochartCharts({ ...this.getGeochartState(mapId)?.geochartChartsConfig, ...toAdd });

// TODO: Also update the layer array in other store state to inform the later has a geochart attached to it (when code is done over there)
// TODO: Also update the layer array in other store state to inform the later has a geochart attached to it (when code is done over there)?
}

/**
Expand All @@ -139,7 +151,7 @@ export class GeochartEventProcessor extends AbstractEventProcessor {
// Update the layer data array in the store
this.getGeochartState(mapId)!.actions.setGeochartCharts({ ...chartConfigs });

// TODO: Also update the layer array in other store state to inform the later has a geochart attached to it (when code is done over there)
// TODO: Also update the layer array in other store state to inform the later has a geochart attached to it (when code is done over there)?
}
}

Expand Down
59 changes: 30 additions & 29 deletions packages/geoview-core/src/geo/layer/other/geocore.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import defaultsDeep from 'lodash/defaultsDeep';
import { TypeJsonValue, api, generateId, getLocalizedMessage, replaceParams, showError } from '@/app';
import { UUIDmapConfigReader, UUIDmapConfigReaderResponse } from '@/core/utils/config/reader/uuid-config-reader';
import { UUIDmapConfigReader } from '@/core/utils/config/reader/uuid-config-reader';
import { ConfigValidation } from '@/core/utils/config/config-validation';
import { AppEventProcessor } from '@/api/event-processors/event-processor-children/app-event-processor';
import { GeochartEventProcessor } from '@/api/event-processors/event-processor-children/geochart-event-processor';
Expand Down Expand Up @@ -115,47 +115,48 @@ export class GeoCore {
const mapConfig = MapEventProcessor.getGeoViewMapConfig(this.mapId);

// For each layer entry config in the list
const promiseOfLayerConfigs: Promise<UUIDmapConfigReaderResponse>[] = [];
geocoreLayerConfig.listOfLayerEntryConfig.forEach((layerConfig: TypeLayerEntryConfig) => {
const listOfLayerCreated: TypeListOfGeoviewLayerConfig[] = [];
for (let i = 0; i < geocoreLayerConfig.listOfLayerEntryConfig.length; i++) {
// Get the config
const layerConfig = geocoreLayerConfig.listOfLayerEntryConfig[i];

// Get the language
const lang = api.maps[this.mapId].getDisplayLanguage();

// Generate the url
// TODO: Check - Is the metadataAccessPath still used? Because it seems to be incompatible with the rest now?
const url = geocoreLayerConfig.metadataAccessPath?.[lang] || `${mapConfig!.serviceUrls.geocoreUrl}`;
const uuids = [layerConfig.layerId];

try {
// Get the GV config from UUID
promiseOfLayerConfigs.push(UUIDmapConfigReader.getGVConfigFromUUIDs(url, lang, [layerConfig.layerId]));
// Get the GV config from UUID and await even if within loop
// eslint-disable-next-line no-await-in-loop
const response = await UUIDmapConfigReader.getGVConfigFromUUIDs(url, lang, uuids);

// Cumulate
listOfLayerCreated.push(response.layers);

// For each found layer associated with the Geocore UUIDs
response.layers.forEach((geoviewLayerConfig) => {
this.copyConfigSettingsOverGeocoreSettings(layerConfig, geoviewLayerConfig);
});
this.configValidation.validateListOfGeoviewLayerConfig(AppEventProcessor.getSupportedLanguages(this.mapId), response.layers);

// For each found geochart associated with the Geocore UUIDs
response.geocharts?.forEach((geochartConfig) => {
// Add a GeoChart
GeochartEventProcessor.addGeochartChart(this.mapId, geochartConfig.layers[0].layerId as string, geochartConfig);
});
} catch (error) {
// Log
logger.logError('Failed to get the GeoView layer from UUI', layerConfig.layerId, error);
logger.logError(`Failed to get the GeoView layer from UUI ${uuids}`, error);
const message = replaceParams([error as TypeJsonValue, this.mapId], getLocalizedMessage(this.mapId, 'validation.layer.loadfailed'));
showError(this.mapId, message);
}
});

// Wait until all configs processed
const listOfLayerCreated = await Promise.all(promiseOfLayerConfigs);

// For each config
listOfLayerCreated.forEach((listOfGeoviewLayerConfig, index) => {
listOfGeoviewLayerConfig.layers.forEach((geoviewLayerConfig) => {
this.copyConfigSettingsOverGeocoreSettings(geocoreLayerConfig.listOfLayerEntryConfig[index], geoviewLayerConfig);
});
this.configValidation.validateListOfGeoviewLayerConfig(
AppEventProcessor.getSupportedLanguages(this.mapId),
listOfGeoviewLayerConfig.layers
);

// For each found geochart associated with the Geocore UUID
listOfGeoviewLayerConfig.geocharts?.forEach((geochartConfig) => {
// Add a GeoChart
GeochartEventProcessor.addGeochartChart(this.mapId, geochartConfig.layers[0].layerId as string, geochartConfig);
});
});

return listOfLayerCreated.map((config) => config.layers);
}

// Return the created layers
return listOfLayerCreated;
}

/**
Expand Down

0 comments on commit e86338e

Please sign in to comment.