Skip to content

Commit

Permalink
GeoCore implementation update
Browse files Browse the repository at this point in the history
Add-Layer fix when adding GeoCore layer with uuid
GeoChart linkage with GeoCore
  • Loading branch information
Alex-NRCan committed Feb 19, 2024
1 parent ef43e12 commit ce9bf2c
Show file tree
Hide file tree
Showing 21 changed files with 608 additions and 399 deletions.
2 changes: 1 addition & 1 deletion packages/geoview-core/public/configs/sample-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
},
"externalPackages": [],
"serviceUrls": {
"keys": "https://geocore.api.geo.ca"
"geocoreUrl": "https://geocore.api.geo.ca"
},
"suportedLanguages": ["en", "fr"],
"version": "1.0"
Expand Down
2 changes: 1 addition & 1 deletion packages/geoview-core/schema-default-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"corePackages": [],
"externalPackages": [],
"serviceUrls": {
"keys": "https://geocore.api.geo.ca",
"geocoreUrl": "https://geocore.api.geo.ca",
"geolocator": "https://geolocator.api.geo.ca?keys=geonames,nominatim,locate"
},
"suportedLanguages": [
Expand Down
4 changes: 2 additions & 2 deletions packages/geoview-core/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1728,7 +1728,7 @@
"type": "object",
"description": "Service endpoint urls",
"properties": {
"keys": {
"geocoreUrl": {
"type": "string",
"default": "https://geocore.api.geo.ca",
"description": "Service end point to access API for layers specification (loading and plugins parameters). By default it is GeoCore but can be another endpoint with similar output."
Expand All @@ -1742,7 +1742,7 @@
"description": "Service end point to access geo location of searched value."
}
},
"required": ["keys"]
"required": ["geocoreUrl"]
},
"TypeDisplayLanguage": {
"enum": ["en", "fr"],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { TypeArrayOfLayerData, TypeJsonObject } from '@/core/types/global-types';
import { GeoviewStoreType } from '@/core/stores';
import { TypeArrayOfLayerData } from '@/core/types/global-types';
import { GeoChartStoreByLayerPath, IGeochartState } from '@/core/stores/store-interface-and-intial-values/geochart-state';
import { GeoChartConfig } from '@/core/utils/config/reader/uuid-config-reader';
import { logger } from '@/core/utils/logger';

import { AbstractEventProcessor, BatchedPropagationLayerDataArrayByMap } from '../abstract-event-processor';

Expand All @@ -23,6 +26,36 @@ export class GeochartEventProcessor 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.
static timeDelayBetweenPropagationsForBatch = 2000;

/**
* Overrides initialization of the GeoChart Event Processor
* @param {GeoviewStoreType} store The store associated with the GeoChart Event Processor
* @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
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) => {
// If it was in the layerdata array and is not anymore
if (prev.includes(chartLayerPath) && !cur.includes(chartLayerPath)) {
// Remove it
GeochartEventProcessor.removeGeochartChart(store.getState().mapId, chartLayerPath);

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

return [unsubLayerRemoved];
}

/**
* Shortcut to get the Geochart state for a given map id
* @param {string} mapId The mapId
Expand All @@ -36,23 +69,21 @@ export class GeochartEventProcessor extends AbstractEventProcessor {
}

/**
* Set the default layers from configuration.
* Sets the default layers from configuration.
* In the store, the GeoChart configurations are stored in an object with layerPath as its property name
* (to retrieve the configuration per layer faster).
*
* @param {string} mapId the map id
* @param {TypeJsonObject} charts The array of JSON configuration for geochart
* @param {GeoChartConfig[]} charts The array of JSON configuration for GeoChart
*/
static setGeochartCharts(mapId: string, charts: TypeJsonObject[]): void {
static setGeochartCharts(mapId: string, charts: GeoChartConfig[]): void {
// The store object representation
const chartData: GeoChartStoreByLayerPath = {};

// Loop on the charts
// eslint-disable-next-line @typescript-eslint/no-explicit-any
charts.forEach((chartInfo: any) => {
charts.forEach((chartInfo) => {
// For each layer path
// eslint-disable-next-line @typescript-eslint/no-explicit-any
chartInfo.layers.forEach((layer: any) => {
chartInfo.layers.forEach((layer) => {
// Get the layer path
const layerPath = layer.layerId;
chartData[layerPath] = chartInfo;
Expand All @@ -61,10 +92,59 @@ 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)
}

/**
* Adds a GeoChart Configuration to the specified map id and layer path
* @param {string} mapId The map ID
* @param {string} layerPath The layer path
* @param {GeoChartConfig} chartConfig The Geochart Configuration
*/
static addGeochartChart(mapId: string, layerPath: string, chartConfig: GeoChartConfig): void {
// The processor needs an initialized chart store which is only initialized if the Geochart plugin exists.
// Therefore, we validate its existence first.
if (!this.getGeochartState(mapId)) return;

// Config to add
const toAdd: GeoChartStoreByLayerPath = {};
toAdd[layerPath] = chartConfig;

// 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)
}

/**
* Removes a GeoChart Configuration at the specified map id and layer path
* @param {string} mapId The map ID
* @param {string} layerPath The layer path
*/
static removeGeochartChart(mapId: string, layerPath: string): void {
// The processor needs an initialized chart store which is only initialized if the Geochart plugin exists.
// Therefore, we validate its existence first.
if (!this.getGeochartState(mapId)) return;
if (!this.getGeochartState(mapId)?.geochartChartsConfig) return;

// Config to remove
if (Object.keys(this.getGeochartState(mapId)!.geochartChartsConfig).includes(layerPath)) {
// Grab the config
const chartConfigs = this.getGeochartState(mapId)!.geochartChartsConfig;

// Delete the config
delete chartConfigs[layerPath];

// 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)
}
}

/**
* Propagate feature info layer sets to the store and the also in a batched manner.
* Propagates feature info layer sets to the store and the also in a batched manner.
* @param {string} mapId The map id
* @param {string} layerDataArray The layer data array to propagate in the store
*/
Expand All @@ -81,7 +161,7 @@ export class GeochartEventProcessor 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 @@ -38,7 +38,7 @@ export class MapEventProcessor extends AbstractEventProcessor {
(cur, prev) => {
if (cur !== prev) {
// Log (too annoying, already have trace in EVENT_MAP_LOADED handler that works well)
// logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - mapLoaded (changed)', cur);
// logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - mapLoaded (changed)', mapId, cur);

api.event.emit(mapPayload(EVENT_NAMES.MAP.EVENT_MAP_LOADED, mapId, store.getState().mapState.mapElement!));
}
Expand All @@ -51,7 +51,7 @@ export class MapEventProcessor extends AbstractEventProcessor {
(cur, prev) => {
if (cur !== prev) {
// Log (too annoying, already have trace in EVENT_MAP_MOVE_END handler that works well)
// logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - centerCoordinates (changed)', cur);
// logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - centerCoordinates (changed)', mapId, cur);

api.event.emit(lngLatPayload(EVENT_NAMES.MAP.EVENT_MAP_MOVE_END, mapId, cur));
}
Expand All @@ -63,7 +63,7 @@ export class MapEventProcessor extends AbstractEventProcessor {
(cur, prev) => {
if (cur! && cur !== prev) {
// Log (too annoying, already have trace in EVENT_MAP_POINTER_MOVE handler that works well)
// logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - pointerPosition (changed)', cur);
// logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - pointerPosition (changed)', mapId, cur);

api.event.emit(mapMouseEventPayload(EVENT_NAMES.MAP.EVENT_MAP_POINTER_MOVE, mapId, cur));
}
Expand All @@ -75,7 +75,7 @@ export class MapEventProcessor extends AbstractEventProcessor {
(cur, prev) => {
if (cur! && cur !== prev) {
// Log (this event is raised, and we currently have no handles for it, by design)
logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - currentProjection (changed)', cur);
logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - currentProjection (changed)', mapId, cur);

api.event.emit(mapViewProjectionPayload(EVENT_NAMES.MAP.EVENT_MAP_VIEW_PROJECTION_CHANGE, mapId, cur!));
}
Expand All @@ -87,7 +87,7 @@ export class MapEventProcessor extends AbstractEventProcessor {
(cur, prev) => {
if (cur && cur !== prev) {
// Log (too annoying, already have trace in EVENT_MAP_SINGLE_CLICK handler that works well)
// logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - currentProjection (changed)', cur);
// logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - currentProjection (changed)', mapId, cur);

api.event.emit(mapMouseEventPayload(EVENT_NAMES.MAP.EVENT_MAP_SINGLE_CLICK, mapId, cur));
}
Expand All @@ -99,7 +99,7 @@ export class MapEventProcessor extends AbstractEventProcessor {
(cur, prev) => {
if (cur! && cur !== prev) {
// Log
logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - zoom (changed)', cur);
logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - zoom (changed)', mapId, cur);

api.event.emit(numberPayload(EVENT_NAMES.MAP.EVENT_MAP_ZOOM_END, mapId, cur));
}
Expand All @@ -113,7 +113,7 @@ export class MapEventProcessor extends AbstractEventProcessor {
(state) => state.mapState.highlightedFeatures,
(curFeatures, prevFeatures) => {
// Log
logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - highlightedFeatures', curFeatures);
logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - highlightedFeatures', mapId, curFeatures);

if (curFeatures.length === 0) api.maps[mapId].layer.featureHighlight.removeHighlight('all');
else {
Expand All @@ -137,7 +137,7 @@ export class MapEventProcessor extends AbstractEventProcessor {
(state) => state.mapState.selectedFeatures,
(curFeatures, prevFeatures) => {
// Log
logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - selectedFeatures', curFeatures);
logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - selectedFeatures', mapId, curFeatures);

// TODO: on reload, layer object is undefined, need to test for now and solve in #1580
if (curFeatures.length === 0 && api.maps[mapId].layer !== undefined) api.maps[mapId].layer.featureHighlight.resetAnimation('all');
Expand All @@ -162,7 +162,7 @@ export class MapEventProcessor extends AbstractEventProcessor {
(state) => state.layerState.legendLayers,
(cur) => {
// Log
logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - legendLayers', cur);
logger.logTraceCoreStoreSubscription('MAP EVENT PROCESSOR - legendLayers', mapId, cur);

const orderedLayerPaths = MapEventProcessor.evaluateLayerPathsFromLegendsArray(cur);
const prevLayerOrder = [...store.getState().mapState.layerOrder];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export function DetailsPanel(): JSX.Element {
// #region MAIN HOOKS SECTION ***************************************************************************************

/**
* Check if feature is in the store checkedFeatures array
* Checks if feature is in the store checkedFeatures array
*
* @param {TypeFeatureInfoEntry} feature The feature to check
* @returns {boolean} true if feature is in checkedFeatures
Expand All @@ -83,7 +83,7 @@ export function DetailsPanel(): JSX.Element {
);

/**
* Helper function to clear the highlighed features when they are not checked.
* Clears the highlighed features when they are not checked.
* @param {TypeArrayOfFeatureInfoEntries} arrayToClear The array to clear of the unchecked features
*/
const clearHighlightsUnchecked = useCallback(
Expand All @@ -100,7 +100,7 @@ export function DetailsPanel(): JSX.Element {
);

/**
* Get the label for the number of features of a layer.
* Gets the label for the number of features of a layer.
* @returns string
*/
const getNumFeaturesLabel = useCallback(
Expand All @@ -115,7 +115,7 @@ export function DetailsPanel(): JSX.Element {
);

/**
* Memoize the layers list for the LayerList component and centralizing indexing purposes.
* Memoizes the layers list for the LayerList component and centralizing indexing purposes.
*/
const memoLayersList = useMemo(() => {
// Log
Expand All @@ -140,7 +140,7 @@ export function DetailsPanel(): JSX.Element {
}, [visibleLayers, arrayOfLayerDataBatch, getNumFeaturesLabel]);

/**
* Memoize the selected layer for the LayerList component.
* Memoizes the selected layer for the LayerList component.
*/
const memoLayerSelectedItem = useMemo(() => {
// Log
Expand All @@ -149,7 +149,7 @@ export function DetailsPanel(): JSX.Element {
}, [memoLayersList, selectedLayerPath]);

/**
* Memoize the selected layer data.
* Memoizes the selected layer data.
*/
const memoSelectedLayerData = useMemo(() => {
// Log
Expand All @@ -158,7 +158,7 @@ export function DetailsPanel(): JSX.Element {
}, [arrayOfLayerDataBatch, selectedLayerPath]);

/**
* Memoize the selected layer data features.
* Memoizes the selected layer data features.
*/
const memoSelectedLayerDataFeatures = useMemo(() => {
// Log
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,13 @@ import {
TypeEsriFeatureLayerConfig,
TypeEsriFeatureLayerEntryConfig,
TypeEsriImageLayerEntryConfig,
TypeGeoCoreLayerConfig,
TypeGeoJSONLayerConfig,
TypeGeoJSONLayerEntryConfig,
TypeGeoPackageLayerConfig,
TypeGeoPackageLayerEntryConfig,
TypeGeocoreLayerEntryConfig,
TypeGeoviewLayerConfig,
TypeGeoviewLayerType,
TypeLayerEntryConfig,
TypeLayerEntryType,
TypeListOfGeoviewLayerConfig,
TypeListOfLayerEntryConfig,
TypeOgcWmsLayerEntryConfig,
Expand All @@ -39,7 +36,7 @@ import { ButtonPropsLayerPanel, SelectChangeEvent, TypeJsonArray, TypeJsonObject
import { useGeoViewMapId } from '@/core/stores/geoview-store';
import { createLocalizedString } from '@/core/utils/utilities';
import { useLayerStoreActions, useLayersList } from '@/core/stores/store-interface-and-intial-values/layer-state';
import { Cast, Config, api, generateId } from '@/app';
import { Cast, Config, api } from '@/app';
import { logger } from '@/core/utils/logger';
import { EsriImage, TypeEsriImageLayerConfig } from '@/geo/layer/geoview-layers/raster/esri-image';

Expand Down Expand Up @@ -98,10 +95,10 @@ export function AddNewLayer(): JSX.Element {
// const acceptedFiles = ["*.json"];

useEffect(() => {
// eslint-disable-next-line no-console
console.log('layersList ', layersList);
// Log
logger.logTraceUseEffect('layersList ', layersList);

// setIsLoading(false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [layersList]);

const sxClasses = {
Expand Down Expand Up @@ -413,19 +410,9 @@ export function AddNewLayer(): JSX.Element {
try {
const isValid = layerURL.indexOf('/') === -1 && layerURL.replaceAll('-', '').length === 32;
if (!isValid) throw new Error('err');
const geoCoreGeoviewLayerConfig = {
geoviewLayerId: generateId(),
geoviewLayerType: 'geoCore',
listOfLayerEntryConfig: [
new TypeGeocoreLayerEntryConfig({
schemaTag: 'geoCore' as TypeGeoviewLayerType,
entryType: 'geoCore' as TypeLayerEntryType,
layerId: layerURL,
} as TypeGeocoreLayerEntryConfig),
] as TypeGeocoreLayerEntryConfig[],
} as TypeGeoCoreLayerConfig;

const geoCoreGeoviewLayerInstance = new GeoCore(mapId);
const layers = await geoCoreGeoviewLayerInstance.createLayers(geoCoreGeoviewLayerConfig);
const layers = await geoCoreGeoviewLayerInstance.createLayersFromUUID(layerURL);
if (layers.length === 1) {
if (layers[0].length === 1) {
setLayerName(layers[0][0].geoviewLayerName!.en! as string);
Expand Down Expand Up @@ -843,6 +830,10 @@ export function AddNewLayer(): JSX.Element {
(layerList as TypeListOfGeoviewLayerConfig).forEach((geoviewLayerConfig) => {
api.maps[mapId].layer.addGeoviewLayer(geoviewLayerConfig);
});
} else if (layerEntries.length > 0) {
(layerEntries as TypeListOfGeoviewLayerConfig).forEach((geoviewLayerConfig) => {
api.maps[mapId].layer.addGeoviewLayer(geoviewLayerConfig);
});
}
} else if (geoviewLayerInstance) {
geoviewLayerInstance.geoviewLayerName = createLocalizedString(layerName);
Expand Down
Loading

0 comments on commit ce9bf2c

Please sign in to comment.