diff --git a/docs/programming/logging.md b/docs/programming/logging.md
index a39eec8a0b3..3dd9200d39f 100644
--- a/docs/programming/logging.md
+++ b/docs/programming/logging.md
@@ -11,7 +11,7 @@ The logger class can be found here: [https://github.com/Canadian-Geospatial-Pla
The `logger` provides functions for high-level logging abstraction following best-practices concepts and the following constants:
```ts
// The most detailed messages. Disabled by default. Only shows if actually running in dev environment, never shown otherwise.
-export const LOG_TRACE_DETAILED 1;
+export const LOG_TRACE_DETAILED = 1;
// For tracing useEffect unmounting. Disabled by default. Only shows if running in dev environment or GEOVIEW_LOG_ACTIVE key is set in local storage.
export const LOG_TRACE_USE_EFFECT_UNMOUNT = 2;
// For tracing rendering. Disabled by default. Only shows if running in dev environment or GEOVIEW_LOG_ACTIVE key is set in local storage.
diff --git a/packages/geoview-core/public/configs/package-footer-panel-geochart-map1-config-geochart.json b/packages/geoview-core/public/configs/package-footer-panel-geochart-map1-config-geochart.json
index cfb75decb96..9bc3a7637b5 100644
--- a/packages/geoview-core/public/configs/package-footer-panel-geochart-map1-config-geochart.json
+++ b/packages/geoview-core/public/configs/package-footer-panel-geochart-map1-config-geochart.json
@@ -137,7 +137,7 @@
{
"layers": [
{
- "layerId": "geojsonLYR5/polygons.json"
+ "layerId": "geojsonLYR5/geojsonLYR5/polygons.json"
}
],
"chart": "pie",
@@ -232,7 +232,7 @@
{
"layers": [
{
- "layerId": "geojsonLYR5/point-feature-group/points.json"
+ "layerId": "geojsonLYR5/geojsonLYR5/point-feature-group/points.json"
}
],
"chart": "bar",
diff --git a/packages/geoview-core/public/configs/package-footer-panel-geochart-map2-config-geochart.json b/packages/geoview-core/public/configs/package-footer-panel-geochart-map2-config-geochart.json
index cfb75decb96..9bc3a7637b5 100644
--- a/packages/geoview-core/public/configs/package-footer-panel-geochart-map2-config-geochart.json
+++ b/packages/geoview-core/public/configs/package-footer-panel-geochart-map2-config-geochart.json
@@ -137,7 +137,7 @@
{
"layers": [
{
- "layerId": "geojsonLYR5/polygons.json"
+ "layerId": "geojsonLYR5/geojsonLYR5/polygons.json"
}
],
"chart": "pie",
@@ -232,7 +232,7 @@
{
"layers": [
{
- "layerId": "geojsonLYR5/point-feature-group/points.json"
+ "layerId": "geojsonLYR5/geojsonLYR5/point-feature-group/points.json"
}
],
"chart": "bar",
diff --git a/packages/geoview-core/public/templates/package-footer-panel-geochart.html b/packages/geoview-core/public/templates/package-footer-panel-geochart.html
index d9dbcecdd29..6c88e8c5fcd 100644
--- a/packages/geoview-core/public/templates/package-footer-panel-geochart.html
+++ b/packages/geoview-core/public/templates/package-footer-panel-geochart.html
@@ -40,7 +40,7 @@
Package Footer Panel With Geo-Chart
-
+
diff --git a/packages/geoview-core/src/api/event-processors/event-processor-children/geochart-event-processor.ts b/packages/geoview-core/src/api/event-processors/event-processor-children/geochart-event-processor.ts
new file mode 100644
index 00000000000..359e658b64c
--- /dev/null
+++ b/packages/geoview-core/src/api/event-processors/event-processor-children/geochart-event-processor.ts
@@ -0,0 +1,59 @@
+import { GeoviewStoreType } from '@/core/stores/geoview-store';
+import { AbstractEventProcessor } from '../abstract-event-processor';
+import { getGeoViewStore } from '@/core/stores/stores-managers';
+import { TypeJsonObject } from '@/core/types/global-types';
+import { GeoChartStoreByLayerPath } from '@/core/stores/store-interface-and-intial-values/geochart-state';
+
+export class GeochartEventProcessor extends AbstractEventProcessor {
+ onInitialize(store: GeoviewStoreType) {
+ store.getState();
+
+ // add to arr of subscriptions so it can be destroyed later
+ this.subscriptionArr.push();
+ }
+
+ // **********************************************************
+ // Static functions for Typescript files to access store actions
+ // **********************************************************
+ //! Typescript MUST always use store action to modify store - NEVER use setState!
+ //! Some action does state modifications AND map actions.
+ //! ALWAYS use map event processor when an action modify store and IS NOT trap by map state event handler
+
+ // #region
+
+ /**
+ * Set 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
+ */
+ static setGeochartCharts(mapId: string, charts: TypeJsonObject[]): 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) => {
+ // For each layer path
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ chartInfo.layers.forEach((layer: any) => {
+ // Get the layer path
+ const layerPath = layer.layerId;
+ chartData[layerPath] = chartInfo;
+ });
+ });
+
+ // set store charts config
+ getGeoViewStore(mapId).getState().geochartState.actions.setGeochartCharts(chartData);
+ }
+
+ // #endregion
+
+ // **********************************************************
+ // Static functions for Store Map State to action on API
+ // **********************************************************
+ //! NEVER add a store action who does set state AND map action at a same time.
+ //! Review the action in store state to make sure
+}
diff --git a/packages/geoview-core/src/api/event-processors/index.ts b/packages/geoview-core/src/api/event-processors/index.ts
index 2034644e339..b13bfa490b5 100644
--- a/packages/geoview-core/src/api/event-processors/index.ts
+++ b/packages/geoview-core/src/api/event-processors/index.ts
@@ -4,12 +4,17 @@ import { GeoviewStoreType } from '@/core/stores/geoview-store';
import { LegendEventProcessor } from '@/api/event-processors/event-processor-children/legend-event-processor';
import { MapEventProcessor } from '@/api/event-processors/event-processor-children/map-event-processor';
import { TimeSliderEventProcessor } from '@/api/event-processors/event-processor-children/time-slider-event-processor';
+import { GeochartEventProcessor } from '@/api/event-processors/event-processor-children/geochart-event-processor';
+// core
const appEventProcessor = new AppEventProcessor();
const featureInfoEventProcessor = new FeatureInfoEventProcessor();
const legendEventProcessor = new LegendEventProcessor();
const mapEventProcessor = new MapEventProcessor();
+
+// packages
const timeSliderEventProcessor = new TimeSliderEventProcessor();
+const geochartEventProcessor = new GeochartEventProcessor();
export function initializeEventProcessors(store: GeoviewStoreType) {
// core stores
@@ -21,6 +26,7 @@ export function initializeEventProcessors(store: GeoviewStoreType) {
// package stores, only create if needed
// TODO: Change this check for something more generic that checks in appBarTabs too
if (store.getState().mapConfig!.footerTabs?.tabs.core.includes('time-slider')) timeSliderEventProcessor.onInitialize(store);
+ if (store.getState().mapConfig!.footerTabs?.tabs.core.includes('geochart')) geochartEventProcessor.onInitialize(store);
}
export function destroyEventProcessors(store: GeoviewStoreType) {
@@ -33,4 +39,5 @@ export function destroyEventProcessors(store: GeoviewStoreType) {
// package stores, only destroy if created
// TODO: Change this check for something more generic that checks in appBarTabs too
if (store.getState().mapConfig!.footerTabs?.tabs.core.includes('time-slider')) timeSliderEventProcessor.onDestroy(store);
+ if (store.getState().mapConfig!.footerTabs?.tabs.core.includes('geochart')) geochartEventProcessor.onDestroy(store);
}
diff --git a/packages/geoview-core/src/core/app-start.tsx b/packages/geoview-core/src/core/app-start.tsx
index 58230aba44a..598a1470c15 100644
--- a/packages/geoview-core/src/core/app-start.tsx
+++ b/packages/geoview-core/src/core/app-start.tsx
@@ -1,4 +1,4 @@
-import React, { Suspense, useMemo } from 'react';
+import { createContext, Suspense, useMemo } from 'react';
import './translation/i18n';
import i18n from 'i18next';
@@ -16,7 +16,7 @@ import { logger } from './utils/logger';
// create a state that will hold map config information
// TODO: use store, only keep map id on context for store manager to gather right store on hooks
-export const MapContext = React.createContext({
+export const MapContext = createContext({
mapId: '',
mapFeaturesConfig: undefined,
});
@@ -80,7 +80,9 @@ function AppStart(props: AppStartProps): JSX.Element {
+ {/* */}
+ {/* */}
diff --git a/packages/geoview-core/src/core/components/common/enlarge-button.tsx b/packages/geoview-core/src/core/components/common/enlarge-button.tsx
index 04f87c20fa1..fa87b7af026 100644
--- a/packages/geoview-core/src/core/components/common/enlarge-button.tsx
+++ b/packages/geoview-core/src/core/components/common/enlarge-button.tsx
@@ -6,6 +6,7 @@ import { getSxClasses } from './enlarge-button-style';
interface EnlargeButtonProps {
isEnlargeDataTable: boolean;
+ // TODO: Refactor this props something like 'onEnlarge'? (getting rid of legacy 'data table' stuff and align with callback namings conv?)
setIsEnlargeDataTable: Dispatch;
}
diff --git a/packages/geoview-core/src/core/components/common/layer-list.tsx b/packages/geoview-core/src/core/components/common/layer-list.tsx
index e167d5bf794..683b8aa8904 100644
--- a/packages/geoview-core/src/core/components/common/layer-list.tsx
+++ b/packages/geoview-core/src/core/components/common/layer-list.tsx
@@ -7,9 +7,9 @@ import { IconStack } from '@/app';
export interface LayerListEntry {
layerName: string;
layerPath: string;
- layerFeatures?: ReactNode | undefined;
- mapFilteredIcon?: ReactNode | undefined;
- tooltip?: ReactNode | undefined;
+ layerFeatures?: ReactNode;
+ mapFilteredIcon?: ReactNode;
+ tooltip?: ReactNode;
numOffeatures?: number;
}
diff --git a/packages/geoview-core/src/core/components/common/layout.tsx b/packages/geoview-core/src/core/components/common/layout.tsx
index ffa991475c0..15c22aacab9 100644
--- a/packages/geoview-core/src/core/components/common/layout.tsx
+++ b/packages/geoview-core/src/core/components/common/layout.tsx
@@ -14,10 +14,12 @@ interface LayoutProps {
children?: ReactNode;
layerList: LayerListEntry[];
selectedLayerPath: string;
+ // ? Name this onLayerListClicked? and make it optional with '?' suffix?
handleLayerList: (layer: LayerListEntry) => void;
+ onIsEnlargeClicked?: (isEnlarge: boolean) => void;
}
-export function Layout({ children, layerList, handleLayerList, selectedLayerPath }: LayoutProps) {
+export function Layout({ children, layerList, selectedLayerPath, handleLayerList, onIsEnlargeClicked }: LayoutProps) {
const { t } = useTranslation();
const theme = useTheme();
@@ -40,6 +42,19 @@ export function Layout({ children, layerList, handleLayerList, selectedLayerPath
setIsLayersPanelVisible(true);
};
+ /**
+ * Handles click on the Enlarge button.
+ *
+ * @param {boolean} isEnlarge Indicate if enlarge
+ */
+ const handleIsEnlarge = (isEnlarge: boolean): void => {
+ // Set the isEnlarge
+ setIsEnlargeDataTable(isEnlarge);
+
+ // Callback
+ onIsEnlargeClicked?.(isEnlarge);
+ };
+
/**
* Render group layers as list.
*
@@ -57,7 +72,7 @@ export function Layout({ children, layerList, handleLayerList, selectedLayerPath
/>
);
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [selectedLayerPath, isEnlargeDataTable]);
+ }, [selectedLayerPath, isEnlargeDataTable, layerList]);
return (
@@ -76,7 +91,7 @@ export function Layout({ children, layerList, handleLayerList, selectedLayerPath
>
{layerList.find((layer) => layer.layerPath === selectedLayerPath)?.layerName ?? ''}
-
+
@@ -96,4 +111,5 @@ export function Layout({ children, layerList, handleLayerList, selectedLayerPath
Layout.defaultProps = {
children: null,
+ onIsEnlargeClicked: undefined,
};
diff --git a/packages/geoview-core/src/core/stores/geoview-store.ts b/packages/geoview-core/src/core/stores/geoview-store.ts
index 9a476f1d156..e71dcc4a0be 100644
--- a/packages/geoview-core/src/core/stores/geoview-store.ts
+++ b/packages/geoview-core/src/core/stores/geoview-store.ts
@@ -10,6 +10,7 @@ import { ILayerState, initializeLayerState } from './store-interface-and-intial-
import { IMapState, initializeMapState } from './store-interface-and-intial-values/map-state';
import { IMapDataTableState, initialDataTableState } from './store-interface-and-intial-values/data-table-state';
import { ITimeSliderState, initializeTimeSliderState } from './store-interface-and-intial-values/time-slider-state';
+import { IGeochartState, initializeGeochartState } from './store-interface-and-intial-values/geochart-state';
import { IUIState, initializeUIState } from './store-interface-and-intial-values/ui-state';
import { TypeMapFeaturesConfig } from '@/core/types/global-types';
@@ -26,14 +27,17 @@ export interface IGeoviewState {
mapId: string;
setMapConfig: (config: TypeMapFeaturesConfig) => void;
- // state interfaces
+ // core state interfaces
appState: IAppState;
detailsState: IDetailsState;
dataTableState: IMapDataTableState;
layerState: ILayerState;
mapState: IMapState;
- timeSliderState: ITimeSliderState;
uiState: IUIState;
+
+ // packages state interface
+ geochartState: IGeochartState;
+ timeSliderState: ITimeSliderState;
}
export const geoviewStoreDefinition = (set: TypeSetStore, get: TypeGetStore) => {
@@ -56,6 +60,7 @@ export const geoviewStoreDefinition = (set: TypeSetStore, get: TypeGetStore) =>
// packages states, only create if needed
// TODO: Change this check for something more generic that checks in appBarTabs too
if (config.footerTabs?.tabs.core.includes('time-slider')) set({ timeSliderState: initializeTimeSliderState(set, get) });
+ if (config.footerTabs?.tabs.core.includes('geochart')) set({ geochartState: initializeGeochartState(set, get) });
},
// core states
diff --git a/packages/geoview-core/src/core/stores/store-interface-and-intial-values/geochart-state.ts b/packages/geoview-core/src/core/stores/store-interface-and-intial-values/geochart-state.ts
new file mode 100644
index 00000000000..812453f1795
--- /dev/null
+++ b/packages/geoview-core/src/core/stores/store-interface-and-intial-values/geochart-state.ts
@@ -0,0 +1,55 @@
+import { useStore } from 'zustand';
+import { useGeoViewStore } from '../stores-managers';
+import { TypeGetStore, TypeSetStore } from '../geoview-store';
+
+export type GeoChartStoreByLayerPath = {
+ [layerPath: string]: ChartInfo;
+};
+
+export type ChartInfo = unknown; // unknown, because the definition is in the external package
+
+// #region INTERFACES
+export interface IGeochartState {
+ geochartChartsConfig: GeoChartStoreByLayerPath;
+ // geochartLayers: TypeJsonObject[];
+ // visibleGeochartLayers: string[];
+
+ actions: {
+ setGeochartCharts: (charts: GeoChartStoreByLayerPath) => void;
+ // setGeochartLayers: (layers: TypeJsonObject) => void;
+ };
+}
+// #endregion INTERFACES
+
+export function initializeGeochartState(set: TypeSetStore, get: TypeGetStore): IGeochartState {
+ const init = {
+ geochartChartsConfig: {},
+ // geochartLayers: {},
+ // geochartChartsConfig: [],
+ // visibleGeochartLayers: [],
+
+ // #region ACTIONS
+ actions: {
+ setGeochartCharts(charts: GeoChartStoreByLayerPath): void {
+ set({
+ geochartState: {
+ ...get().geochartState,
+ geochartChartsConfig: charts,
+ },
+ });
+ },
+ // #endregion ACTIONS
+ },
+ } as IGeochartState;
+
+ return init;
+}
+
+// **********************************************************
+// Layer state selectors
+// **********************************************************
+export const useGeochartConfigs = () => useStore(useGeoViewStore(), (state) => state.geochartState.geochartChartsConfig);
+// export const useGeochartLayers = () => useStore(useGeoViewStore(), (state) => state.geochartState.geochartLayers);
+// export const useVisibleGeochartLayers = () => useStore(useGeoViewStore(), (state) => state.geochartState.visibleGeochartLayers);
+
+export const useGeochartStoreActions = () => useStore(useGeoViewStore(), (state) => state.geochartState.actions);
diff --git a/packages/geoview-core/src/core/stores/store-interface-and-intial-values/layer-state.ts b/packages/geoview-core/src/core/stores/store-interface-and-intial-values/layer-state.ts
index 4c777589af6..3ae53a368d7 100644
--- a/packages/geoview-core/src/core/stores/store-interface-and-intial-values/layer-state.ts
+++ b/packages/geoview-core/src/core/stores/store-interface-and-intial-values/layer-state.ts
@@ -303,7 +303,7 @@ function findLayerByPath(layers: TypeLegendLayer[], layerPath: string): TypeLege
if (layerPath === l.layerPath) {
return l;
}
- if (layerPath.startsWith(l.layerPath) && l.children?.length > 0) {
+ if (layerPath?.startsWith(l.layerPath) && l.children?.length > 0) {
const result: TypeLegendLayer | undefined = findLayerByPath(l.children, layerPath);
if (result) {
return result;
diff --git a/packages/geoview-core/src/core/stores/store-interface-and-intial-values/time-slider-state.ts b/packages/geoview-core/src/core/stores/store-interface-and-intial-values/time-slider-state.ts
index 086c711ccbd..aa421d9b7f7 100644
--- a/packages/geoview-core/src/core/stores/store-interface-and-intial-values/time-slider-state.ts
+++ b/packages/geoview-core/src/core/stores/store-interface-and-intial-values/time-slider-state.ts
@@ -3,6 +3,7 @@ import { useGeoViewStore } from '../stores-managers';
import { TypeGetStore, TypeSetStore } from '../geoview-store';
import { TimeSliderEventProcessor } from '@/api/event-processors/event-processor-children/time-slider-event-processor';
+// #region INTERFACES
export interface TypeTimeSliderValues {
title?: string;
description?: string;
@@ -23,6 +24,7 @@ export interface TypeTimeSliderValues {
export interface ITimeSliderState {
timeSliderLayers: { [index: string]: TypeTimeSliderValues };
visibleTimeSliderLayers: string[];
+
actions: {
addTimeSliderLayer: (newLayer: { [index: string]: TypeTimeSliderValues }) => void;
applyFilters: (layerPath: string, values: number[]) => void;
@@ -38,11 +40,14 @@ export interface ITimeSliderState {
setVisibleTimeSliderLayers: (visibleLayerPaths: string[]) => void;
};
}
+// #endregion INTERFACES
export function initializeTimeSliderState(set: TypeSetStore, get: TypeGetStore): ITimeSliderState {
const init = {
timeSliderLayers: {},
visibleTimeSliderLayers: [],
+
+ // #region ACTIONS
actions: {
addTimeSliderLayer(newLayer: { [index: string]: TypeTimeSliderValues }): void {
set({
@@ -157,6 +162,7 @@ export function initializeTimeSliderState(set: TypeSetStore, get: TypeGetStore):
},
});
},
+ // #endregion ACTIONS
},
} as ITimeSliderState;
@@ -168,4 +174,5 @@ export function initializeTimeSliderState(set: TypeSetStore, get: TypeGetStore):
// **********************************************************
export const useTimeSliderLayers = () => useStore(useGeoViewStore(), (state) => state.timeSliderState.timeSliderLayers);
export const useVisibleTimeSliderLayers = () => useStore(useGeoViewStore(), (state) => state.timeSliderState.visibleTimeSliderLayers);
+
export const useTimeSliderStoreActions = () => useStore(useGeoViewStore(), (state) => state.timeSliderState.actions);
diff --git a/packages/geoview-core/src/geo/utils/feature-info-layer-set.ts b/packages/geoview-core/src/geo/utils/feature-info-layer-set.ts
index 58a622db57c..dcda3d4ee3c 100644
--- a/packages/geoview-core/src/geo/utils/feature-info-layer-set.ts
+++ b/packages/geoview-core/src/geo/utils/feature-info-layer-set.ts
@@ -60,17 +60,19 @@ export class FeatureInfoLayerSet {
private constructor(mapId: string) {
// This function determines whether a layer can be registered.
const registrationConditionFunction = (layerPath: string): boolean => {
+ // Log
+ logger.logTraceCore('FeatureInfoLayerSet registration condition...', layerPath, Object.keys(this.resultSets));
+
const layerConfig = api.maps[this.mapId].layer.registeredLayers[layerPath];
const queryable = layerConfig?.source?.featureInfo?.queryable;
- if (queryable) {
- FeatureInfoEventProcessor.propagateFeatureInfoToStore(mapId, layerPath, 'click', this.resultSets);
- return true;
- }
- return false;
+ return !!queryable;
};
// This function is used to initialise the data property of the layer path entry.
const registrationUserDataInitialisation = (layerPath: string) => {
+ // Log
+ logger.logTraceCore('FeatureInfoLayerSet initializing...', layerPath, Object.keys(this.resultSets));
+
this.disableClickOnLayer[layerPath] = false;
this.disableHoverOverLayer[layerPath] = false;
this.resultSets[layerPath].data = {};
@@ -83,6 +85,9 @@ export class FeatureInfoLayerSet {
};
this.resultSets[layerPath].data[eventType] = undefined;
});
+
+ // Propagate feature info to the store, now that the this.resultSets is more representative of the reality
+ FeatureInfoEventProcessor.propagateFeatureInfoToStore(mapId, layerPath, 'click', this.resultSets);
};
this.mapId = mapId;
diff --git a/packages/geoview-core/src/ui/autocomplete/autocomplete.tsx b/packages/geoview-core/src/ui/autocomplete/autocomplete.tsx
index a3d9942833e..5ed8482a48c 100644
--- a/packages/geoview-core/src/ui/autocomplete/autocomplete.tsx
+++ b/packages/geoview-core/src/ui/autocomplete/autocomplete.tsx
@@ -9,10 +9,7 @@ export interface TypeAutocompleteProps<
DisableClearable extends boolean | undefined = undefined,
FreeSolo extends boolean | undefined = undefined
> extends AutocompleteProps {
- // eslint-disable-next-line react/require-default-props
- mapId?: string;
- // eslint-disable-next-line react/require-default-props
- fullWidth?: boolean;
+ fullWidth: boolean;
}
/**
diff --git a/packages/geoview-geochart/src/geochart-panel.tsx b/packages/geoview-geochart/src/geochart-panel.tsx
index e816fa7c034..3376da311db 100644
--- a/packages/geoview-geochart/src/geochart-panel.tsx
+++ b/packages/geoview-geochart/src/geochart-panel.tsx
@@ -1,21 +1,28 @@
+import { useTranslation } from 'react-i18next';
+import { useTheme } from '@mui/material/styles';
import { TypeWindow } from 'geoview-core';
import { ChartType, SchemaValidator } from 'geochart';
import { LayerListEntry, Layout } from 'geoview-core/src/core/components/common';
+import { TypeArrayOfLayerData, TypeLayerData } from 'geoview-core/src/api/events/payloads';
+import { Typography } from 'geoview-core/src/ui/typography/typography';
+import { Paper } from 'geoview-core/src/ui';
+import { logger } from 'geoview-core/src/core/utils/logger';
+import { useMapVisibleLayers } from 'geoview-core/src/core/stores/store-interface-and-intial-values/map-state';
+import { useDetailsStoreLayerDataArray } from 'geoview-core/src/core/stores';
+import { useGeochartConfigs } from 'geoview-core/src/core/stores/store-interface-and-intial-values/geochart-state';
+
import { GeoChart } from './geochart';
-import { PluginGeoChartConfig } from './geochart-types';
+import { GeoViewGeoChartConfig } from './geochart-types';
+import { getSxClasses } from './geochart-style';
+
+type GeoChartRenders = {
+ [layerPath: string]: boolean;
+};
interface GeoChartPanelProps {
mapId: string;
- configObj: PluginGeoChartConfig;
- layerList: LayerListEntry[];
}
-const { cgpv } = window as TypeWindow;
-
-type GeoChartLayerChartConfig = {
- [layerPath: string]: PluginGeoChartConfig;
-};
-
/**
* Geo Chart tab
*
@@ -23,41 +30,150 @@ type GeoChartLayerChartConfig = {
* @returns {JSX.Element} Geo Chart tab
*/
export function GeoChartPanel(props: GeoChartPanelProps): JSX.Element {
- const { mapId, configObj, layerList } = props;
- const { react, ui } = cgpv;
- const { useState } = react;
- const { Box } = ui.elements;
+ // Log
+ logger.logTraceRender('geochart/geochart-panel');
+
+ const { cgpv } = window as TypeWindow;
+ const { mapId } = props;
+ const { react } = cgpv;
+ const { useState, useCallback, useEffect, useRef } = react;
+ const { t } = useTranslation();
+
+ const theme = useTheme();
+ const sxClasses = getSxClasses(theme);
+
+ // Prepare the states
const [selectedLayerPath, setSelectedLayerPath] = useState('');
+ const [geoChartLayersList, setGeoChartLayersList] = useState([]);
- // For a GeoChart to be used in a footer panel with a layer selection happening independently from the GeoChart itself,
- // we want 1 chart per layer, so this splits the main config in multiple chunks for each chart layer
- const layerChartConfigsState: GeoChartLayerChartConfig = {};
- configObj.charts.forEach((chart) => {
- // Retrieve a chart config for each layer
- const specificChartConfig = { charts: [chart] };
- const lyrPaths = chart.layers?.map((lyr) => {
- return lyr.layerId;
- });
- lyrPaths?.forEach((lyrPath) => {
- if (!(lyrPath in layerChartConfigsState)) {
- layerChartConfigsState[lyrPath] = specificChartConfig;
- }
- });
- });
- const [layerChartConfigs] = useState(layerChartConfigsState);
+ // get store geochart info
+ const configObj = useGeochartConfigs();
+ const visibleLayers = useMapVisibleLayers() as string[];
+ const arrayOfLayerData = useDetailsStoreLayerDataArray() as TypeArrayOfLayerData;
// Create the validator shared for all the charts in the footer
const [schemaValidator] = useState(new SchemaValidator());
+ // Lists the charts that were rendered at least once
+ const chartFirstRenders = useRef({} as GeoChartRenders);
+
+ /**
+ * Get number of features of a layer.
+ * @returns string
+ */
+ const getFeaturesOfLayer = useCallback(
+ (layer: TypeLayerData): string => {
+ const numOfFeatures = layer.features?.length ?? 0;
+ return `${numOfFeatures} ${t('details.feature')}${numOfFeatures > 1 ? 's' : ''}`;
+ },
+ [t]
+ );
+
/**
* Handles clicks to layers in left panel. Sets selected layer.
*
* @param {LayerListEntry} layer The data of the selected layer
*/
- const handleLayerChange = (layer: LayerListEntry): void => {
+ const handleLayerChange = useCallback((layer: LayerListEntry): void => {
+ // Log
+ logger.logTraceUseCallback('GEOCHART-PANEL - layer', layer);
+
+ // Set the selected layer path
setSelectedLayerPath(layer.layerPath);
- };
+ }, []);
+
+ /**
+ * Handles click on enlarge button in the layout component.
+ *
+ * @param {LayerListEntry} layer The data of the selected layer
+ */
+ const handleIsEnlargeClicked = useCallback(
+ (isEnlarge: boolean) => {
+ // Log
+ logger.logTraceUseCallback('GEOCHART-PANEL - is enlarge', isEnlarge);
+
+ // We need to redraw when the canvas isn't 'showing' in the DOM and when the user resizes the canvas placeholder.
+ cgpv.api.maps[mapId].plugins.geochart.redrawChart();
+ },
+ [cgpv.api.maps, mapId]
+ );
+
+ // Reacts when the array of layer data updates
+ useEffect(() => {
+ // Log
+ logger.logTraceUseEffect('GEOCHART-PANEL - ArrayOfLayerData', arrayOfLayerData);
+
+ // Update the layers list information
+ const layerListEntry: LayerListEntry[] = [];
+ arrayOfLayerData.forEach((layer) => {
+ // If the layer is visible and has a chart in the config
+ if (visibleLayers.includes(layer.layerPath) && configObj[layer.layerPath]) {
+ layerListEntry.push({
+ layerName: layer.layerName ?? '',
+ layerPath: layer.layerPath,
+ numOffeatures: layer.features?.length ?? 0,
+ layerFeatures: getFeaturesOfLayer(layer),
+ tooltip: `${layer.layerName}, ${getFeaturesOfLayer(layer)}`,
+ });
+ }
+ });
+
+ // Set the list
+ setGeoChartLayersList(layerListEntry);
+ }, [arrayOfLayerData, configObj, getFeaturesOfLayer, visibleLayers]);
+
+ // Reacts when the list of layers being officially listed changed
+ useEffect(() => {
+ // Log
+ logger.logTraceUseEffect('GEOCHART-PANEL - GeoChartLayersList', geoChartLayersList, selectedLayerPath);
+
+ // If there was a selected layer path already
+ let changeSelectedLayerPath = false;
+ let finalLayerPath = '';
+ if (selectedLayerPath) {
+ // Get the layer list entry for that layer path
+ const geoChartLayerEntry = geoChartLayersList.find((geoChartLayer: LayerListEntry) => geoChartLayer.layerPath === selectedLayerPath);
+
+ // If found
+ if (geoChartLayerEntry) {
+ // Check if there's nothing currently selected on that layer path
+ if (!geoChartLayerEntry.numOffeatures) {
+ changeSelectedLayerPath = true;
+ } else {
+ // There is something, stay on it.
+ finalLayerPath = selectedLayerPath;
+ }
+ }
+ } else {
+ // Find another layer with features
+ changeSelectedLayerPath = true;
+ }
+
+ // If changing
+ if (changeSelectedLayerPath) {
+ // Find another layer with features
+ const anotherGeoChartLayerEntry = geoChartLayersList.find((geoChartLayer: LayerListEntry) => geoChartLayer.numOffeatures);
+ // If found
+ if (anotherGeoChartLayerEntry) {
+ // Select that one
+ setSelectedLayerPath(anotherGeoChartLayerEntry.layerPath);
+ finalLayerPath = anotherGeoChartLayerEntry.layerPath;
+ } else {
+ // None found, select none
+ setSelectedLayerPath('');
+ }
+ }
+
+ // If it was the first rendering for that particular layer path
+ if (!chartFirstRenders.current[finalLayerPath]) {
+ // Rendered at least once
+ chartFirstRenders.current[finalLayerPath] = true;
+
+ // We need to redraw when the canvas isn't 'showing' in the DOM and when the user resizes the canvas placeholder.
+ cgpv.api.maps[mapId].plugins.geochart.redrawChart();
+ }
+ }, [cgpv.api.maps, geoChartLayersList, mapId, selectedLayerPath]);
/**
* Renders a single GeoChart component
@@ -65,31 +181,55 @@ export function GeoChartPanel(props: GeoChartPanelProps): JSX.Element {
* @param sx CSSProperties Styling to apply (basically if the GeoChart should be visible or not depending on the selected layer)
* @returns JSX.Element
*/
- const renderChart = (chartConfig: PluginGeoChartConfig, sx: React.CSSProperties, key: string) => {
- return ;
+ const renderChart = (chartConfig: GeoViewGeoChartConfig, sx: React.CSSProperties, key: string) => {
+ return ;
};
- // Loop on the layers to find out which is selected and grab the chart associated with the selected layer by moving its position to 0px
- // TODO: Remove the TEMP REDRAW button, obviously. Maybe think of a better way to handle the chart rendering? Leaving it like this for now.
- return (
-
-
- {
- cgpv.api.maps[mapId].plugins.geochart.redrawChart();
- }}
- />
-
- {Object.entries(layerChartConfigs).map(([layerPath, layerChartConfig], index) => {
- const sx: React.CSSProperties = { position: 'absolute', top: '-5000px' };
- if (layerPath === selectedLayerPath) {
- sx.top = '0px';
- }
- return renderChart(layerChartConfig, sx, index.toString());
- })}
-
- );
+ /**
+ * Renders the complete GeoChart Panel component
+ * @returns JSX.Element
+ */
+ const renderComplete = () => {
+ if (geoChartLayersList) {
+ if (geoChartLayersList.length > 0) {
+ return (
+
+ {selectedLayerPath &&
+ Object.entries(configObj).map(([layerPath, layerChartConfig], index) => {
+ const sx: React.CSSProperties = { position: 'absolute', top: '-5000px' };
+ if (layerPath === selectedLayerPath) {
+ sx.top = '0px';
+ }
+ return renderChart(layerChartConfig as GeoViewGeoChartConfig, sx, index.toString());
+ })}
+ {!selectedLayerPath && (
+
+
+ {t('geochart.chartPanel.noLayers')}
+
+
+ {t('geochart.chartPanel.noLayers')}
+
+
+ )}
+
+ );
+ }
+
+ // No layers
+ return {t('chartPanel.panel.noLayers')};
+ }
+
+ // Loading UI
+ return {t('chartPanel.panel.loadingUI')};
+ // return ;
+ };
+
+ // Render
+ return renderComplete();
}
diff --git a/packages/geoview-geochart/src/geochart-style.ts b/packages/geoview-geochart/src/geochart-style.ts
new file mode 100644
index 00000000000..0b3d98775f4
--- /dev/null
+++ b/packages/geoview-geochart/src/geochart-style.ts
@@ -0,0 +1,11 @@
+import { Theme } from '@mui/material/styles';
+
+export const getSxClasses = (theme: Theme) => ({
+ detailsInstructionsTitle: {
+ font: theme.footerPanel.titleFont,
+ fontSize: '1.5rem',
+ },
+ detailsInstructionsBody: {
+ fontSize: '1rem',
+ },
+});
diff --git a/packages/geoview-geochart/src/geochart.tsx b/packages/geoview-geochart/src/geochart.tsx
index e96ae62f7e5..1ef6dfe63ed 100644
--- a/packages/geoview-geochart/src/geochart.tsx
+++ b/packages/geoview-geochart/src/geochart.tsx
@@ -46,7 +46,7 @@ export function GeoChart(props: GeoChartProps): JSX.Element {
// Tweak the default colors based on the theme
const defaultColors: GeoChartDefaultColors = {
backgroundColor: theme.palette.background.default,
- borderColor: theme.palette.border.primary,
+ borderColor: 'black', // theme.palette.primary,
color: theme.palette.primary.main,
};
diff --git a/packages/geoview-geochart/src/index-appbar.tsx b/packages/geoview-geochart/src/index-appbar.tsx
index a57b48c537c..990b5af7366 100644
--- a/packages/geoview-geochart/src/index-appbar.tsx
+++ b/packages/geoview-geochart/src/index-appbar.tsx
@@ -1,12 +1,11 @@
import { Cast, AnySchemaObject, TypeIconButtonProps, TypePanelProps, toJsonObject, TypeJsonObject } from 'geoview-core';
import { AppBarPlugin } from 'geoview-core/src/api/plugin/appbar-plugin';
-import { ChartType, SchemaValidator } from 'geochart';
+import { ChartType } from 'geochart';
import { ChartIcon } from 'geoview-core/src/ui/icons';
import { PayloadBaseClassChart, EVENT_CHART_REDRAW } from './geochart-event-base';
import { PayloadChartConfig } from './geochart-event-config';
import { PluginGeoChartConfig } from './geochart-types';
-import { GeoChart } from './geochart';
import schema from '../schema.json';
import defaultConfig from '../default-config-geochart.json';
@@ -70,7 +69,9 @@ export class GeoChartAppBarPlugin extends AppBarPlugin {
onCreateContent(): JSX.Element {
// Fetch cgpv
- return ;
+ // TODO: Create a geochart-appbar-panel equivalent to geochart-panel to hold the GeoChart itself and hook on the useGeochartConfigs store the same way geochart-panel does it
+ // return ;
+ return Not implemented
;
}
/**
diff --git a/packages/geoview-geochart/src/index.tsx b/packages/geoview-geochart/src/index.tsx
index 543602f2d49..5fb6d7a054f 100644
--- a/packages/geoview-geochart/src/index.tsx
+++ b/packages/geoview-geochart/src/index.tsx
@@ -2,9 +2,9 @@ import { Cast, AnySchemaObject, toJsonObject, TypeJsonObject } from 'geoview-cor
import { FooterPlugin } from 'geoview-core/src/api/plugin/footer-plugin';
import { TypeTabs } from 'geoview-core/src/ui/tabs/tabs';
import { ChartType } from 'geochart';
-import { LayerListEntry } from 'geoview-core/src/core/components/common';
import { ChartIcon } from 'geoview-core/src/ui/icons';
+import { GeochartEventProcessor } from 'geoview-core/src/api/event-processors/event-processor-children/geochart-event-processor';
import { PayloadBaseClassChart, EVENT_CHART_REDRAW } from './geochart-event-base';
import { PayloadChartConfig } from './geochart-event-config';
import { PluginGeoChartConfig } from './geochart-types';
@@ -37,54 +37,32 @@ class GeoChartFooterPlugin extends FooterPlugin {
en: {
geochart: {
title: 'Chart',
+ panel: {
+ noLayers: 'No layers with chart data',
+ },
},
},
fr: {
geochart: {
title: 'Graphique',
+ panel: {
+ noLayers: 'Pas de couches avec des données graphiques',
+ },
},
},
});
- onCreateContentProps(): TypeTabs {
- // Cast the config
- const chartConfig: PluginGeoChartConfig | undefined = this.configObj;
-
- // Create content
- const layerList = chartConfig?.charts
- .map((chart) => {
- const layerIds =
- chart.layers?.map((layer) => {
- return layer.layerId;
- }) ?? [];
-
- return layerIds;
- })
- .flat()
- .reduce((acc, curr) => {
- if (this.api.maps[this.pluginProps.mapId].layer.registeredLayers[curr]) {
- const currLayer = this.api.maps[this.pluginProps.mapId].layer.registeredLayers[curr];
- const layerName =
- currLayer.layerName && this.displayLanguage() in currLayer.layerName
- ? currLayer.layerName[this.displayLanguage()]
- : currLayer.layerName;
- const layerData = {
- layerName,
- layerPath: curr,
- tooltip: layerName,
- };
- acc.push(layerData);
- }
+ onAdd(): void {
+ // Initialize the store with geochart provided configuration
+ GeochartEventProcessor.setGeochartCharts(this.pluginProps.mapId, this.configObj.charts);
- return acc;
- }, [] as LayerListEntry[]);
+ // Call parent
+ super.onAdd();
+ }
- // If any layers list
- let content = No layers in config
;
- if (layerList) {
- // Create element
- content = ;
- }
+ onCreateContentProps(): TypeTabs {
+ // Create element
+ const content = ;
return {
id: 'geochart',
diff --git a/packages/geoview-time-slider/src/time-slider-panel.tsx b/packages/geoview-time-slider/src/time-slider-panel.tsx
index 2d69a9e928c..699a589be74 100644
--- a/packages/geoview-time-slider/src/time-slider-panel.tsx
+++ b/packages/geoview-time-slider/src/time-slider-panel.tsx
@@ -2,6 +2,7 @@ import { TypeWindow } from 'geoview-core';
import { LayerListEntry, Layout } from 'geoview-core/src/core/components/common';
import { useVisibleTimeSliderLayers, useTimeSliderLayers } from 'geoview-core/src/core/stores';
import { getLocalizedMessage } from 'geoview-core/src/core/utils/utilities';
+import { Typography } from 'geoview-core/src/ui';
import { TimeSlider } from './time-slider';
import { ConfigProps } from './time-slider-types';
@@ -20,9 +21,8 @@ const { cgpv } = window as TypeWindow;
*/
export function TimeSliderPanel(props: TypeTimeSliderProps): JSX.Element {
const { mapId, configObj } = props;
- const { react, ui } = cgpv;
- const { useState, useEffect } = react;
- const { Box } = ui.elements;
+ const { react } = cgpv;
+ const { useState, useEffect, useCallback } = react;
// internal state
const [selectedLayerPath, setSelectedLayerPath] = useState();
@@ -44,18 +44,19 @@ export function TimeSliderPanel(props: TypeTimeSliderProps): JSX.Element {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [visibleTimeSliderLayers]);
- if (!visibleTimeSliderLayers.length) return {getLocalizedMessage(mapId, 'timeSlider.panel.noLayers')};
+ const renderLayerList = useCallback(() => {
+ const array = visibleTimeSliderLayers.map((layerPath: string) => {
+ return { layerName: timeSliderLayers[layerPath].name, layerPath, tooltip: timeSliderLayers[layerPath].name };
+ });
+
+ return array;
+ }, [timeSliderLayers, visibleTimeSliderLayers]);
+
return selectedLayerPath ? (
- {
- return { layerName: timeSliderLayers[layerPath].name, layerPath, tooltip: timeSliderLayers[layerPath].name };
- })}
- >
+
) : (
-
+ {getLocalizedMessage(mapId, 'timeSlider.panel.noLayers')}
);
}