From 7cd511422d3c79fde456390a80f8118ff7566af5 Mon Sep 17 00:00:00 2001 From: Bjorn Sandvik Date: Wed, 27 Sep 2023 21:15:57 +0200 Subject: [PATCH 1/2] chore: period type select refactor (#2977) --- i18n/en.pot | 10 ++--- src/actions/layerEdit.js | 4 +- .../edit/thematic/ThematicDialog.js | 3 +- src/components/periods/PeriodTypeSelect.js | 40 +++++++++++-------- src/constants/periods.js | 14 ++++--- src/reducers/layerEdit.js | 6 +-- src/util/periods.js | 6 ++- 7 files changed, 47 insertions(+), 36 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index fbce2a3d2..7686c57f4 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2023-09-06T13:08:55.205Z\n" -"PO-Revision-Date: 2023-09-06T13:08:55.205Z\n" +"POT-Creation-Date: 2023-09-27T14:55:16.411Z\n" +"PO-Revision-Date: 2023-09-27T14:55:16.411Z\n" msgid "Untitled map, {{date}}" msgstr "Untitled map, {{date}}" @@ -802,9 +802,6 @@ msgstr "Previous year" msgid "Next year" msgstr "Next year" -msgid "Relative" -msgstr "Relative" - msgid "Period type" msgstr "Period type" @@ -1202,6 +1199,9 @@ msgstr "Equal counts" msgid "Symbol" msgstr "Symbol" +msgid "Relative" +msgstr "Relative" + msgid "Daily" msgstr "Daily" diff --git a/src/actions/layerEdit.js b/src/actions/layerEdit.js index 80037f548..e6fd006ad 100644 --- a/src/actions/layerEdit.js +++ b/src/actions/layerEdit.js @@ -179,10 +179,10 @@ export const setPeriodName = (periodName) => ({ }) // Set period type (thematic) -export const setPeriodType = (periodType, clearPeriod = true) => ({ +export const setPeriodType = (periodType, keepPeriod) => ({ type: types.LAYER_EDIT_PERIOD_TYPE_SET, periodType, - clearPeriod, + keepPeriod, }) // Set period (event & thematic) diff --git a/src/components/edit/thematic/ThematicDialog.js b/src/components/edit/thematic/ThematicDialog.js index 263c4120f..d074437c0 100644 --- a/src/components/edit/thematic/ThematicDialog.js +++ b/src/components/edit/thematic/ThematicDialog.js @@ -224,7 +224,6 @@ class ThematicDialog extends Component { noDataColor, operand, periodType, - settings, renderingStrategy, startDate, endDate, @@ -420,7 +419,7 @@ class ThematicDialog extends Component { { + const { hiddenPeriods } = useSystemSettings() + + const periodTypes = useMemo( + () => getPeriodTypes(includeRelativePeriods, hiddenPeriods), + [includeRelativePeriods, hiddenPeriods] + ) + + // Set default period type useEffect(() => { - const relativePeriodType = { - id: RELATIVE_PERIODS, - name: i18n.t('Relative'), - } + if (!value) { + const isRelativePeriod = !!( + includeRelativePeriods && + period && + getRelativePeriods().find((p) => p.id === period.id) + ) - if (!value && period) { - if (getRelativePeriods().find((p) => p.id === period.id)) { - // false will not clear the period dropdown - onChange(relativePeriodType, false) + if (!period || isRelativePeriod) { + // default to first period type + onChange(periodTypes[0], isRelativePeriod) } - } else if (!value) { - // set relativePeriods as default - onChange(relativePeriodType) } - }, [value, period, onChange]) + }, [value, period, periodTypes, includeRelativePeriods, onChange]) return ( [ - { - id: RELATIVE_PERIODS, - name: i18n.t('Relative'), - }, +export const periodTypes = (includeRelativePeriods) => [ + ...(includeRelativePeriods + ? [ + { + id: RELATIVE_PERIODS, + name: i18n.t('Relative'), + }, + ] + : []), { id: DAILY, name: i18n.t('Daily'), diff --git a/src/reducers/layerEdit.js b/src/reducers/layerEdit.js index 2549cbb36..f40823e43 100644 --- a/src/reducers/layerEdit.js +++ b/src/reducers/layerEdit.js @@ -107,9 +107,9 @@ const layerEdit = (state = null, action) => { return { ...state, periodType: action.periodType.id, - filters: action.clearPeriod - ? removePeriodFromFilters(state.filters) - : state.filters, + filters: action.keepPeriod + ? state.filters + : removePeriodFromFilters(state.filters), } case types.LAYER_EDIT_PERIOD_SET: diff --git a/src/util/periods.js b/src/util/periods.js index 366633e8a..2fdd0f826 100644 --- a/src/util/periods.js +++ b/src/util/periods.js @@ -6,8 +6,10 @@ import { periodTypes, periodGroups } from '../constants/periods.js' const getYearOffsetFromNow = (year) => year - new Date(Date.now()).getFullYear() -export const getPeriodTypes = (hiddenPeriods = []) => - periodTypes().filter(({ group }) => !hiddenPeriods.includes(group)) +export const getPeriodTypes = (includeRelativePeriods, hiddenPeriods = []) => + periodTypes(includeRelativePeriods).filter( + ({ group }) => !hiddenPeriods.includes(group) + ) export const getFixedPeriodsByType = (periodType, year) => { const period = getFixedPeriodsOptionsById(periodType) From 7f84e89e6338de66466155d03d887fdab6cf6e70 Mon Sep 17 00:00:00 2001 From: Bjorn Sandvik Date: Wed, 27 Sep 2023 21:16:20 +0200 Subject: [PATCH 2/2] chore: color scale refactor (#2982) --- .../classification/Classification.js | 2 +- src/components/core/ColorScaleSelect.js | 7 +- .../edit/earthEngine/EarthEngineDialog.js | 2 +- .../edit/earthEngine/LegendPreview.js | 2 +- .../edit/earthEngine/StyleSelect.js | 4 +- src/components/edit/earthEngine/StyleTab.js | 2 +- src/constants/earthEngine.js | 77 ++++++++++++++++--- src/loaders/earthEngineLoader.js | 10 ++- src/reducers/layerEdit.js | 2 +- .../__tests__/getMigratedMapConfig.spec.js | 37 +++++++++ src/util/colors.js | 14 ++-- src/util/favorites.js | 5 ++ src/util/getMigratedMapConfig.js | 36 ++++++--- src/util/legend.js | 3 +- 14 files changed, 158 insertions(+), 45 deletions(-) diff --git a/src/components/classification/Classification.js b/src/components/classification/Classification.js index 1b9031abe..5227c6168 100644 --- a/src/components/classification/Classification.js +++ b/src/components/classification/Classification.js @@ -68,7 +68,7 @@ Classification.propTypes = { setClassification: PropTypes.func.isRequired, setColorScale: PropTypes.func.isRequired, classes: PropTypes.number, - colorScale: PropTypes.string, + colorScale: PropTypes.array, method: PropTypes.number, } diff --git a/src/components/core/ColorScaleSelect.js b/src/components/core/ColorScaleSelect.js index ee3d735ac..7498ce974 100644 --- a/src/components/core/ColorScaleSelect.js +++ b/src/components/core/ColorScaleSelect.js @@ -14,12 +14,11 @@ const ColorScaleSelect = ({ palette, width, onChange, className }) => { const [isOpen, setIsOpen] = useState(false) const anchorRef = useRef() - const bins = palette.split(',').length + const bins = palette.length const scale = getColorScale(palette) const onColorScaleSelect = (scale) => { - const classes = palette.split(',').length - onChange(getColorPalette(scale, classes)) + onChange(getColorPalette(scale, bins)) setIsOpen(false) } @@ -61,7 +60,7 @@ const ColorScaleSelect = ({ palette, width, onChange, className }) => { } ColorScaleSelect.propTypes = { - palette: PropTypes.string.isRequired, + palette: PropTypes.array.isRequired, onChange: PropTypes.func.isRequired, className: PropTypes.string, width: PropTypes.number, diff --git a/src/components/edit/earthEngine/EarthEngineDialog.js b/src/components/edit/earthEngine/EarthEngineDialog.js index a435d042c..37998b45f 100644 --- a/src/components/edit/earthEngine/EarthEngineDialog.js +++ b/src/components/edit/earthEngine/EarthEngineDialog.js @@ -266,7 +266,7 @@ EarthEngineDialog.propTypes = { params: PropTypes.shape({ max: PropTypes.number.isRequired, min: PropTypes.number.isRequired, - palette: PropTypes.string.isRequired, + palette: PropTypes.array.isRequired, }), rows: PropTypes.array, } diff --git a/src/components/edit/earthEngine/LegendPreview.js b/src/components/edit/earthEngine/LegendPreview.js index f84c04baa..6cfdf387c 100644 --- a/src/components/edit/earthEngine/LegendPreview.js +++ b/src/components/edit/earthEngine/LegendPreview.js @@ -31,7 +31,7 @@ LegendPreview.propTypes = { params: PropTypes.shape({ max: PropTypes.number.isRequired, min: PropTypes.number.isRequired, - palette: PropTypes.string.isRequired, + palette: PropTypes.array.isRequired, }), } diff --git a/src/components/edit/earthEngine/StyleSelect.js b/src/components/edit/earthEngine/StyleSelect.js index 383f959ad..310e49df4 100644 --- a/src/components/edit/earthEngine/StyleSelect.js +++ b/src/components/edit/earthEngine/StyleSelect.js @@ -12,7 +12,7 @@ const maxSteps = 9 const StyleSelect = ({ unit, params, setParams }) => { const { min, max, palette } = params - const [steps, setSteps] = useState(palette.split(',').length) + const [steps, setSteps] = useState(palette.length) const onStepsChange = useCallback( (steps) => { @@ -95,7 +95,7 @@ StyleSelect.propTypes = { params: PropTypes.shape({ max: PropTypes.number.isRequired, min: PropTypes.number.isRequired, - palette: PropTypes.string.isRequired, + palette: PropTypes.array.isRequired, }), } diff --git a/src/components/edit/earthEngine/StyleTab.js b/src/components/edit/earthEngine/StyleTab.js index c065162e8..b48328b6d 100644 --- a/src/components/edit/earthEngine/StyleTab.js +++ b/src/components/edit/earthEngine/StyleTab.js @@ -25,7 +25,7 @@ StyleTab.propTypes = { params: PropTypes.shape({ max: PropTypes.number.isRequired, min: PropTypes.number.isRequired, - palette: PropTypes.string.isRequired, + palette: PropTypes.array.isRequired, }), } diff --git a/src/constants/earthEngine.js b/src/constants/earthEngine.js index 2c3d7d099..94880cd81 100644 --- a/src/constants/earthEngine.js +++ b/src/constants/earthEngine.js @@ -31,7 +31,14 @@ export const earthEngineLayers = () => [ params: { min: 0, max: 25, - palette: '#fee5d9,#fcbba1,#fc9272,#fb6a4a,#de2d26,#a50f15', // Reds + palette: [ + '#fee5d9', + '#fcbba1', + '#fc9272', + '#fb6a4a', + '#de2d26', + '#a50f15', + ], // Reds }, opacity: 0.9, }, @@ -209,7 +216,14 @@ export const earthEngineLayers = () => [ params: { min: 0, max: 10, - palette: '#fee5d9,#fcbba1,#fc9272,#fb6a4a,#de2d26,#a50f15', // Reds + palette: [ + '#fee5d9', + '#fcbba1', + '#fc9272', + '#fb6a4a', + '#de2d26', + '#a50f15', + ], // Reds }, opacity: 0.9, tileScale: 4, @@ -254,7 +268,14 @@ export const earthEngineLayers = () => [ params: { min: 0, max: 1500, - palette: '#ffffd4,#fee391,#fec44f,#fe9929,#d95f0e,#993404', // YlOrBr + palette: [ + '#ffffd4', + '#fee391', + '#fec44f', + '#fe9929', + '#d95f0e', + '#993404', + ], // YlOrBr }, opacity: 0.9, }, @@ -279,7 +300,14 @@ export const earthEngineLayers = () => [ params: { min: 0, max: 100, - palette: '#eff3ff,#c6dbef,#9ecae1,#6baed6,#3182bd,#08519c', // Blues + palette: [ + '#eff3ff', + '#c6dbef', + '#9ecae1', + '#6baed6', + '#3182bd', + '#08519c', + ], // Blues }, opacity: 0.9, }, @@ -309,8 +337,17 @@ export const earthEngineLayers = () => [ params: { min: 0, max: 40, - palette: - '#fff5f0,#fee0d2,#fcbba1,#fc9272,#fb6a4a,#ef3b2c,#cb181d,#a50f15,#67000d', // Reds + palette: [ + '#fff5f0', + '#fee0d2', + '#fcbba1', + '#fc9272', + '#fb6a4a', + '#ef3b2c', + '#cb181d', + '#a50f15', + '#67000d', + ], // Reds }, opacity: 0.9, }, @@ -450,7 +487,14 @@ export const earthEngineLayers = () => [ params: { min: 0, max: 10, - palette: '#fee5d9,#fcbba1,#fc9272,#fb6a4a,#de2d26,#a50f15', // Reds + palette: [ + '#fee5d9', + '#fcbba1', + '#fc9272', + '#fb6a4a', + '#de2d26', + '#a50f15', + ], // Reds }, opacity: 0.9, }, @@ -483,7 +527,14 @@ export const earthEngineLayers = () => [ params: { min: 0, max: 1000, - palette: '#fee5d9,#fcbba1,#fc9272,#fb6a4a,#de2d26,#a50f15', // Reds + palette: [ + '#fee5d9', + '#fcbba1', + '#fc9272', + '#fb6a4a', + '#de2d26', + '#a50f15', + ], // Reds }, methods: { multiply: [100], // Convert from people/hectare to people/km2 @@ -510,7 +561,15 @@ export const earthEngineLayers = () => [ params: { min: 0, max: 63, - palette: '#ffffd4,#fee391,#fec44f,#fe9929,#ec7014,#cc4c02,#8c2d04', // YlOrBr + palette: [ + '#ffffd4', + '#fee391', + '#fec44f', + '#fe9929', + '#ec7014', + '#cc4c02', + '#8c2d04', + ], // YlOrBr }, opacity: 0.9, }, diff --git a/src/loaders/earthEngineLoader.js b/src/loaders/earthEngineLoader.js index b72677a23..13d456e54 100644 --- a/src/loaders/earthEngineLoader.js +++ b/src/loaders/earthEngineLoader.js @@ -108,6 +108,11 @@ const earthEngineLoader = async (config) => { } } + // Backward compability for layers saved before 2.40 + if (typeof layerConfig.params?.palette === 'string') { + layerConfig.params.palette = layerConfig.params.palette.split(',') + } + dataset = getEarthEngineLayer(layerConfig.id) if (dataset) { @@ -168,15 +173,14 @@ const earthEngineLoader = async (config) => { } export const createLegend = ({ min, max, palette }) => { - const colors = palette.split(',') - const step = (max - min) / (colors.length - (min > 0 ? 2 : 1)) + const step = (max - min) / (palette.length - (min > 0 ? 2 : 1)) const precision = precisionRound(step, max) const valueFormat = numberPrecision(precision) let from = min let to = valueFormat(min + step) - return colors.map((color, index) => { + return palette.map((color, index) => { const item = { color } if (index === 0 && min > 0) { diff --git a/src/reducers/layerEdit.js b/src/reducers/layerEdit.js index f40823e43..148c763fa 100644 --- a/src/reducers/layerEdit.js +++ b/src/reducers/layerEdit.js @@ -318,7 +318,7 @@ const layerEdit = (state = null, action) => { newState = { ...state, colorScale: action.colorScale, - classes: action.colorScale.split(',').length, + classes: action.colorScale.length, } if (newState.styleDataItem) { diff --git a/src/util/__tests__/getMigratedMapConfig.spec.js b/src/util/__tests__/getMigratedMapConfig.spec.js index 09526c407..7b078a832 100644 --- a/src/util/__tests__/getMigratedMapConfig.spec.js +++ b/src/util/__tests__/getMigratedMapConfig.spec.js @@ -172,3 +172,40 @@ test('getMigratedMapConfig with old GIS app format and Boundary layer', () => { }) ) }) + +test('getMigratedMapConfig with colorScale converted to an array', () => { + const config = { + id: 'mapId', + name: 'map name', + basemap: { id: 'osmStreet' }, + mapViews: [ + { + layer: 'thematic', + name: 'Thematic layer', + colorScale: '#fee5d9,#fcbba1,#fc9272,#fb6a4a,#de2d26,#a50f15', + }, + ], + } + + expect(getMigratedMapConfig(config, defaultBasemapId)).toEqual( + expect.objectContaining({ + id: 'mapId', + name: 'map name', + basemap: { id: 'osmStreet' }, + mapViews: [ + { + layer: 'thematic', + name: 'Thematic layer', + colorScale: [ + '#fee5d9', + '#fcbba1', + '#fc9272', + '#fb6a4a', + '#de2d26', + '#a50f15', + ], + }, + ], + }) + ) +}) diff --git a/src/util/colors.js b/src/util/colors.js index 37ad87c3d..f52e38a20 100644 --- a/src/util/colors.js +++ b/src/util/colors.js @@ -38,17 +38,15 @@ export const colorScales = [ ] // Returns a color brewer scale for a number of classes -export const getColorPalette = (scale, classes) => { - return colorbrewer[scale][classes].join(',') -} +export const getColorPalette = (scale, classes) => colorbrewer[scale][classes] // Returns color scale name for a palette -export const getColorScale = (palette) => { - const classes = palette.split(',').length - return colorScales.find( - (name) => colorbrewer[name][classes].join(',') === palette +// join(',') is used to compare two arrays of colors +export const getColorScale = (palette) => + colorScales.find( + (name) => + colorbrewer[name][palette.length].join(',') === palette.join(',') ) -} export const defaultColorScaleName = 'YlOrBr' export const defaultClasses = 5 diff --git a/src/util/favorites.js b/src/util/favorites.js index 2c6b1b231..9d8f0020c 100644 --- a/src/util/favorites.js +++ b/src/util/favorites.js @@ -202,6 +202,11 @@ const models2objects = (config) => { } } + // Color scale needs to be stored as a string in analytical object + if (Array.isArray(config.colorScale)) { + config.colorScale = config.colorScale.join(',') + } + return config } diff --git a/src/util/getMigratedMapConfig.js b/src/util/getMigratedMapConfig.js index 312544941..e970c0fbb 100644 --- a/src/util/getMigratedMapConfig.js +++ b/src/util/getMigratedMapConfig.js @@ -2,7 +2,7 @@ import { isString, isObject, sortBy } from 'lodash/fp' import { EXTERNAL_LAYER } from '../constants/layers.js' export const getMigratedMapConfig = (config, defaultBasemapId) => - renameBoundaryLayerToOrgUnitLayer( + upgradeMapViews( upgradeGisAppLayers(extractBasemap(config, defaultBasemapId)) ) @@ -79,15 +79,27 @@ const upgradeGisAppLayers = (config) => { } // Change layer name from boundary to orgUnit when loading an old map +// Change colorScale from string to array // TODO: Change in db with an upgrade script -const renameBoundaryLayerToOrgUnitLayer = (config) => ({ - ...config, - mapViews: config.mapViews.map((v) => - v.layer === 'boundary' - ? { - ...v, - layer: 'orgUnit', - } - : v - ), -}) +const upgradeMapViews = (config) => { + if ( + config.mapViews.find( + (view) => + view.layer === 'boundary' || typeof view.colorScale === 'string' + ) + ) { + return { + ...config, + mapViews: config.mapViews.map((view) => ({ + ...view, + layer: view.layer === 'boundary' ? 'orgUnit' : view.layer, + colorScale: + typeof view.colorScale === 'string' + ? view.colorScale.split(',') + : view.colorScale, + })), + } + } else { + return config + } +} diff --git a/src/util/legend.js b/src/util/legend.js index 9048f8572..fd1e49c33 100644 --- a/src/util/legend.js +++ b/src/util/legend.js @@ -73,11 +73,10 @@ export const getAutomaticLegendItems = ( colorScale = defaultColorScale ) => { const items = data.length ? getLegendItems(data, method, classes) : [] - const colors = colorScale.split(',') return items.map((item, index) => ({ ...item, - color: colors[index], + color: colorScale[index], })) } /* eslint-enable max-params */