Skip to content

Commit 27024dd

Browse files
authored
Merge pull request #302 from carbonplan/katamartin/fresh/oae
Add OAE efficiency
2 parents 8ca30fb + 6a5e6b4 commit 27024dd

21 files changed

+3118
-0
lines changed

articles/oae-efficiency-explainer/components/alk-chem-diagram.js

Lines changed: 511 additions & 0 deletions
Large diffs are not rendered by default.

articles/oae-efficiency-explainer/components/carb-chem-diagram.js

Lines changed: 472 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Box } from 'theme-ui'
2+
3+
export const HCO3 = () => {
4+
return (
5+
<>
6+
HCO₃
7+
<Box as='span' sx={{ ml: '-5px' }}>
8+
9+
</Box>
10+
</>
11+
)
12+
}
13+
14+
export const CO3 = () => {
15+
return (
16+
<>
17+
CO₃
18+
<Box as='span' sx={{ ml: '-6px' }}>
19+
²⁻
20+
</Box>
21+
</>
22+
)
23+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { createContext, useContext, useEffect, useState } from 'react'
2+
import { feature } from 'topojson-client'
3+
import zarr from 'zarr-js'
4+
5+
const DataContext = createContext({})
6+
7+
export const DataWrapper = ({ children }) => {
8+
const [efficiencies, setEfficiencies] = useState({})
9+
const [regions, setRegions] = useState()
10+
const [loader, setLoader] = useState(null)
11+
12+
useEffect(() => {
13+
zarr().open(
14+
'https://carbonplan-oae-efficiency.s3.us-west-2.amazonaws.com/v2/store1b_rechunked.zarr/OAE_efficiency',
15+
(err, get) => {
16+
setLoader(() => get)
17+
}
18+
)
19+
}, [])
20+
21+
return (
22+
<DataContext.Provider
23+
value={{ efficiencies, setEfficiencies, regions, setRegions, loader }}
24+
>
25+
{children}
26+
</DataContext.Provider>
27+
)
28+
}
29+
30+
export const useEfficiency = (injectionMonth) => {
31+
const { efficiencies, loader, setEfficiencies } = useContext(DataContext)
32+
33+
useEffect(() => {
34+
if (!efficiencies[injectionMonth] && loader) {
35+
setEfficiencies((prev) => ({ ...prev, [injectionMonth]: 'loading' }))
36+
loader([0, 0, injectionMonth], (innerErr, array) => {
37+
setEfficiencies((prev) => ({ ...prev, [injectionMonth]: array }))
38+
})
39+
}
40+
}, [efficiencies, setEfficiencies, injectionMonth, loader])
41+
42+
return {
43+
efficiency:
44+
efficiencies[injectionMonth] === 'loading'
45+
? null
46+
: efficiencies[injectionMonth],
47+
}
48+
}
49+
50+
export const useRegions = () => {
51+
const { regions, setRegions } = useContext(DataContext)
52+
53+
useEffect(() => {
54+
if (!regions) {
55+
fetch(
56+
'https://carbonplan-oae-efficiency.s3.us-west-2.amazonaws.com/regions.topojson'
57+
)
58+
.then((response) => response.json())
59+
.then((topojsonData) => {
60+
const geojsonData = feature(
61+
topojsonData,
62+
topojsonData.objects.regions
63+
)
64+
setRegions(geojsonData)
65+
})
66+
}
67+
}, [regions, setRegions])
68+
69+
return { regions: regions === 'loading' ? null : regions }
70+
}
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import { Minimap, Path, useMinimap } from '@carbonplan/minimaps'
2+
import { naturalEarth1 } from '@carbonplan/minimaps/projections'
3+
import React, { useEffect, useState } from 'react'
4+
import { geoPath } from 'd3-geo'
5+
6+
import { Box, Flex, useThemeUI } from 'theme-ui'
7+
import { useThemedColormap } from '@carbonplan/colormaps'
8+
import { Colorbar, Column, Filter, Row } from '@carbonplan/components'
9+
import TimeSlider from './time-slider'
10+
import { useEfficiency, useRegions } from './data'
11+
12+
const getColor = (polygon_id, efficiencies, colormap) => {
13+
if (!efficiencies) {
14+
return null
15+
}
16+
17+
const index = Number(polygon_id)
18+
const efficiency = efficiencies.get(index)
19+
20+
const rgb = colormap[Math.round(efficiency * (colormap.length - 1))] ?? []
21+
22+
return `rgb(${rgb.join(',')})`
23+
}
24+
25+
const Regions = ({ colormap, injectionMonth, time }) => {
26+
const { projection } = useMinimap()
27+
const [regions, setRegions] = useState({})
28+
const [efficiencies, setEfficiencies] = useState()
29+
const { efficiency } = useEfficiency(injectionMonth)
30+
const { regions: regionsGeojson } = useRegions()
31+
32+
useEffect(() => {
33+
if (efficiency) {
34+
setEfficiencies(efficiency.pick(time, null, 0))
35+
}
36+
}, [efficiency, time])
37+
38+
useEffect(() => {
39+
if (regionsGeojson) {
40+
setRegions(
41+
regionsGeojson.features.reduce((a, f) => {
42+
const id = f.properties.polygon_id
43+
const path = geoPath(projection)(f)
44+
// append paths for multipart geometries
45+
if (a[id]) {
46+
a[id] = a[id] + ' ' + path
47+
} else {
48+
a[id] = path
49+
}
50+
return a
51+
}, {})
52+
)
53+
}
54+
}, [regionsGeojson, projection])
55+
56+
return (
57+
<>
58+
{Object.keys(regions).map((polygon_id) => (
59+
<path
60+
key={polygon_id}
61+
d={regions[polygon_id]}
62+
fill={getColor(polygon_id, efficiencies, colormap)}
63+
/>
64+
))}
65+
</>
66+
)
67+
}
68+
69+
const MONTH_LABELS = {
70+
Jan: 0,
71+
Apr: 1,
72+
Jul: 2,
73+
Oct: 3,
74+
}
75+
76+
const formatTime = (t) => {
77+
return `Y${Math.floor(t / 12) + 1} M${(t % 12) + 1}`
78+
}
79+
const EfficienciesMap = () => {
80+
const { theme } = useThemeUI()
81+
const [injectionMonth, setInjectionMonth] = useState(0)
82+
const [time, setTime] = useState(179)
83+
const colormap = useThemedColormap('cool')
84+
85+
return (
86+
<Box sx={{ width: '100%' }}>
87+
<Row columns={6}>
88+
<Column start={1} width={[6, 3, 3, 3]}>
89+
<Box
90+
as='label'
91+
sx={{
92+
fontFamily: 'heading',
93+
letterSpacing: 'smallcaps',
94+
textTransform: 'uppercase',
95+
fontSize: [2, 2, 2, 3],
96+
}}
97+
>
98+
Elapsed time
99+
</Box>
100+
101+
<Box sx={{ mt: 2 }} />
102+
<TimeSlider
103+
time={time}
104+
setTime={setTime}
105+
max={179}
106+
formatDate={formatTime}
107+
delay={500}
108+
pause='max'
109+
/>
110+
</Column>
111+
112+
<Column
113+
start={[1, 5, 5, 5]}
114+
width={[6, 2, 2, 2]}
115+
sx={{ mt: [3, 0, 0, 0] }}
116+
>
117+
<Box
118+
as='label'
119+
sx={{
120+
fontFamily: 'heading',
121+
letterSpacing: 'smallcaps',
122+
textTransform: 'uppercase',
123+
fontSize: [2, 2, 2, 3],
124+
}}
125+
>
126+
Injection month
127+
</Box>
128+
129+
<Filter
130+
values={{
131+
Jan: injectionMonth === MONTH_LABELS['Jan'],
132+
Apr: injectionMonth === MONTH_LABELS['Apr'],
133+
Jul: injectionMonth === MONTH_LABELS['Jul'],
134+
Oct: injectionMonth === MONTH_LABELS['Oct'],
135+
}}
136+
setValues={(obj) =>
137+
setInjectionMonth(
138+
MONTH_LABELS[Object.keys(obj).find((k) => obj[k])]
139+
)
140+
}
141+
sx={{ mt: 3 }}
142+
/>
143+
</Column>
144+
</Row>
145+
146+
<Box sx={{ mx: [-3, -3, -3, -5] }}>
147+
<Minimap projection={naturalEarth1} scale={1} translate={[0, 0]}>
148+
<Regions
149+
colormap={colormap}
150+
injectionMonth={injectionMonth}
151+
time={time}
152+
/>
153+
<Path
154+
stroke={theme.colors.primary}
155+
source={'https://cdn.jsdelivr.net/npm/world-atlas@2/land-50m.json'}
156+
feature={'land'}
157+
opacity={0.7}
158+
fill={theme.colors.background}
159+
/>
160+
</Minimap>
161+
</Box>
162+
<Flex sx={{ justifyContent: 'flex-end' }}>
163+
<Colorbar
164+
colormap={colormap}
165+
clim={[0, 1]}
166+
label='Efficiency'
167+
horizontal
168+
sx={{ flexShrink: 0 }}
169+
/>
170+
</Flex>
171+
</Box>
172+
)
173+
}
174+
175+
export default EfficienciesMap

0 commit comments

Comments
 (0)