Skip to content

Commit

Permalink
Merge pull request #294 from carbonplan/katamartin/oae-efficiency
Browse files Browse the repository at this point in the history
Add OAE efficiency explainer and tool
  • Loading branch information
Shane98c authored Jul 16, 2024
2 parents 74ad03b + 589f4f9 commit 63b18e9
Show file tree
Hide file tree
Showing 22 changed files with 3,144 additions and 21 deletions.
511 changes: 511 additions & 0 deletions articles/oae-efficiency-explainer/components/alk-chem-diagram.js

Large diffs are not rendered by default.

472 changes: 472 additions & 0 deletions articles/oae-efficiency-explainer/components/carb-chem-diagram.js

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions articles/oae-efficiency-explainer/components/chemistry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Box } from 'theme-ui'

export const HCO3 = () => {
return (
<>
HCO₃
<Box as='span' sx={{ ml: '-5px' }}>
</Box>
</>
)
}

export const CO3 = () => {
return (
<>
CO₃
<Box as='span' sx={{ ml: '-6px' }}>
²⁻
</Box>
</>
)
}
70 changes: 70 additions & 0 deletions articles/oae-efficiency-explainer/components/data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { createContext, useContext, useEffect, useState } from 'react'
import { feature } from 'topojson-client'
import zarr from 'zarr-js'

const DataContext = createContext({})

export const DataWrapper = ({ children }) => {
const [efficiencies, setEfficiencies] = useState({})
const [regions, setRegions] = useState()
const [loader, setLoader] = useState(null)

useEffect(() => {
zarr().open(
'https://carbonplan-oae-efficiency.s3.us-west-2.amazonaws.com/store1b.zarr/OAE_efficiency',
(err, get) => {
setLoader(() => get)
}
)
}, [])

return (
<DataContext.Provider
value={{ efficiencies, setEfficiencies, regions, setRegions, loader }}
>
{children}
</DataContext.Provider>
)
}

export const useEfficiency = (injectionMonth) => {
const { efficiencies, loader, setEfficiencies } = useContext(DataContext)

useEffect(() => {
if (!efficiencies[injectionMonth] && loader) {
setEfficiencies((prev) => ({ ...prev, [injectionMonth]: 'loading' }))
loader([0, 0, injectionMonth], (innerErr, array) => {
setEfficiencies((prev) => ({ ...prev, [injectionMonth]: array }))
})
}
}, [efficiencies, setEfficiencies, injectionMonth, loader])

return {
efficiency:
efficiencies[injectionMonth] === 'loading'
? null
: efficiencies[injectionMonth],
}
}

export const useRegions = () => {
const { regions, setRegions } = useContext(DataContext)

useEffect(() => {
if (!regions) {
fetch(
'https://carbonplan-oae-efficiency.s3.us-west-2.amazonaws.com/regions.topojson'
)
.then((response) => response.json())
.then((topojsonData) => {
const geojsonData = feature(
topojsonData,
topojsonData.objects.regions
)
setRegions(geojsonData)
})
}
}, [regions, setRegions])

return { regions: regions === 'loading' ? null : regions }
}
175 changes: 175 additions & 0 deletions articles/oae-efficiency-explainer/components/efficiencies-map.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import { Minimap, Path, useMinimap } from '@carbonplan/minimaps'
import { naturalEarth1 } from '@carbonplan/minimaps/projections'
import React, { useEffect, useState } from 'react'
import { geoPath } from 'd3-geo'

import { Box, Flex, useThemeUI } from 'theme-ui'
import { useThemedColormap } from '@carbonplan/colormaps'
import { Colorbar, Column, Filter, Row } from '@carbonplan/components'
import TimeSlider from './time-slider'
import { useEfficiency, useRegions } from './data'

const getColor = (polygon_id, efficiencies, colormap) => {
if (!efficiencies) {
return null
}

const index = Number(polygon_id)
const efficiency = efficiencies.get(index)

const rgb = colormap[Math.round(efficiency * (colormap.length - 1))] ?? []

return `rgb(${rgb.join(',')})`
}

const Regions = ({ colormap, injectionMonth, time }) => {
const { projection } = useMinimap()
const [regions, setRegions] = useState({})
const [efficiencies, setEfficiencies] = useState()
const { efficiency } = useEfficiency(injectionMonth)
const { regions: regionsGeojson } = useRegions()

useEffect(() => {
if (efficiency) {
setEfficiencies(efficiency.pick(time, null, 0))
}
}, [efficiency, time])

useEffect(() => {
if (regionsGeojson) {
setRegions(
regionsGeojson.features.reduce((a, f) => {
const id = f.properties.polygon_id
const path = geoPath(projection)(f)
// append paths for multipart geometries
if (a[id]) {
a[id] = a[id] + ' ' + path
} else {
a[id] = path
}
return a
}, {})
)
}
}, [regionsGeojson, projection])

return (
<>
{Object.keys(regions).map((polygon_id) => (
<path
key={polygon_id}
d={regions[polygon_id]}
fill={getColor(polygon_id, efficiencies, colormap)}
/>
))}
</>
)
}

const MONTH_LABELS = {
Jan: 0,
Apr: 1,
Jul: 2,
Oct: 3,
}

const formatTime = (t) => {
return `Y${Math.floor(t / 12) + 1} M${(t % 12) + 1}`
}
const EfficienciesMap = () => {
const { theme } = useThemeUI()
const [injectionMonth, setInjectionMonth] = useState(0)
const [time, setTime] = useState(179)
const colormap = useThemedColormap('cool')

return (
<Box sx={{ width: '100%' }}>
<Row columns={6}>
<Column start={1} width={[6, 3, 3, 3]}>
<Box
as='label'
sx={{
fontFamily: 'heading',
letterSpacing: 'smallcaps',
textTransform: 'uppercase',
fontSize: [2, 2, 2, 3],
}}
>
Elapsed time
</Box>

<Box sx={{ mt: 2 }} />
<TimeSlider
time={time}
setTime={setTime}
max={179}
formatDate={formatTime}
delay={500}
pause='max'
/>
</Column>

<Column
start={[1, 5, 5, 5]}
width={[6, 2, 2, 2]}
sx={{ mt: [3, 0, 0, 0] }}
>
<Box
as='label'
sx={{
fontFamily: 'heading',
letterSpacing: 'smallcaps',
textTransform: 'uppercase',
fontSize: [2, 2, 2, 3],
}}
>
Injection month
</Box>

<Filter
values={{
Jan: injectionMonth === MONTH_LABELS['Jan'],
Apr: injectionMonth === MONTH_LABELS['Apr'],
Jul: injectionMonth === MONTH_LABELS['Jul'],
Oct: injectionMonth === MONTH_LABELS['Oct'],
}}
setValues={(obj) =>
setInjectionMonth(
MONTH_LABELS[Object.keys(obj).find((k) => obj[k])]
)
}
sx={{ mt: 3 }}
/>
</Column>
</Row>

<Box sx={{ mx: [-3, -3, -3, -5] }}>
<Minimap projection={naturalEarth1} scale={1} translate={[0, 0]}>
<Regions
colormap={colormap}
injectionMonth={injectionMonth}
time={time}
/>
<Path
stroke={theme.colors.primary}
source={'https://cdn.jsdelivr.net/npm/world-atlas@2/land-50m.json'}
feature={'land'}
opacity={0.7}
fill={theme.colors.background}
/>
</Minimap>
</Box>
<Flex sx={{ justifyContent: 'flex-end' }}>
<Colorbar
colormap={colormap}
clim={[0, 1]}
label='Efficiency'
horizontal
sx={{ flexShrink: 0 }}
/>
</Flex>
</Box>
)
}

export default EfficienciesMap
Loading

0 comments on commit 63b18e9

Please sign in to comment.