Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions src/components/explore/landcover/Landcover.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import DataLoader from '../../shared/DataLoader.jsx'
import Chart from '../Chart.jsx'
import getChartConfig from './charts/landcover.js'
import getAllConfig from './charts/landcoverAll.js'
import getDynamicWorldConfig from './charts/dynamicWorld.js'
import LandcoverSelect from './LandcoverSelect.jsx'

const dataset = {
Expand All @@ -17,16 +18,50 @@ const period = {
endTime: '2024',
}

const dynamicWorld = {
datasetId: 'GOOGLE/DYNAMICWORLD/V1',
band: [
'water',
'trees',
'grass',
'flooded_vegetation',
'crops',
'shrub_and_scrub',
'built',
'bare',
'snow_and_ice',
], // 'label'
// reducer: 'frequencyHistogram',
// reducer: 'count',
// aggregationPeriod: MONTHLY,
}

const LandCover = () => {
const orgUnit = exploreStore((state) => state.orgUnit)
const type = exploreStore((state) => state.landcoverType)

const data = useEarthEngineTimeSeries(dataset, period, orgUnit)
// const dynamicWorldPeriod = exploreStore((state) => state.monthlyPeriod)
const dynamicWorldPeriod = {
startTime: '2024-01',
endTime: '2024-06',
}

// const data = useEarthEngineTimeSeries(dataset, period, orgUnit)
let data

if (!data) {
const dynamicWorldData = useEarthEngineTimeSeries({
dataset: dynamicWorld,
period: dynamicWorldPeriod,
feature: orgUnit,
})

// console.log('dynamicWorldData', dynamicWorldData, dynamicWorldPeriod)

if (!data && !dynamicWorldData) {
return <DataLoader />
}

/*
return (
<>
<Chart config={getAllConfig(orgUnit.properties.name, data)} />
Expand All @@ -36,6 +71,19 @@ const LandCover = () => {
/>
</>
)
*/

return (
<>
<Chart
config={getDynamicWorldConfig(
orgUnit.properties.name,
dynamicWorldData,
dynamicWorld.band
)}
/>
</>
)
}

export default LandCover
160 changes: 160 additions & 0 deletions src/components/explore/landcover/charts/dynamicWorld.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import i18n from '@dhis2/d2-i18n'
import { colors } from '@dhis2/ui'
import { roundOneDecimal } from '../../../../utils/calc.js'
import { animation, landCoverCredits } from '../../../../utils/chart.js'
import { landcoverTypes } from '../LandcoverSelect.jsx'

const band = 'LC_Type1'

const getChartConfig = (name, data, bands) => {
console.log(name, data, bands)

// const band = 'trees'

const filteredData = data.filter((d) => d['trees'] !== undefined)

const series = bands.map((band) => ({
type: 'line',
data: filteredData.map((d) => ({
x: d.startTime,
y: d[band],
})),
name: band,
color: colors.green600,
lineWidth: 3,
zIndex: 2,
}))

return {
/*
title: {
text: i18n.t('{{name}}: {{band}} vegetation index {{period}}', {
name,
band,
period: getDailyPeriod(data),
nsSeparator: ';',
}),
},
subtitle: isFacility && {
text: i18n.t(
'Value is only for 250 x 250 m where the facility is located'
),
},
*/
// credits: vegetationCredits,
tooltip: {
crosshairs: true,
shared: true,
valueSuffix: '°C',
},
xAxis: {
type: 'datetime',
tickInterval: 2592000000,
labels: {
format: '{value: %b}',
},
},
yAxis: {
title: false,
min: 0,
max: 1,
},
chart: {
height: 480,
marginBottom: 75,
},
plotOptions: {
series: {
animation,
},
/*
column: {
borderColor: null,
pointPadding: 0,
groupPadding: 0,
},
*/
},
series,
}

/*
const years = data.map((d) => d.id.slice(0, 4)).map(Number)

const total = Object.values(data[0][band]).reduce(
(acc, cur) => acc + cur,
0
)

const keys = [
...new Set(data.map((d) => Object.keys(d[band]).map(Number)).flat()),
]

// console.log("Keys", keys);

const series = keys.map((key) => ({
key,
name: landcoverTypes.find((c) => c.value === key).name,
color: landcoverTypes.find((c) => c.value === key).color,
data: data.map((d) =>
roundOneDecimal(((d[band][key] || 0) / total) * 100)
),
}))

series.sort((a, b) => {
const aValue = data[0][band][a.key] || 0
const bValue = data[0][band][b.key] || 0
return aValue - bValue
})
*/

/*
return {
chart: {
type: 'column',
height: 580,
},
title: {
text: i18n.t('{{name}}: Land cover changes {{years}}', {
name,
years: `${years[0]}-${years[years.length - 1]}`,
nsSeparator: ';',
}),
},
credits: landCoverCredits,
xAxis: {
categories: years,
},
yAxis: {
min: 0,
max: 100,
labels: {
format: '{value}%',
},
title: {
enabled: false,
},
},
tooltip: {
shared: true,
headerFormat:
'<span style="font-size:12px"><b>{point.key}</b></span><br>',
valueSuffix: '%',
},
plotOptions: {
series: {
animation,
},
column: {
stacking: 'normal',
borderColor: null,
// pointPadding: 0,
groupPadding: 0,
},
},
series,
}
*/
}

export default getChartConfig
4 changes: 4 additions & 0 deletions src/hooks/useEarthEngineTimeSeries.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const useEarthEngineTimeSeries = ({ dataset, period, feature, filter }) => {
useEffect(() => {
let canceled = false

// console.log('A', dataset, feature)

if (dataset && feature) {
const key = getCacheKey({ dataset, period, feature, filter })
const { geometry } = feature
Expand All @@ -42,6 +44,8 @@ const useEarthEngineTimeSeries = ({ dataset, period, feature, filter }) => {
}
}

// console.log('B')

setData()
eePromise.then((ee) => {
cachedPromise[key] = period
Expand Down
43 changes: 33 additions & 10 deletions src/utils/ee-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,17 @@ export const getTimeSeriesData = async ({
}) => {
const { datasetId, band, aggregationPeriod } = dataset

let collection = ee.ImageCollection(datasetId).select(band)
const { type, coordinates } = geometry
const eeGeometry = ee.Geometry[type](coordinates)

// console.log('getTimeSeriesData', dataset)

// let collection = ee.ImageCollection(datasetId).select(band)
let collection = ee
.ImageCollection(datasetId)
.select(band)
.filter(ee.Filter.bounds(eeGeometry))
// .reduce(ee.Reducer.mode())

if (Array.isArray(filter)) {
filter.forEach((f) => {
Expand All @@ -410,17 +420,23 @@ export const getTimeSeriesData = async ({
const months = ee.List.sequence(0, monthCount.subtract(1))
const dates = months.map((month) => startMonth.advance(month, 'month'))

// console.log('#########')

const byMonth = ee.ImageCollection.fromImages(
dates.map((date) => {
const startDate = ee.Date(date)
const endDate = startDate.advance(1, 'month')

return collection
.filter(ee.Filter.date(startDate, endDate))
.mean() // Use mean to avoid extremes on monthly chart
.set('system:index', startDate.format('YYYYMM'))
.set('system:time_start', startDate.millis())
.set('system:time_end', endDate.millis())
return (
collection
.filter(ee.Filter.date(startDate, endDate))
.mean() // Use mean to avoid extremes on monthly chart
// .mode()
// .reduce(ee.Reducer.mode())
.set('system:index', startDate.format('YYYYMM'))
.set('system:time_start', startDate.millis())
.set('system:time_end', endDate.millis())
)
})
)

Expand All @@ -437,7 +453,7 @@ export const getTimeSeriesData = async ({

let eeScale = getScale(collection.first())

const { type, coordinates } = geometry
// const { type, coordinates } = geometry

if (type.includes('Polygon')) {
// unweighted reducer may fail if the features are smaller than the pixel area
Expand All @@ -449,7 +465,9 @@ export const getTimeSeriesData = async ({
}
}

const eeGeometry = ee.Geometry[type](coordinates)
// const eeGeometry = ee.Geometry[type](coordinates)

const maxPixels = 1e13

// Returns a time series array of objects
return getInfo(
Expand All @@ -458,7 +476,12 @@ export const getTimeSeriesData = async ({
ee
.Feature(
null,
image.reduceRegion(eeReducer, eeGeometry, eeScale)
image.reduceRegion({
reducer: eeReducer,
geometry: eeGeometry,
scale: eeScale,
maxPixels: maxPixels,
})
)
.set('system:index', image.get('system:index'))
// .set("id", image.get("system:index"))
Expand Down