From cf2451955ff2f1003e2b25fa97dab8393e8aeff6 Mon Sep 17 00:00:00 2001 From: dgboss Date: Tue, 7 Nov 2023 13:38:09 -0800 Subject: [PATCH] ASA: Color coded fuel types (#3220) --- .../fba/components/viz/FuelTypesBreakdown.tsx | 63 +++++++++---------- .../features/fba/components/viz/color.test.ts | 28 +++++++++ web/src/features/fba/components/viz/color.ts | 22 +++++++ 3 files changed, 80 insertions(+), 33 deletions(-) create mode 100644 web/src/features/fba/components/viz/color.test.ts create mode 100644 web/src/features/fba/components/viz/color.ts diff --git a/web/src/features/fba/components/viz/FuelTypesBreakdown.tsx b/web/src/features/fba/components/viz/FuelTypesBreakdown.tsx index 2798398d6..edbd1c414 100644 --- a/web/src/features/fba/components/viz/FuelTypesBreakdown.tsx +++ b/web/src/features/fba/components/viz/FuelTypesBreakdown.tsx @@ -4,6 +4,7 @@ import { Typography } from '@mui/material' import { isUndefined } from 'lodash' import { FireShape, FireZoneThresholdFuelTypeArea } from 'api/fbaAPI' import { PieChart, Pie, ResponsiveContainer, Cell } from 'recharts' +import { getColorByFuelTypeCode } from 'features/fba/components/viz/color' const PREFIX = 'FuelTypesBreakdown' @@ -35,50 +36,30 @@ interface FuelTypeDataForPieChart { } const RADIAN = Math.PI / 180 -const COLOURS = [ - '#2191FB', - '#FCB1A6', - '#B33951', - '#CCF5AC', - '#8CDEDC', - '#9DACFF', - '#4F7CAC', - '#FFA62B', - '#C09BD8', - '#EBC3DB', - '#D19C1D', - '#FFC0BE', - '#ED7D3A' -] const FuelTypesBreakdown = (props: Props) => { const renderCustomizedLabel = ({ cx, cy, midAngle, - innerRadius, outerRadius, percent, - fuel_type_code, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - area, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - index + fuel_type_code }: { cx: number cy: number midAngle: number - innerRadius: number outerRadius: number percent: number fuel_type_code: string - area: number - index: number }) => { - const radius = innerRadius + (outerRadius - innerRadius) * 0.5 - const x = cx + radius * Math.cos(-midAngle * RADIAN) - const y = cy + radius * Math.sin(-midAngle * RADIAN) + // Labels are positioned at the outer edge of the pie + the length of label lines (20px) + + // an arbitrary buffer/whitespace of 5px + const labelRadius = outerRadius + 25 + const x = cx + labelRadius * Math.cos(-midAngle * RADIAN) + const y = cy + labelRadius * Math.sin(-midAngle * RADIAN) + // Only label pie slices that contribute >= 2% if (percent * 100 < 2) { return } @@ -90,6 +71,22 @@ const FuelTypesBreakdown = (props: Props) => { ) } + const renderLabelLine = ({ + percent, + points, + stroke + }: { + percent: number + points: { x: number; y: number }[] + stroke: string + }) => { + if (!points || points.length < 2 || percent * 100 < 2) { + return <> + } + + return + } + if (isUndefined(props.selectedFireZone) || isUndefined(props.fuelTypeInfo[props.selectedFireZone.fire_shape_id])) { return
} else { @@ -116,11 +113,11 @@ const FuelTypesBreakdown = (props: Props) => { cy="50%" outerRadius={80} fill="#8884d8" - labelLine={false} + labelLine={renderLabelLine} label={renderCustomizedLabel} > - {advisories.map((entry, index) => ( - + {advisories.map(entry => ( + ))} @@ -135,11 +132,11 @@ const FuelTypesBreakdown = (props: Props) => { cx="50%" cy="50%" outerRadius={80} - labelLine={false} + labelLine={renderLabelLine} label={renderCustomizedLabel} > - {warnings.map((entry, index) => ( - + {warnings.map(entry => ( + ))} diff --git a/web/src/features/fba/components/viz/color.test.ts b/web/src/features/fba/components/viz/color.test.ts new file mode 100644 index 000000000..f587b70da --- /dev/null +++ b/web/src/features/fba/components/viz/color.test.ts @@ -0,0 +1,28 @@ +import { getColorByFuelTypeCode } from 'features/fba/components/viz/color' + +const colorByFuelTypeCodeTestMap = new Map() +colorByFuelTypeCodeTestMap.set('C-1', 'rgb(209, 255, 115)') +colorByFuelTypeCodeTestMap.set('C-2', 'rgb(34, 102, 51)') +colorByFuelTypeCodeTestMap.set('C-3', 'rgb(131, 199, 149)') +colorByFuelTypeCodeTestMap.set('C-4', 'rgb(112, 168, 0)') +colorByFuelTypeCodeTestMap.set('C-5', 'rgb(223, 184, 230)') +colorByFuelTypeCodeTestMap.set('C-6', 'rgb(172, 102, 237)') +colorByFuelTypeCodeTestMap.set('C-7', 'rgb(112, 12, 242)') +colorByFuelTypeCodeTestMap.set('D-1/D-2', 'rgb(137, 112, 68)') +colorByFuelTypeCodeTestMap.set('S-1', 'rgb(251, 190, 185)') +colorByFuelTypeCodeTestMap.set('S-2', 'rgb(247, 104, 161)') +colorByFuelTypeCodeTestMap.set('S-3', 'rgb(174, 1, 126)') +colorByFuelTypeCodeTestMap.set('O-1a/O-1b', 'rgb(255, 255, 190)') +colorByFuelTypeCodeTestMap.set('M-1/M-2', 'rgb(255, 211, 127)') + +describe('getColorByFuelTypeCode', () => { + it('should return the correct colour for each fuel type code', () => { + const keys = colorByFuelTypeCodeTestMap.keys() + for (const key of keys) { + expect(getColorByFuelTypeCode(key)).toBe(colorByFuelTypeCodeTestMap.get(key)) + } + }) + it('should return a default color when a matching key is not found', () => { + expect(getColorByFuelTypeCode('foo')).toBe('rgb(0, 255, 255)') + }) +}) diff --git a/web/src/features/fba/components/viz/color.ts b/web/src/features/fba/components/viz/color.ts new file mode 100644 index 000000000..e20ffdf32 --- /dev/null +++ b/web/src/features/fba/components/viz/color.ts @@ -0,0 +1,22 @@ +// A Map of fuel type codes to the colour typically used in BCWS +const colorByFuelTypeCode = new Map() +colorByFuelTypeCode.set('C-1', 'rgb(209, 255, 115)') +colorByFuelTypeCode.set('C-2', 'rgb(34, 102, 51)') +colorByFuelTypeCode.set('C-3', 'rgb(131, 199, 149)') +colorByFuelTypeCode.set('C-4', 'rgb(112, 168, 0)') +colorByFuelTypeCode.set('C-5', 'rgb(223, 184, 230)') +colorByFuelTypeCode.set('C-6', 'rgb(172, 102, 237)') +colorByFuelTypeCode.set('C-7', 'rgb(112, 12, 242)') +colorByFuelTypeCode.set('D-1/D-2', 'rgb(137, 112, 68)') +colorByFuelTypeCode.set('S-1', 'rgb(251, 190, 185)') +colorByFuelTypeCode.set('S-2', 'rgb(247, 104, 161)') +colorByFuelTypeCode.set('S-3', 'rgb(174, 1, 126)') +colorByFuelTypeCode.set('O-1a/O-1b', 'rgb(255, 255, 190)') +colorByFuelTypeCode.set('M-1/M-2', 'rgb(255, 211, 127)') + +// Retrieve a color from the Map base don the fuel type code. +// Provide a default value in case a non-standard code is encountered so we don't end up with empty pie slices. +export const getColorByFuelTypeCode = (code: string): string => { + const color = colorByFuelTypeCode.get(code) + return color ?? 'rgb(0, 255, 255)' +}