Skip to content

Commit

Permalink
PSU Insights (#4147)
Browse files Browse the repository at this point in the history
- closes SFMS: Create New UI #4124
- Adds new PSU Insights page
   - Contains simple map with 2km fuel grid pmtiles
- Adds Tool Card for the new page (only in dev for now)
  • Loading branch information
brettedw authored Dec 4, 2024
1 parent 8235d3c commit 0ee473d
Show file tree
Hide file tree
Showing 11 changed files with 205 additions and 4 deletions.
1 change: 1 addition & 0 deletions .github/workflows/pr_description.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module.exports = async ({ github, context }) => {
body += `[FireBat bookmark](${prBaseUrl}/fire-behaviour-calculator?s=266&f=c5&c=NaN&w=20,s=286&f=c7&c=NaN&w=16,s=1055&f=c7&c=NaN&w=NaN,s=305&f=c7&c=NaN&w=NaN,s=344&f=c5&c=NaN&w=NaN,s=346&f=c7&c=NaN&w=NaN,s=328&f=c7&c=NaN&w=NaN,s=1399&f=c7&c=NaN&w=NaN,s=334&f=c7&c=NaN&w=NaN,s=1082&f=c3&c=NaN&w=NaN,s=388&f=c7&c=NaN&w=NaN,s=309&f=c7&c=NaN&w=16,s=306&f=c7&c=NaN&w=NaN,s=1029&f=c7&c=NaN&w=NaN,s=298&f=c7&c=NaN&w=NaN,s=836&f=c7&c=NaN&w=NaN,s=9999&f=c7&c=NaN&w=NaN)\n`;
body += `[Auto Spatial Advisory (ASA)](${prBaseUrl}/auto-spatial-advisory)\n`;
body += `[HFI Calculator](${prBaseUrl}/hfi-calculator)\n`;
body += `[PSU Insights](${prBaseUrl}/insights)\n`;
github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
Expand Down
12 changes: 11 additions & 1 deletion web/src/app/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ import {
FIRE_BEHAVIOR_CALC_ROUTE,
FIRE_BEHAVIOUR_ADVISORY_ROUTE,
LANDING_PAGE_ROUTE,
MORE_CAST_2_ROUTE
MORE_CAST_2_ROUTE,
PSU_INSIGHTS_ROUTE
} from 'utils/constants'
import { NoMatchPage } from 'features/NoMatchPage'
const FireBehaviourCalculator = lazy(() => import('features/fbaCalculator/pages/FireBehaviourCalculatorPage'))
const FireBehaviourAdvisoryPage = lazy(() => import('features/fba/pages/FireBehaviourAdvisoryPage'))
const LandingPage = lazy(() => import('features/landingPage/pages/LandingPage'))
const MoreCast2Page = lazy(() => import('features/moreCast2/pages/MoreCast2Page'))
import LoadingBackdrop from 'features/hfiCalculator/components/LoadingBackdrop'
import { PSUInsightsPage } from '@/features/psuInsights/pages/PSUInsightsPage'

const shouldShowDisclaimer = HIDE_DISCLAIMER === 'false' || HIDE_DISCLAIMER === undefined

Expand Down Expand Up @@ -89,6 +91,14 @@ const WPSRoutes: React.FunctionComponent = () => {
</AuthWrapper>
}
/>
<Route
path={PSU_INSIGHTS_ROUTE}
element={
<AuthWrapper>
<PSUInsightsPage />
</AuthWrapper>
}
/>
<Route path="*" element={<NoMatchPage />} />
</Routes>
</Suspense>
Expand Down
2 changes: 1 addition & 1 deletion web/src/features/fba/components/viz/color.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// A Map of fuel type codes to the colour typically used in BCWS
const colorByFuelTypeCode = new Map()
export 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)')
Expand Down
2 changes: 1 addition & 1 deletion web/src/features/landingPage/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const Root = styled('div', {
name: `${PREFIX}-links`
})({
backgroundColor: theme.palette.primary.main,
minHeight: '80px'
minHeight: '30px'
})

const FooterLinks = styled('div', {
Expand Down
20 changes: 19 additions & 1 deletion web/src/features/landingPage/toolInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import CalculateOutlinedIcon from '@mui/icons-material/CalculateOutlined'
import LocalFireDepartmentIcon from '@mui/icons-material/LocalFireDepartment'
import PercentIcon from '@mui/icons-material/Percent'
import PublicIcon from '@mui/icons-material/Public'
import InsightsIcon from '@mui/icons-material/Insights'
import WhatshotOutlinedIcon from '@mui/icons-material/WhatshotOutlined'
import Link from '@mui/material/Link'
import Typography from '@mui/material/Typography'
Expand All @@ -22,7 +23,9 @@ import {
PERCENTILE_CALC_NAME,
PERCENTILE_CALC_ROUTE,
MORE_CAST_NAME,
MORECAST_ROUTE
MORECAST_ROUTE,
PSU_INSIGHTS_NAME,
PSU_INSIGHTS_ROUTE
} from 'utils/constants'

const ICON_FONT_SIZE = 'large'
Expand Down Expand Up @@ -132,11 +135,26 @@ export const fbpGoInfo: ToolInfo = {
isBeta: false
}

export const psuInsightsInfo: ToolInfo = {
name: PSU_INSIGHTS_NAME,
route: PSU_INSIGHTS_ROUTE,
description: (
<Typography>
A visualization tool providing an interactive map-based interface to analyze and understand critical
wildfire-related data. The tool offers a comprehensive view of key datasets, allowing users to visualize and
explore valuable information.
</Typography>
),
icon: <InsightsIcon color="primary" fontSize={ICON_FONT_SIZE} />,
isBeta: true
}

// The order of items in this array determines the order of items as they appear in the landing page
// side bar and order of CardTravelSharp.
export const toolInfos = [
moreCastInfo,
fireBehaviourAdvisoryInfo,
...(import.meta.env.MODE === 'development' ? [psuInsightsInfo] : []),
cHainesInfo,
fireBehaviourCalcInfo,
hfiCalcInfo,
Expand Down
76 changes: 76 additions & 0 deletions web/src/features/psuInsights/components/map/PSUMap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { BC_EXTENT, CENTER_OF_BC } from '@/utils/constants'
import { Map, View } from 'ol'
import { PMTilesVectorSource } from 'ol-pmtiles'
import { defaults as defaultControls } from 'ol/control'
import { boundingExtent } from 'ol/extent'
import 'ol/ol.css'
import { fromLonLat } from 'ol/proj'
import { PMTILES_BUCKET } from 'utils/env'

import React, { useEffect, useRef, useState } from 'react'

import { styleFuelGrid } from '@/features/psuInsights/components/map/psuFeatureStylers'
import { Box } from '@mui/material'
import { ErrorBoundary } from '@sentry/react'
import { source as baseMapSource } from 'features/fireWeather/components/maps/constants'
import TileLayer from 'ol/layer/Tile'
import VectorTileLayer from 'ol/layer/VectorTile'

const MapContext = React.createContext<Map | null>(null)

const bcExtent = boundingExtent(BC_EXTENT.map(coord => fromLonLat(coord)))

const PSUMap = () => {
const [map, setMap] = useState<Map | null>(null)
const mapRef = useRef<HTMLDivElement | null>(null) as React.MutableRefObject<HTMLElement>

const fuelGridVectorSource = new PMTilesVectorSource({
url: `${PMTILES_BUCKET}fuel/fbp2024.pmtiles`
})

const [fuelGridVTL] = useState(
new VectorTileLayer({
source: fuelGridVectorSource,
style: styleFuelGrid(),
zIndex: 51,
opacity: 0.6
})
)

useEffect(() => {
if (!mapRef.current) return

const mapObject = new Map({
target: mapRef.current,
layers: [new TileLayer({ source: baseMapSource }), fuelGridVTL],
controls: defaultControls(),
view: new View({
zoom: 5,
center: fromLonLat(CENTER_OF_BC)
})
})
mapObject.getView().fit(bcExtent, { padding: [50, 50, 50, 50] })
setMap(mapObject)

return () => {
mapObject.setTarget('')
}
}, [])

return (
<ErrorBoundary>
<MapContext.Provider value={map}>
<Box
ref={mapRef}
data-testid={'psu-map'}
sx={{
width: '100%',
height: '100%'
}}
></Box>
</MapContext.Provider>
</ErrorBoundary>
)
}

export default PSUMap
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { getColorForRasterValue } from '@/features/psuInsights/components/map/psuFeatureStylers'

describe('getColorForRasterValue', () => {
it('should get the correct colour for the specified raster value', () => {
const rasterValue = 1
const colour = getColorForRasterValue(rasterValue)
expect(colour).toBe('rgb(209, 255, 115)')
})
it('should return a transparent colour if no colour is found', () => {
const rasterValue = 1000
const colour = getColorForRasterValue(rasterValue)
expect(colour).toBe('rgba(0, 0, 0, 0)')
})
})
40 changes: 40 additions & 0 deletions web/src/features/psuInsights/components/map/psuFeatureStylers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { colorByFuelTypeCode } from '@/features/fba/components/viz/color'
import * as ol from 'ol'
import Geometry from 'ol/geom/Geometry'
import RenderFeature from 'ol/render/Feature'
import Fill from 'ol/style/Fill'
import Style from 'ol/style/Style'

const rasterValueToFuelTypeCode = new Map([
[1, 'C-1'],
[2, 'C-2'],
[3, 'C-3'],
[4, 'C-4'],
[5, 'C-5'],
[6, 'C-6'],
[7, 'C-7'],
[8, 'D-1/D-2'],
[9, 'S-1'],
[10, 'S-2'],
[11, 'S-3'],
[12, 'O-1a/O-1b'],
[13, 'M-3'],
[14, 'M-1/M-2']
])

export const getColorForRasterValue = (rasterValue: number): string => {
const fuelTypeCode = rasterValueToFuelTypeCode.get(rasterValue)
return fuelTypeCode ? colorByFuelTypeCode.get(fuelTypeCode) : 'rgba(0, 0, 0, 0)'
}

export const styleFuelGrid = () => {
const style = (feature: RenderFeature | ol.Feature<Geometry>) => {
const fuelTypeInt = feature.getProperties().fuel
const fillColour = getColorForRasterValue(fuelTypeInt)

return new Style({
fill: new Fill({ color: fillColour })
})
}
return style
}
22 changes: 22 additions & 0 deletions web/src/features/psuInsights/components/map/psuMap.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import PSUMap from '@/features/psuInsights/components/map/PSUMap'
import { render } from '@testing-library/react'

describe('PSUMap', () => {
it('should render the map', () => {
class ResizeObserver {
observe() {
// mock no-op
}
unobserve() {
// mock no-op
}
disconnect() {
// mock no-op
}
}
window.ResizeObserver = ResizeObserver
const { getByTestId } = render(<PSUMap />)
const map = getByTestId('psu-map')
expect(map).toBeVisible()
})
})
17 changes: 17 additions & 0 deletions web/src/features/psuInsights/pages/PSUInsightsPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { GeneralHeader } from '@/components/GeneralHeader'
import Footer from '@/features/landingPage/components/Footer'
import PSUMap from '@/features/psuInsights/components/map/PSUMap'
import { PSU_INSIGHTS_NAME } from '@/utils/constants'
import Box from '@mui/material/Box'

export const PSUInsightsPage = () => {
return (
<Box sx={{ height: '100vh', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
<GeneralHeader isBeta={true} spacing={1} title={PSU_INSIGHTS_NAME} productName={PSU_INSIGHTS_NAME} />
<Box sx={{ flex: 1, position: 'relative' }}>
<PSUMap />
</Box>
<Footer />
</Box>
)
}
3 changes: 3 additions & 0 deletions web/src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const FBP_GO_ROUTE = 'https://psu.nrs.gov.bc.ca/fbp-go'
export const FIRE_BEHAVIOR_CALC_ROUTE = '/fire-behaviour-calculator'
export const FIRE_BEHAVIOUR_ADVISORY_ROUTE = '/auto-spatial-advisory'
export const MORE_CAST_2_ROUTE = '/morecast-2'
export const PSU_INSIGHTS_ROUTE = '/insights'
export const LANDING_PAGE_ROUTE = '/'

// ExpandableContainer widths
Expand All @@ -45,6 +46,7 @@ export const FIRE_BEHAVIOUR_CALC_NAME = 'FireBat'
export const HFI_CALC_NAME = 'HFI Calculator'
export const MORE_CAST_NAME = 'MoreCast'
export const PERCENTILE_CALC_NAME = 'Percentile Calculator'
export const PSU_INSIGHTS_NAME = 'PSU Insights'

// UI constants
export const HEADER_HEIGHT = 56
Expand All @@ -58,6 +60,7 @@ export const FIREBAT_DOC_TITLE = 'FireBat | BCWS PSU'
export const HFI_CALC_DOC_TITLE = 'HFI Calculator | BCWS PSU'
export const MORE_CAST_DOC_TITLE = 'MoreCast | BCWS PSU'
export const PERCENTILE_CALC_DOC_TITLE = 'Percentile Calculator | BCWS PSU'
export const PSU_INSIGHTS_DOC_TITLE = 'PSU Insights | BCWS PSU'

export enum FireCentres {
CARIBOO_FC = 'Cariboo Fire Centre',
Expand Down

0 comments on commit 0ee473d

Please sign in to comment.