From 2dff16cbdc37f413ec9b286434f2ceeb282c81ff Mon Sep 17 00:00:00 2001 From: Ismail Kutlu Date: Wed, 25 May 2022 14:28:03 +0200 Subject: [PATCH 1/9] feat: add custom action --- package-lock.json | 14 ++++++++ package.json | 2 ++ src/store.ts | 61 -------------------------------- src/store/actions.ts | 7 ++++ src/store/index.ts | 84 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 107 insertions(+), 61 deletions(-) delete mode 100644 src/store.ts create mode 100644 src/store/actions.ts create mode 100644 src/store/index.ts diff --git a/package-lock.json b/package-lock.json index cba72aac4d..c6d32f0ae5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,6 +41,7 @@ "react-router-dom": "^6.2.2", "react-scripts": "5.0.0", "redux": "^4.1.2", + "redux-actions": "^2.6.5", "redux-saga": "^1.1.3", "rsocket-core": "^0.0.27", "rsocket-flowable": "^0.0.27", @@ -60,6 +61,7 @@ "@commitlint/config-angular": "^16.0.0", "@types/node": "^17.0.13", "@types/react-virtualized-auto-sizer": "^1.0.1", + "@types/redux-actions": "^2.6.2", "@typescript-eslint/eslint-plugin": "^5.8.1", "@typescript-eslint/parser": "^5.8.1", "autoprefixer": "^10.4.1", @@ -4652,6 +4654,12 @@ "@types/react": "*" } }, + "node_modules/@types/redux-actions": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/redux-actions/-/redux-actions-2.6.2.tgz", + "integrity": "sha512-TvcINy8rWFANcpc3EiEQX9Yv3owM3d3KIrqr2ryUIOhYIYzXA/bhDZeGSSSuai62iVR2qMZUgz9tQ5kr0Kl+Tg==", + "dev": true + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -25394,6 +25402,12 @@ "@types/react": "*" } }, + "@types/redux-actions": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/redux-actions/-/redux-actions-2.6.2.tgz", + "integrity": "sha512-TvcINy8rWFANcpc3EiEQX9Yv3owM3d3KIrqr2ryUIOhYIYzXA/bhDZeGSSSuai62iVR2qMZUgz9tQ5kr0Kl+Tg==", + "dev": true + }, "@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", diff --git a/package.json b/package.json index 7c18c27df0..197731df98 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "react-router-dom": "^6.2.2", "react-scripts": "5.0.0", "redux": "^4.1.2", + "redux-actions": "^2.6.5", "redux-saga": "^1.1.3", "rsocket-core": "^0.0.27", "rsocket-flowable": "^0.0.27", @@ -98,6 +99,7 @@ "@commitlint/config-angular": "^16.0.0", "@types/node": "^17.0.13", "@types/react-virtualized-auto-sizer": "^1.0.1", + "@types/redux-actions": "^2.6.2", "@typescript-eslint/eslint-plugin": "^5.8.1", "@typescript-eslint/parser": "^5.8.1", "autoprefixer": "^10.4.1", diff --git a/src/store.ts b/src/store.ts deleted file mode 100644 index a622f72e7e..0000000000 --- a/src/store.ts +++ /dev/null @@ -1,61 +0,0 @@ -// import createSagaMiddleware from 'redux-saga'; -import { composeWithDevTools } from '@redux-devtools/extension'; -import keplerGlReducer, { visStateUpdaters } from 'kepler.gl/reducers'; -import { taskMiddleware } from 'react-palm/tasks'; -import { createStore, combineReducers, applyMiddleware } from 'redux'; - -const { INITIAL_VIS_STATE, INITIAL_UI_STATE } = visStateUpdaters; -const reducer = combineReducers({ - // <-- mount kepler.gl reducer in your app - keplerGl: keplerGlReducer.initialState({ - // Focus on Bruges - mapState: { - altitude: 1.5, - bearing: 24, - dragRotate: true, - latitude: 51.25821293189015, - longitude: 3.202802092344189, - maxPitch: 60, - maxZoom: 24, - minPitch: 0, - minZoom: 0, - pitch: 50, - zoom: 11.78, - }, - mapStyle: { - styleType: 'light', - }, - visState: { - ...INITIAL_VIS_STATE, - loaders: [], // Add additional loaders.gl loaders here - loadOptions: {}, // Add additional loaders.gl loader options here - - // enable search functionality - interactionConfig: { - ...INITIAL_VIS_STATE.interactionConfig, - geocoder: { - ...INITIAL_VIS_STATE.interactionConfig.geocoder, - enabled: true, - }, - }, - }, - uiState: { - ...INITIAL_UI_STATE, - activeSidePanel: 'odt.datasets', - currentModal: null, - }, - }), - // // Your other reducers here - // app: appReducer -}); - -// create store -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export const store = createStore(reducer, {}, composeWithDevTools(applyMiddleware(taskMiddleware))); - -// sagaMiddleware.run(rootSaga); - -// Infer the `RootState` and `AppDispatch` types from the store itself -export type RootState = ReturnType; -// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState} -export type AppDispatch = typeof store.dispatch; diff --git a/src/store/actions.ts b/src/store/actions.ts new file mode 100644 index 0000000000..7bcdf37f59 --- /dev/null +++ b/src/store/actions.ts @@ -0,0 +1,7 @@ +import { createAction } from 'redux-actions'; + +export const SHOW_LEGEND = 'SHOW_LEGEND'; + +// extra actions plugged into kepler.gl reducer +// eslint-disable-next-line import/prefer-default-export +export const showLegend = createAction(SHOW_LEGEND); diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 0000000000..93245ee097 --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,84 @@ +// import createSagaMiddleware from 'redux-saga'; +import { composeWithDevTools } from '@redux-devtools/extension'; +import keplerGlReducer, { visStateUpdaters, uiStateUpdaters } from 'kepler.gl/reducers'; +import { taskMiddleware } from 'react-palm/tasks'; +import { createStore, combineReducers, applyMiddleware } from 'redux'; + +import { SHOW_LEGEND } from './actions'; + +const { INITIAL_VIS_STATE } = visStateUpdaters; +const { INITIAL_UI_STATE } = uiStateUpdaters; +console.log('🚀 ~ INITIAL_UI_STATE', INITIAL_UI_STATE); + +const reducer = combineReducers({ + // <-- mount kepler.gl reducer in your app + keplerGl: keplerGlReducer + .initialState({ + // Focus on Bruges + mapState: { + altitude: 1.5, + bearing: 24, + dragRotate: true, + latitude: 51.25821293189015, + longitude: 3.202802092344189, + maxPitch: 60, + maxZoom: 24, + minPitch: 0, + minZoom: 0, + pitch: 50, + zoom: 11.78, + }, + mapStyle: { + styleType: 'light', + }, + visState: { + ...INITIAL_VIS_STATE, + loaders: [], // Add additional loaders.gl loaders here + loadOptions: {}, // Add additional loaders.gl loader options here + + // enable search functionality + interactionConfig: { + ...INITIAL_VIS_STATE.interactionConfig, + geocoder: { + ...INITIAL_VIS_STATE.interactionConfig.geocoder, + enabled: true, + }, + }, + }, + uiState: { + ...INITIAL_UI_STATE, + activeSidePanel: 'odt.datasets', + currentModal: null, + }, + }) + .plugin({ + // 1. as reducer map + [SHOW_LEGEND]: (state: any) => { + console.log('SHOW_LEGEND'); + return { + ...state, + uiState: { + ...state.uiState, + mapControls: { + ...INITIAL_UI_STATE.mapControls, + mapLegend: { + ...INITIAL_UI_STATE.mapControls.mapLegend, + active: true, + }, + }, + }, + }; + }, + }), +}); + +// create store +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const store = createStore(reducer, {}, composeWithDevTools(applyMiddleware(taskMiddleware))); + +// sagaMiddleware.run(rootSaga); + +// Infer the `RootState` and `AppDispatch` types from the store itself +export type RootState = ReturnType; +// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState} +export type AppDispatch = typeof store.dispatch; From 0168f93892f8b15fa55e52b3d28fe9e970040140 Mon Sep 17 00:00:00 2001 From: Ismail Kutlu Date: Wed, 25 May 2022 14:28:17 +0200 Subject: [PATCH 2/9] feat: dispatch legend action on data load --- .../components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx b/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx index 2e465ded34..6556a7c9b4 100644 --- a/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx +++ b/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx @@ -10,6 +10,7 @@ import { processRowObject } from 'kepler.gl/processors'; import { DataSet } from 'models/datasets'; import { getData as getDataFAPI } from 'repository/fapi'; import { getData as getDataSDP } from 'repository/sdpapi'; +import { showLegend } from 'store/actions'; import formStateToParams from 'utils/formUtils'; import { createKeplerTimeRangeFilter } from 'utils/keplerStateUtils'; @@ -72,6 +73,11 @@ const OdtDatasetsPanel = () => { }, }), ); + if (!json.length) { + console.warn('No data found for this period'); + return; + } + dispatch(showLegend()); } catch (error) { console.error(error); } finally { From f2fe4bf5b4b86d77157c8cb6f69d0ba67504c121 Mon Sep 17 00:00:00 2001 From: Ismail Kutlu Date: Mon, 30 May 2022 15:34:04 +0200 Subject: [PATCH 3/9] feat: improve legend scale and grouping --- .../OdtDatasetsPanel.tsx | 21 +++++++++++++++++-- src/store/index.ts | 11 +++++----- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx b/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx index 6556a7c9b4..db2d172315 100644 --- a/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx +++ b/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx @@ -5,11 +5,13 @@ import FieldsetBuilder from 'components/FieldsetBuilder'; import InlineLoader from 'components/InlineLoader'; import RadioGroup from 'components/RadioGroup'; import layerConfig from 'config/layerConfig'; -import { addDataToMap } from 'kepler.gl/actions'; +import { addDataToMap, layerVisualChannelConfigChange, layerColorUIChange } from 'kepler.gl/actions'; import { processRowObject } from 'kepler.gl/processors'; +import KeplerGlSchema from 'kepler.gl/schemas'; import { DataSet } from 'models/datasets'; import { getData as getDataFAPI } from 'repository/fapi'; import { getData as getDataSDP } from 'repository/sdpapi'; +import { store } from 'store'; import { showLegend } from 'store/actions'; import formStateToParams from 'utils/formUtils'; import { createKeplerTimeRangeFilter } from 'utils/keplerStateUtils'; @@ -41,6 +43,11 @@ const OdtDatasetsPanel = () => { [setFormValues, setDataset], ); + const getKeplerState = useCallback(() => { + const state = store.getState() as any; + return state.keplerGl.map; + }, []); + const handleSubmit = useCallback( async (event: FormEvent) => { try { @@ -73,11 +80,21 @@ const OdtDatasetsPanel = () => { }, }), ); + dispatch(showLegend()); + const state = getKeplerState(); + const { layers } = state.visState; + const oldLayer = layers.find((l: any) => l.config.dataId === dataId); + dispatch(layerColorUIChange(oldLayer, 'colorRange', { colorRangeConfig: { reversed: true } })); + dispatch( + layerVisualChannelConfigChange(oldLayer, { + colorScale: 'quantize', + colorDomain: [0, 100], + }), + ); if (!json.length) { console.warn('No data found for this period'); return; } - dispatch(showLegend()); } catch (error) { console.error(error); } finally { diff --git a/src/store/index.ts b/src/store/index.ts index 93245ee097..983fc2b3ea 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -52,9 +52,7 @@ const reducer = combineReducers({ }, }) .plugin({ - // 1. as reducer map [SHOW_LEGEND]: (state: any) => { - console.log('SHOW_LEGEND'); return { ...state, uiState: { @@ -72,11 +70,12 @@ const reducer = combineReducers({ }), }); -// create store -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export const store = createStore(reducer, {}, composeWithDevTools(applyMiddleware(taskMiddleware))); +const composeEnhancers = composeWithDevTools({ + actionsBlacklist: ['@@kepler.gl/MOUSE_MOVE', '@@kepler.gl/UPDATE_MAP', '@@kepler.gl/LAYER_HOVER'], +}); -// sagaMiddleware.run(rootSaga); +// create store +export const store = createStore(reducer, {}, composeEnhancers(applyMiddleware(taskMiddleware))); // Infer the `RootState` and `AppDispatch` types from the store itself export type RootState = ReturnType; From 5a121e862e50f13814e103f452a5bdf2d4c9f6cb Mon Sep 17 00:00:00 2001 From: Ismail Kutlu Date: Mon, 30 May 2022 17:16:02 +0200 Subject: [PATCH 4/9] refactor: toggle layer sidepanel --- .../CustomSidePanelsFactory/OdtDatasetsPanel.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx b/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx index db2d172315..279a9b3570 100644 --- a/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx +++ b/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx @@ -5,9 +5,8 @@ import FieldsetBuilder from 'components/FieldsetBuilder'; import InlineLoader from 'components/InlineLoader'; import RadioGroup from 'components/RadioGroup'; import layerConfig from 'config/layerConfig'; -import { addDataToMap, layerVisualChannelConfigChange, layerColorUIChange } from 'kepler.gl/actions'; +import { addDataToMap, layerVisualChannelConfigChange, layerColorUIChange, toggleSidePanel } from 'kepler.gl/actions'; import { processRowObject } from 'kepler.gl/processors'; -import KeplerGlSchema from 'kepler.gl/schemas'; import { DataSet } from 'models/datasets'; import { getData as getDataFAPI } from 'repository/fapi'; import { getData as getDataSDP } from 'repository/sdpapi'; @@ -59,6 +58,11 @@ const OdtDatasetsPanel = () => { ? await getDataSDP(selectedDataSet, params) : await getDataFAPI(selectedDataSet, params); + if (!json.length) { + console.warn('No data found for this period'); + return; + } + const dataId = new Date().getTime() + selectedDataSet.id; dispatch( @@ -85,16 +89,13 @@ const OdtDatasetsPanel = () => { const { layers } = state.visState; const oldLayer = layers.find((l: any) => l.config.dataId === dataId); dispatch(layerColorUIChange(oldLayer, 'colorRange', { colorRangeConfig: { reversed: true } })); + dispatch(toggleSidePanel('layer')); dispatch( layerVisualChannelConfigChange(oldLayer, { colorScale: 'quantize', colorDomain: [0, 100], }), ); - if (!json.length) { - console.warn('No data found for this period'); - return; - } } catch (error) { console.error(error); } finally { From b0ddfca1203c7c26b35e7cf3548fcbeb0d031eb8 Mon Sep 17 00:00:00 2001 From: Ismail Kutlu Date: Mon, 30 May 2022 17:31:29 +0200 Subject: [PATCH 5/9] build(docke): add redux-action types to deps --- package-lock.json | 8 +++----- package.json | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index c6d32f0ae5..730b5bcb0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@types/jest": "^27.4.0", "@types/react": "^17.0.37", "@types/react-dom": "^17.0.11", + "@types/redux-actions": "^2.6.2", "@types/rsocket-core": "^0.0.7", "@types/rsocket-websocket-client": "0.0.4", "ajv": "^8.8.2", @@ -61,7 +62,6 @@ "@commitlint/config-angular": "^16.0.0", "@types/node": "^17.0.13", "@types/react-virtualized-auto-sizer": "^1.0.1", - "@types/redux-actions": "^2.6.2", "@typescript-eslint/eslint-plugin": "^5.8.1", "@typescript-eslint/parser": "^5.8.1", "autoprefixer": "^10.4.1", @@ -4657,8 +4657,7 @@ "node_modules/@types/redux-actions": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/@types/redux-actions/-/redux-actions-2.6.2.tgz", - "integrity": "sha512-TvcINy8rWFANcpc3EiEQX9Yv3owM3d3KIrqr2ryUIOhYIYzXA/bhDZeGSSSuai62iVR2qMZUgz9tQ5kr0Kl+Tg==", - "dev": true + "integrity": "sha512-TvcINy8rWFANcpc3EiEQX9Yv3owM3d3KIrqr2ryUIOhYIYzXA/bhDZeGSSSuai62iVR2qMZUgz9tQ5kr0Kl+Tg==" }, "node_modules/@types/resolve": { "version": "1.17.1", @@ -25405,8 +25404,7 @@ "@types/redux-actions": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/@types/redux-actions/-/redux-actions-2.6.2.tgz", - "integrity": "sha512-TvcINy8rWFANcpc3EiEQX9Yv3owM3d3KIrqr2ryUIOhYIYzXA/bhDZeGSSSuai62iVR2qMZUgz9tQ5kr0Kl+Tg==", - "dev": true + "integrity": "sha512-TvcINy8rWFANcpc3EiEQX9Yv3owM3d3KIrqr2ryUIOhYIYzXA/bhDZeGSSSuai62iVR2qMZUgz9tQ5kr0Kl+Tg==" }, "@types/resolve": { "version": "1.17.1", diff --git a/package.json b/package.json index 197731df98..80ee0cff63 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@types/jest": "^27.4.0", "@types/react": "^17.0.37", "@types/react-dom": "^17.0.11", + "@types/redux-actions": "^2.6.2", "@types/rsocket-core": "^0.0.7", "@types/rsocket-websocket-client": "0.0.4", "ajv": "^8.8.2", @@ -99,7 +100,6 @@ "@commitlint/config-angular": "^16.0.0", "@types/node": "^17.0.13", "@types/react-virtualized-auto-sizer": "^1.0.1", - "@types/redux-actions": "^2.6.2", "@typescript-eslint/eslint-plugin": "^5.8.1", "@typescript-eslint/parser": "^5.8.1", "autoprefixer": "^10.4.1", From cdcc4acba24e005b0d60388b37d9c6e5290dc1c5 Mon Sep 17 00:00:00 2001 From: Ismail Kutlu Date: Tue, 31 May 2022 09:36:14 +0200 Subject: [PATCH 6/9] refactor: remove log --- src/store/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/store/index.ts b/src/store/index.ts index 983fc2b3ea..a372cae60e 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -8,7 +8,6 @@ import { SHOW_LEGEND } from './actions'; const { INITIAL_VIS_STATE } = visStateUpdaters; const { INITIAL_UI_STATE } = uiStateUpdaters; -console.log('🚀 ~ INITIAL_UI_STATE', INITIAL_UI_STATE); const reducer = combineReducers({ // <-- mount kepler.gl reducer in your app From 9956dc64d648748a2034b2c8bfe08052bfe22892 Mon Sep 17 00:00:00 2001 From: Ismail Kutlu Date: Tue, 31 May 2022 14:54:45 +0200 Subject: [PATCH 7/9] feat: add color scale support --- src/config/layerConfig.ts | 57 +++++++++++++++ src/models/datasets.ts | 8 +++ .../OdtDatasetsPanel.tsx | 69 +++++++++++++++---- 3 files changed, 122 insertions(+), 12 deletions(-) diff --git a/src/config/layerConfig.ts b/src/config/layerConfig.ts index c395ab33f9..377fa63321 100644 --- a/src/config/layerConfig.ts +++ b/src/config/layerConfig.ts @@ -39,6 +39,21 @@ const trafficAdapter = (datasetId: string, params: any): Adapter => { }; }; +// Generated by https://colorbrewer2.org/ +const aqColors = [ + '#006837', + '#1a9850', + '#66bd63', + '#a6d96a', + '#d9ef8b', + '#fee08b', + '#fdae61', + '#f46d43', + '#d73027', + '#a50026', +]; +const tfColors = ['#1a9641', '#a6d96a', '#ffffbf', '#fdae61', '#d7191c']; + const layerConfig: { [key: string]: DataSet } = { qweriu: { id: 'qweriu', @@ -79,6 +94,20 @@ const layerConfig: { [key: string]: DataSet } = { initialValue: new Date().toISOString().split('T')[0], }, ], + colorScale: { + 88: { + range: [0, 70], + colors: aqColors, + }, + 89: { + range: [0, 70], + colors: aqColors, + }, + 91: { + range: [0, 100], + colors: aqColors, + }, + }, tags: [{ label: TagName.AQ, className: 'bg-violet-50 border border-violet-600 text-violet-600' }], }, luftdaten: { @@ -116,6 +145,16 @@ const layerConfig: { [key: string]: DataSet } = { initialValue: new Date().toISOString().split('T')[0], }, ], + colorScale: { + 'sensordatavalues.0.value': { + range: [0, 70], + colors: aqColors, + }, + 'sensordatavalues.1.value': { + range: [0, 70], + colors: aqColors, + }, + }, tags: [{ label: TagName.AQ, className: 'bg-violet-50 border border-violet-600 text-violet-600' }], }, sdpawv: { @@ -197,6 +236,24 @@ const layerConfig: { [key: string]: DataSet } = { }, }, ], + colorScale: { + car: { + range: [0, 100], + colors: tfColors, + }, + bike: { + range: [0, 40], + colors: tfColors, + }, + lorry: { + range: [0, 20], + colors: tfColors, + }, + pedestrian: { + range: [0, 80], + colors: tfColors, + }, + }, tags: [{ label: TagName.TF, className: 'bg-orange-50 border border-orange-500 text-orange-500' }], adapter: ({ timemode, modality }) => trafficAdapter('awv', { timemode, modality }), }, diff --git a/src/models/datasets.ts b/src/models/datasets.ts index a6b41845f2..53ecc6d0fd 100644 --- a/src/models/datasets.ts +++ b/src/models/datasets.ts @@ -30,11 +30,19 @@ interface DateBuilderConfig extends BuilderConfig { type FieldBuilderConfig = SelectBuilderConfig | RadioBuilderConfig | DateBuilderConfig; +export interface ColorScale { + [key: string | number]: { + range: [number, number]; + colors: string[]; + }; +} + export interface DataSet { id: string; name: string; url?: string; filterFields: FieldBuilderConfig[]; + colorScale?: ColorScale; tags?: Tag[]; adapter?(args: any): Adapter; } diff --git a/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx b/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx index 279a9b3570..07d890b1c0 100644 --- a/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx +++ b/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx @@ -3,11 +3,18 @@ import { useDispatch } from 'react-redux'; import FieldsetBuilder from 'components/FieldsetBuilder'; import InlineLoader from 'components/InlineLoader'; -import RadioGroup from 'components/RadioGroup'; +import RadioGroup, { RadioGroupProps } from 'components/RadioGroup'; import layerConfig from 'config/layerConfig'; -import { addDataToMap, layerVisualChannelConfigChange, layerColorUIChange, toggleSidePanel } from 'kepler.gl/actions'; +import { + addDataToMap, + layerVisualChannelConfigChange, + layerColorUIChange, + toggleSidePanel, + layerVisConfigChange, +} from 'kepler.gl/actions'; import { processRowObject } from 'kepler.gl/processors'; -import { DataSet } from 'models/datasets'; +import { ColorScale, DataSet } from 'models/datasets'; +import { TagName } from 'models/tag'; import { getData as getDataFAPI } from 'repository/fapi'; import { getData as getDataSDP } from 'repository/sdpapi'; import { store } from 'store'; @@ -23,6 +30,17 @@ const dataSetOptions = Object.entries(dataSets).map(([key, value]) => ({ tags: value.tags, })); +const getMetric = (selectedDataSet: DataSet): string => { + let metric = ''; + if (selectedDataSet?.tags?.find((tag) => tag.label === TagName.AQ)) { + metric = 'pollutant'; + } + if (selectedDataSet?.tags?.find((tag) => tag.label === TagName.TF)) { + metric = 'modality'; + } + return metric; +}; + const OdtDatasetsPanel = () => { const dispatch = useDispatch(); @@ -47,6 +65,32 @@ const OdtDatasetsPanel = () => { return state.keplerGl.map; }, []); + const setColorScale = (oldLayer: any, layerLabel: string, metric: string, colorScale?: ColorScale) => { + if (colorScale) { + dispatch( + layerVisConfigChange(oldLayer, { + colorRange: { + name: layerLabel, + type: 'custom', + category: 'Custom', + colors: colorScale[metric].colors, + }, + }), + ); + } + // fixes the error about updating the react state on an unmounted component + setTimeout(() => { + dispatch(toggleSidePanel('layer')); + dispatch( + layerVisualChannelConfigChange(oldLayer, { + label: layerLabel, + colorScale: 'quantize', + ...(colorScale && { colorDomain: colorScale[metric].range }), + }), + ); + }); + }; + const handleSubmit = useCallback( async (event: FormEvent) => { try { @@ -63,6 +107,14 @@ const OdtDatasetsPanel = () => { return; } + let layerLabel = ''; + const metric = getMetric(selectedDataSet); + const filterField = selectedDataSet.filterFields.find((f) => f.fieldProps.id === metric); + const { items } = filterField?.fieldProps as RadioGroupProps; + if (filterField && items) { + layerLabel = items.find((i) => i.value === formValues[metric])?.label || ''; + } + const dataId = new Date().getTime() + selectedDataSet.id; dispatch( @@ -70,7 +122,7 @@ const OdtDatasetsPanel = () => { datasets: { info: { id: dataId, - label: `${selectedDataSet.name} ${selectedDataSet.id}`, + label: `${selectedDataSet.name} (${formValues.date})`, }, data: processRowObject(json), }, @@ -88,14 +140,7 @@ const OdtDatasetsPanel = () => { const state = getKeplerState(); const { layers } = state.visState; const oldLayer = layers.find((l: any) => l.config.dataId === dataId); - dispatch(layerColorUIChange(oldLayer, 'colorRange', { colorRangeConfig: { reversed: true } })); - dispatch(toggleSidePanel('layer')); - dispatch( - layerVisualChannelConfigChange(oldLayer, { - colorScale: 'quantize', - colorDomain: [0, 100], - }), - ); + setColorScale(oldLayer, layerLabel, formValues[metric], selectedDataSet.colorScale); } catch (error) { console.error(error); } finally { From 65935c750672eecbb7c1da52656bf722fb0ffd8c Mon Sep 17 00:00:00 2001 From: Ismail Kutlu Date: Tue, 31 May 2022 16:17:20 +0200 Subject: [PATCH 8/9] refactor: ditch color domain and scale & add units --- src/config/layerConfig.ts | 83 +++++++++++++++---- src/models/datasets.ts | 1 + .../OdtDatasetsPanel.tsx | 15 +--- 3 files changed, 73 insertions(+), 26 deletions(-) diff --git a/src/config/layerConfig.ts b/src/config/layerConfig.ts index 377fa63321..1df7cb2429 100644 --- a/src/config/layerConfig.ts +++ b/src/config/layerConfig.ts @@ -7,6 +7,21 @@ import { DataSet } from '../models/datasets'; import config from './configLoader'; import layerConfigValidator from './layerConfigValidator'; +// Generated by https://colorbrewer2.org/ +const aqColors = [ + '#006837', + '#1a9850', + '#66bd63', + '#a6d96a', + '#d9ef8b', + '#fee08b', + '#fdae61', + '#f46d43', + '#d73027', + '#a50026', +]; +const tfColors = ['#1a9641', '#a6d96a', '#ffffbf', '#fdae61', '#d7191c']; + const trafficAdapter = (datasetId: string, params: any): Adapter => { const { timemode, modality } = params; @@ -39,21 +54,6 @@ const trafficAdapter = (datasetId: string, params: any): Adapter => { }; }; -// Generated by https://colorbrewer2.org/ -const aqColors = [ - '#006837', - '#1a9850', - '#66bd63', - '#a6d96a', - '#d9ef8b', - '#fee08b', - '#fdae61', - '#f46d43', - '#d73027', - '#a50026', -]; -const tfColors = ['#1a9641', '#a6d96a', '#ffffbf', '#fdae61', '#d7191c']; - const layerConfig: { [key: string]: DataSet } = { qweriu: { id: 'qweriu', @@ -98,14 +98,17 @@ const layerConfig: { [key: string]: DataSet } = { 88: { range: [0, 70], colors: aqColors, + unit: 'µg/m³', }, 89: { range: [0, 70], colors: aqColors, + unit: 'µg/m³', }, 91: { range: [0, 100], colors: aqColors, + unit: 'µg/m³', }, }, tags: [{ label: TagName.AQ, className: 'bg-violet-50 border border-violet-600 text-violet-600' }], @@ -149,10 +152,12 @@ const layerConfig: { [key: string]: DataSet } = { 'sensordatavalues.0.value': { range: [0, 70], colors: aqColors, + unit: 'µg/m³', }, 'sensordatavalues.1.value': { range: [0, 70], colors: aqColors, + unit: 'µg/m³', }, }, tags: [{ label: TagName.AQ, className: 'bg-violet-50 border border-violet-600 text-violet-600' }], @@ -240,18 +245,22 @@ const layerConfig: { [key: string]: DataSet } = { car: { range: [0, 100], colors: tfColors, + unit: 'veh/h', }, bike: { range: [0, 40], colors: tfColors, + unit: 'veh/h', }, lorry: { range: [0, 20], colors: tfColors, + unit: 'veh/h', }, pedestrian: { range: [0, 80], colors: tfColors, + unit: 'veh/h', }, }, tags: [{ label: TagName.TF, className: 'bg-orange-50 border border-orange-500 text-orange-500' }], @@ -336,6 +345,28 @@ const layerConfig: { [key: string]: DataSet } = { }, }, ], + colorScale: { + car: { + range: [0, 100], + colors: tfColors, + unit: 'veh/h', + }, + bike: { + range: [0, 40], + colors: tfColors, + unit: 'veh/h', + }, + lorry: { + range: [0, 20], + colors: tfColors, + unit: 'veh/h', + }, + pedestrian: { + range: [0, 80], + colors: tfColors, + unit: 'veh/h', + }, + }, tags: [{ label: TagName.TF, className: 'bg-orange-50 border border-orange-500 text-orange-500' }], adapter: ({ timemode, modality }) => trafficAdapter('sig', { timemode, modality }), }, @@ -418,6 +449,28 @@ const layerConfig: { [key: string]: DataSet } = { }, }, ], + colorScale: { + car: { + range: [0, 100], + colors: tfColors, + unit: 'veh/h', + }, + bike: { + range: [0, 40], + colors: tfColors, + unit: 'veh/h', + }, + lorry: { + range: [0, 20], + colors: tfColors, + unit: 'veh/h', + }, + pedestrian: { + range: [0, 80], + colors: tfColors, + unit: 'veh/h', + }, + }, tags: [{ label: TagName.TF, className: 'bg-orange-50 border border-orange-500 text-orange-500' }], adapter: ({ timemode, modality }) => trafficAdapter('tml', { timemode, modality }), }, diff --git a/src/models/datasets.ts b/src/models/datasets.ts index 53ecc6d0fd..90418ee71f 100644 --- a/src/models/datasets.ts +++ b/src/models/datasets.ts @@ -34,6 +34,7 @@ export interface ColorScale { [key: string | number]: { range: [number, number]; colors: string[]; + unit: string; }; } diff --git a/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx b/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx index 07d890b1c0..7386e0383d 100644 --- a/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx +++ b/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx @@ -5,13 +5,7 @@ import FieldsetBuilder from 'components/FieldsetBuilder'; import InlineLoader from 'components/InlineLoader'; import RadioGroup, { RadioGroupProps } from 'components/RadioGroup'; import layerConfig from 'config/layerConfig'; -import { - addDataToMap, - layerVisualChannelConfigChange, - layerColorUIChange, - toggleSidePanel, - layerVisConfigChange, -} from 'kepler.gl/actions'; +import { addDataToMap, layerVisualChannelConfigChange, toggleSidePanel, layerVisConfigChange } from 'kepler.gl/actions'; import { processRowObject } from 'kepler.gl/processors'; import { ColorScale, DataSet } from 'models/datasets'; import { TagName } from 'models/tag'; @@ -66,6 +60,7 @@ const OdtDatasetsPanel = () => { }, []); const setColorScale = (oldLayer: any, layerLabel: string, metric: string, colorScale?: ColorScale) => { + const unit = colorScale ? ` (${colorScale[metric].unit})` : ''; if (colorScale) { dispatch( layerVisConfigChange(oldLayer, { @@ -80,15 +75,13 @@ const OdtDatasetsPanel = () => { } // fixes the error about updating the react state on an unmounted component setTimeout(() => { - dispatch(toggleSidePanel('layer')); dispatch( layerVisualChannelConfigChange(oldLayer, { - label: layerLabel, - colorScale: 'quantize', - ...(colorScale && { colorDomain: colorScale[metric].range }), + label: `${layerLabel}${unit}`, }), ); }); + dispatch(toggleSidePanel('layer')); }; const handleSubmit = useCallback( From cf938fb981998abc50665d6bb29ade343fb2dc7c Mon Sep 17 00:00:00 2001 From: Ismail Kutlu Date: Tue, 31 May 2022 16:34:52 +0200 Subject: [PATCH 9/9] feat: introduct short names --- src/config/layerConfig.ts | 5 +++++ src/config/layerConfigValidator.test.ts | 2 ++ src/models/datasets.ts | 1 + .../CustomSidePanelsFactory/OdtDatasetsPanel.tsx | 15 ++++++++++----- src/pages/Simulations.tsx | 2 ++ src/repository/fapi.test.ts | 1 + 6 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/config/layerConfig.ts b/src/config/layerConfig.ts index 1df7cb2429..415ab2d076 100644 --- a/src/config/layerConfig.ts +++ b/src/config/layerConfig.ts @@ -58,6 +58,7 @@ const layerConfig: { [key: string]: DataSet } = { qweriu: { id: 'qweriu', name: 'Qweriu', + shortName: 'QWE', url: `${config.api.baseUrl}/v1/federated/influxdb-qweriu-air-quality-filter`, filterFields: [ { @@ -116,6 +117,7 @@ const layerConfig: { [key: string]: DataSet } = { luftdaten: { id: 'luftdaten', name: 'Luftdaten', + shortName: 'LFT', url: `${config.api.baseUrl}/v1/federated/influxdb-luftdaten-air-quality-filter`, filterFields: [ { @@ -165,6 +167,7 @@ const layerConfig: { [key: string]: DataSet } = { sdpawv: { id: 'sdpawv', name: 'AWV', + shortName: 'AWV', filterFields: [ { type: FieldType.RADIO, @@ -269,6 +272,7 @@ const layerConfig: { [key: string]: DataSet } = { sdpsignco: { id: 'sdpsignco', name: 'SignCo', + shortName: 'SGC', filterFields: [ { type: FieldType.RADIO, @@ -373,6 +377,7 @@ const layerConfig: { [key: string]: DataSet } = { sdptelraam: { id: 'sdptelraam', name: 'Telraam', + shortName: 'TML', filterFields: [ { type: FieldType.RADIO, diff --git a/src/config/layerConfigValidator.test.ts b/src/config/layerConfigValidator.test.ts index a42f3e8cad..40ea5955c6 100644 --- a/src/config/layerConfigValidator.test.ts +++ b/src/config/layerConfigValidator.test.ts @@ -8,6 +8,7 @@ describe('layer configuration', () => { const invalidConfig: { [key: string]: DataSet } = { qweriu: { id: 'qweriu', + shortName: 'QWE', name: '', url: '', filterFields: [ @@ -37,6 +38,7 @@ describe('layer configuration', () => { const invalidConfig: { [key: string]: DataSet } = { qweriu: { id: 'qweriu', + shortName: 'QWE', name: '', url: '', filterFields: [ diff --git a/src/models/datasets.ts b/src/models/datasets.ts index 90418ee71f..38a9b8f69e 100644 --- a/src/models/datasets.ts +++ b/src/models/datasets.ts @@ -41,6 +41,7 @@ export interface ColorScale { export interface DataSet { id: string; name: string; + shortName: string; url?: string; filterFields: FieldBuilderConfig[]; colorScale?: ColorScale; diff --git a/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx b/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx index 7386e0383d..5e7d87bc92 100644 --- a/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx +++ b/src/pages/Physical/components/CustomSidePanelsFactory/OdtDatasetsPanel.tsx @@ -59,13 +59,13 @@ const OdtDatasetsPanel = () => { return state.keplerGl.map; }, []); - const setColorScale = (oldLayer: any, layerLabel: string, metric: string, colorScale?: ColorScale) => { - const unit = colorScale ? ` (${colorScale[metric].unit})` : ''; + const setColorScale = (oldLayer: any, label: string, metric: string, colorScale?: ColorScale) => { + const unit = colorScale ? ` ${colorScale[metric].unit}` : ''; if (colorScale) { dispatch( layerVisConfigChange(oldLayer, { colorRange: { - name: layerLabel, + name: label, type: 'custom', category: 'Custom', colors: colorScale[metric].colors, @@ -77,7 +77,7 @@ const OdtDatasetsPanel = () => { setTimeout(() => { dispatch( layerVisualChannelConfigChange(oldLayer, { - label: `${layerLabel}${unit}`, + label: `${label}${unit}`, }), ); }); @@ -133,7 +133,12 @@ const OdtDatasetsPanel = () => { const state = getKeplerState(); const { layers } = state.visState; const oldLayer = layers.find((l: any) => l.config.dataId === dataId); - setColorScale(oldLayer, layerLabel, formValues[metric], selectedDataSet.colorScale); + setColorScale( + oldLayer, + `${selectedDataSet.shortName} - ${layerLabel}`, + formValues[metric], + selectedDataSet.colorScale, + ); } catch (error) { console.error(error); } finally { diff --git a/src/pages/Simulations.tsx b/src/pages/Simulations.tsx index 7c08eb8816..b1cc902f04 100644 --- a/src/pages/Simulations.tsx +++ b/src/pages/Simulations.tsx @@ -16,6 +16,7 @@ import { getData as getDataFAPI } from 'repository/fapi'; const simulationOverview: DataSet = { id: 'simulation overview', name: 'Mobilize simulations overview', + shortName: 'MOB', url: `${config.api.baseUrl}/v1/federated/mobilize-simulations-overview`, filterFields: [], }; @@ -185,6 +186,7 @@ const Simulations = () => { const simulationDataSet: DataSet = { id: simulationDataSource.id, name: simulationDataSource.name, + shortName: 'MOB', url: simulationDataSource.url, filterFields: [], }; diff --git a/src/repository/fapi.test.ts b/src/repository/fapi.test.ts index 7c90f1f27a..18c1aecb7f 100644 --- a/src/repository/fapi.test.ts +++ b/src/repository/fapi.test.ts @@ -8,6 +8,7 @@ enableFetchMocks(); // This should be mocked as the config is prone to changes which can break hard-coded expected values. const mockedDataSet: DataSet = { id: 'qweriu', + shortName: 'QWE', name: 'Qweriu (last 24h)', url: 'https://api.dev.bruges.odt.imec-apt.be/v1/federated/influxdb-qweriu-air-quality', filterFields: [],