Skip to content

Commit

Permalink
ASA: Color coded fuel types (bcgov#3220)
Browse files Browse the repository at this point in the history
  • Loading branch information
dgboss authored Nov 7, 2023
1 parent d997a0c commit cf24519
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 33 deletions.
63 changes: 30 additions & 33 deletions web/src/features/fba/components/viz/FuelTypesBreakdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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
}
Expand All @@ -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 <path d={`M${points[0].x},${points[0].y}L${points[1].x},${points[1].y}`} stroke={stroke} strokeWidth={1.5} />
}

if (isUndefined(props.selectedFireZone) || isUndefined(props.fuelTypeInfo[props.selectedFireZone.fire_shape_id])) {
return <div></div>
} else {
Expand All @@ -116,11 +113,11 @@ const FuelTypesBreakdown = (props: Props) => {
cy="50%"
outerRadius={80}
fill="#8884d8"
labelLine={false}
labelLine={renderLabelLine}
label={renderCustomizedLabel}
>
{advisories.map((entry, index) => (
<Cell key={`cell-${entry.fuel_type_code}`} fill={COLOURS[index % COLOURS.length]} />
{advisories.map(entry => (
<Cell key={`cell-${entry.fuel_type_code}`} fill={getColorByFuelTypeCode(entry.fuel_type_code)} />
))}
</Pie>
</PieChart>
Expand All @@ -135,11 +132,11 @@ const FuelTypesBreakdown = (props: Props) => {
cx="50%"
cy="50%"
outerRadius={80}
labelLine={false}
labelLine={renderLabelLine}
label={renderCustomizedLabel}
>
{warnings.map((entry, index) => (
<Cell key={`cell-${entry.fuel_type_code}`} fill={COLOURS[index % COLOURS.length]} />
{warnings.map(entry => (
<Cell key={`cell-${entry.fuel_type_code}`} fill={getColorByFuelTypeCode(entry.fuel_type_code)} />
))}
</Pie>
</PieChart>
Expand Down
28 changes: 28 additions & 0 deletions web/src/features/fba/components/viz/color.test.ts
Original file line number Diff line number Diff line change
@@ -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)')
})
})
22 changes: 22 additions & 0 deletions web/src/features/fba/components/viz/color.ts
Original file line number Diff line number Diff line change
@@ -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)'
}

0 comments on commit cf24519

Please sign in to comment.