Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: new Earth Engine format and functions (DHIS2-16097) #548

Merged
merged 58 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
fabcd9c
feat: new earth engine format
turban Aug 14, 2023
c04c5a7
chore: ee layer refactor
turban Aug 26, 2023
f466706
chore: refactor
turban Aug 29, 2023
998db85
chore: revert tile size change
turban Aug 29, 2023
1f4e7c5
chore: code cleaning
turban Aug 29, 2023
fa875b6
feat: ee feature style
turban Aug 29, 2023
2bfd20e
chore: code cleaning
turban Aug 30, 2023
804d1a0
Merge branch 'master' into feat/custom-ee-layers
turban Sep 7, 2023
e5426f9
fix: ee period filter
turban Sep 7, 2023
bed7b40
fix: remap ee image
turban Oct 9, 2023
e53180c
chore: remove console log
turban Oct 9, 2023
17ab6e7
feat: new ee format
turban Oct 16, 2023
cc3731b
chore: refactor
turban Oct 26, 2023
cc82e54
feat: upgrade ee dep and support cloud score
turban Nov 2, 2023
e339097
chore: cloud score
turban Nov 3, 2023
fadf30f
chore: code cleaning
turban Nov 3, 2023
8655150
chore: code cleaning
turban Nov 3, 2023
c069443
chore: code cleaning
turban Nov 3, 2023
885438f
chore: code cleaning
turban Nov 11, 2023
b7bf893
feat: unweigthed reducer param
turban Nov 15, 2023
f9a3b99
chore: code cleaning
turban Nov 16, 2023
259495a
chore: code cleaning
turban Nov 16, 2023
b71bcab
chore: code cleaning
turban Nov 16, 2023
6887bcd
fix: ee feature layer style
turban Nov 16, 2023
b47e5fe
fix: ee value precision
turban Nov 16, 2023
7e29a4e
feat: ee layer masking
turban Nov 25, 2023
e8e8871
chore: code cleaning
turban Nov 27, 2023
0939a10
fix: run ee methods before masking
turban Nov 29, 2023
ba53e83
chore: merge in master
turban Jan 29, 2024
30f9bf4
Merge branch 'master' into feat/custom-ee-layers
turban Jan 30, 2024
ff46625
Merge branch 'master' into feat/custom-ee-layers
turban Mar 17, 2024
dce822a
chore: use patch-package instead of keeping google script in repo
jenniferarnesen May 15, 2024
922d92d
fix: import correct ee api dependency
turban May 15, 2024
574ed5f
chore: update unit tests to new format
turban May 15, 2024
a894af8
Merge branch 'master' into feat/custom-ee-layers
jenniferarnesen Jun 19, 2024
8b96387
chore: refresh yarn.lock file
jenniferarnesen Jun 19, 2024
90b0244
chore: lint
jenniferarnesen Jun 19, 2024
b3d7f41
chore: upgrade to latest google/earthengine
jenniferarnesen Jun 19, 2024
e33bb95
Merge branch 'master' into feat/custom-ee-layers
jenniferarnesen Jun 19, 2024
2791fca
chore: use supported node
jenniferarnesen Jun 19, 2024
aff5d7d
Merge branch 'master' into feat/custom-ee-layers
jenniferarnesen Jun 19, 2024
9f9fddf
chore: update numbers tests
jenniferarnesen Jun 20, 2024
776cbd3
chore: unit test
turban Jun 22, 2024
52cb21d
chore: refactor
turban Jun 22, 2024
2213cae
chore: unit tests
turban Jun 22, 2024
ae9f38d
chore: use constant
turban Jun 22, 2024
d97aea9
fix: option typo
turban Jun 22, 2024
db465b0
chore: downgrade patch-package to same v as maps-app
jenniferarnesen Jun 24, 2024
b50fa2d
Merge branch 'feat/custom-ee-layers' of github.com:dhis2/maps-gl into…
jenniferarnesen Jun 24, 2024
fd3c968
chore: patch-package needs to be run from app
jenniferarnesen Jun 24, 2024
1bd1e00
chore: no need for postinstall-postinstall
jenniferarnesen Jun 24, 2024
f1526c3
chore: revert the switch to google/earthengine since it fails to inst…
jenniferarnesen Jul 2, 2024
bdb6cf2
Merge branch 'master' into feat/custom-ee-layers
jenniferarnesen Jul 4, 2024
145336c
chore: missing imports
jenniferarnesen Jul 4, 2024
e9a3470
chore: lint
jenniferarnesen Jul 4, 2024
4899a7b
chore: fix lint warnings
jenniferarnesen Jul 4, 2024
e4e507e
chore: lint
jenniferarnesen Jul 4, 2024
d76f116
feat: new unmask aggregation option (#590)
turban Jul 29, 2024
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
14 changes: 7 additions & 7 deletions src/earthengine/__tests__/ee_worker_utils.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import { hasClasses, getHistogramStatistics } from '../ee_worker_utils'

const scale = 1000

const legend = [
const style = [
{
id: 9,
value: 9,
name: 'Savannas',
color: '#d99125',
},
{
id: 13,
value: 13,
name: 'Urban and built-up',
color: '#cc0202',
},
Expand Down Expand Up @@ -76,14 +76,14 @@ describe('earthengine', () => {
data,
scale,
aggregationType,
legend,
style,
})

const itemA = result['O6uvpzGd5pu']
const itemB = result['fdc6uOvgoji']

expect(ids).toEqual(Object.keys(result))
expect(Object.keys(itemA).length).toBe(legend.length)
expect(Object.keys(itemA).length).toBe(style.length)
expect(itemA['9']).toBe(toPercent(0))
expect(itemA['13']).toBe(toPercent(7))
expect(itemB['9']).toBe(toPercent(5))
Expand All @@ -97,7 +97,7 @@ describe('earthengine', () => {
data,
scale,
aggregationType,
legend,
style,
})

const itemA = result['O6uvpzGd5pu']
Expand All @@ -116,7 +116,7 @@ describe('earthengine', () => {
data,
scale,
aggregationType,
legend,
style,
})

const itemA = result['O6uvpzGd5pu']
Expand Down
198 changes: 132 additions & 66 deletions src/earthengine/ee_worker.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expose } from 'comlink'
import ee from './ee_api_js_worker' // https://github.com/google/earthengine-api/pull/173
import { getBufferGeometry } from '../utils/buffers.js'
import ee from './ee_api_js_worker.js' // https://github.com/google/earthengine-api/pull/173
// import { ee } from '@google/earthengine/build/ee_api_js_debug' // Run "yarn add @google/earthengine"
import {
getInfo,
Expand All @@ -9,15 +10,26 @@ import {
getClassifiedImage,
getHistogramStatistics,
getFeatureCollectionProperties,
} from './ee_worker_utils'
import { getBufferGeometry } from '../utils/buffers'

// Why we need to "hack" the '@google/earthengine bundle:
// https://groups.google.com/g/google-earth-engine-developers/c/nvlbqxrnzDk/m/QuyWxGt9AQAJ

const FEATURE_STYLE = { color: 'FFA500', strokeWidth: 2 }
applyFilter,
applyMethods,
applyCloudMask,
} from './ee_worker_utils.js'

const IMAGE = 'Image'
const IMAGE_COLLECTION = 'ImageCollection'
const FEATURE_COLLECTION = 'FeatureCollection'

// Options are defined here:
// https://developers.google.com/earth-engine/apidocs/ee-featurecollection-draw
const DEFAULT_FEATURE_STYLE = {
color: '#FFA500',
strokeWidth: 2,
pointRadius: 5,
}
const DEFAULT_TILE_SCALE = 1

const DEFAULT_MASK_VALUE = 0

class EarthEngineWorker {
constructor(options = {}) {
this.options = options
Expand Down Expand Up @@ -102,12 +114,23 @@ class EarthEngineWorker {
return this.eeImage
}

const { datasetId, filter, mosaic, band, bandReducer, mask, methods } =
this.options
const {
datasetId,
format,
filter,
periodReducer,
mosaic,
band,
bandReducer,
maskOperator,
methods,
style,
cloudScore,
} = this.options

let eeImage

if (!filter) {
if (format === IMAGE) {
// Single image
eeImage = ee.Image(datasetId)
this.eeScale = getScale(eeImage)
Expand All @@ -119,18 +142,26 @@ class EarthEngineWorker {
this.eeScale = getScale(collection.first())

// Apply array of filters (e.g. period)
filter.forEach(f => {
collection = collection.filter(
ee.Filter[f.type].apply(this, f.arguments)
)
})
collection = applyFilter(collection, filter)

// Mask out clouds from satellite images
if (cloudScore) {
collection = applyCloudMask(collection, cloudScore)
}

eeImage = mosaic
? collection.mosaic() // Composite all images inn a collection (e.g. per country)
: ee.Image(collection.first()) // There should only be one image after applying the filters
if (periodReducer) {
// Apply period reducer (e.g. going from daily to monthly)
eeImage = collection[periodReducer]()
} else if (mosaic) {
// Composite all images inn a collection (e.g. per country)
eeImage = collection.mosaic()
} else {
// There should only be one image after applying the filters
eeImage = ee.Image(collection.first())
}
}

// // Select band (e.g. age group)
// Select band (e.g. age group)
if (band) {
eeImage = eeImage.select(band)

Expand All @@ -143,18 +174,14 @@ class EarthEngineWorker {
}
}

// Mask out 0-values
if (mask) {
eeImage = eeImage.updateMask(eeImage.gt(0))
}

// Run methods on image
if (methods) {
Object.keys(methods).forEach(method => {
if (eeImage[method]) {
eeImage = eeImage[method].apply(eeImage, methods[method])
}
})
eeImage = applyMethods(eeImage, methods)

// Use mask operator (e.g. mask out values below a certain threshold)
if (maskOperator && eeImage[maskOperator]) {
eeImage = eeImage.updateMask(
eeImage[maskOperator](style?.min || DEFAULT_MASK_VALUE)
)
}

this.eeImage = eeImage
Expand All @@ -164,38 +191,52 @@ class EarthEngineWorker {

// Returns raster tile url for a classified image
getTileUrl() {
const { format, data } = this.options
const { datasetId, format, data, filter, style } = this.options

return new Promise(resolve => {
if (format === 'FeatureCollection') {
const { datasetId } = this.options
return new Promise((resolve, reject) => {
switch (format) {
case FEATURE_COLLECTION: {
let dataset = ee.FeatureCollection(datasetId)

let dataset = ee
.FeatureCollection(datasetId)
.draw(FEATURE_STYLE)
dataset = applyFilter(dataset, filter).draw({
...DEFAULT_FEATURE_STYLE,
...style,
})

if (data) {
dataset = dataset.clipToCollection(
this.getFeatureCollection()
if (data) {
dataset = dataset.clipToCollection(
this.getFeatureCollection()
)
}

dataset.getMap(null, response =>
resolve(response.urlFormat)
)

break
}
case IMAGE:
case IMAGE_COLLECTION: {
// eslint-disable-next-line prefer-const
let { eeImage, params } = getClassifiedImage(
this.getImage(),
this.options
)

dataset.getMap(null, response => resolve(response.urlFormat))
} else {
let { eeImage, params } = getClassifiedImage(
this.getImage(),
this.options
)
if (data) {
eeImage = eeImage.clipToCollection(
this.getFeatureCollection()
)
}

if (data) {
eeImage = eeImage.clipToCollection(
this.getFeatureCollection()
)
}
eeImage
.visualize(params)
.getMap(null, response => resolve(response.urlFormat))

eeImage
.visualize(params)
.getMap(null, response => resolve(response.urlFormat))
break
}
default:
reject(new Error('Unknown format'))
jenniferarnesen marked this conversation as resolved.
Show resolved Hide resolved
}
})
}
Expand All @@ -219,11 +260,26 @@ class EarthEngineWorker {

const featureCollection = ee
.FeatureCollection(imageCollection)
.select(['system:time_start', 'system:time_end'], null, false)
.select(
['system:time_start', 'system:time_end', 'year'],
null,
false
)

return getInfo(featureCollection)
}

// Returns min and max timestamp for an image collection
getTimeRange(eeId) {
const collection = ee.ImageCollection(eeId)

const range = collection.reduceColumns(ee.Reducer.minMax(), [
'system:time_start',
])

return getInfo(range)
}

// Returns aggregated values for org unit features
async getAggregations(config) {
if (config) {
Expand All @@ -233,20 +289,25 @@ class EarthEngineWorker {
format,
aggregationType,
band,
legend,
useCentroid,
style,
tileScale = DEFAULT_TILE_SCALE,
} = this.options
const singleAggregation = !Array.isArray(aggregationType)
const useHistogram =
singleAggregation && hasClasses(aggregationType) && legend
singleAggregation &&
hasClasses(aggregationType) &&
Array.isArray(style)
const image = await this.getImage()
const scale = this.eeScale
const collection = this.getFeatureCollection() // TODO: Throw error if no feature collection
const collection = this.getFeatureCollection()

if (collection) {
if (format === 'FeatureCollection') {
const { datasetId } = this.options
const dataset = ee.FeatureCollection(datasetId)
if (format === FEATURE_COLLECTION) {
const { datasetId, filter } = this.options
let dataset = ee.FeatureCollection(datasetId)

dataset = applyFilter(dataset, filter)

const aggFeatures = collection
.map(feature => {
Expand Down Expand Up @@ -279,11 +340,11 @@ class EarthEngineWorker {
data,
scale: scaleValue,
aggregationType,
legend,
style,
})
)
} else if (!singleAggregation && aggregationType.length) {
const reducer = combineReducers(ee)(aggregationType)
const reducer = combineReducers(aggregationType, useCentroid)
const props = [...aggregationType]

let aggFeatures = image.reduceRegions({
Expand Down Expand Up @@ -315,13 +376,18 @@ class EarthEngineWorker {
aggFeatures = aggFeatures.select(props, null, false)

return getInfo(aggFeatures).then(getFeatureCollectionProperties)
} else throw new Error('Aggregation type is not valid')
} else throw new Error('Missing org unit features')
} else {
throw new Error('Aggregation type is not valid')
}
} else {
throw new Error('Missing org unit features')
}
}
}

// Service Worker not supported in Safari
if (typeof onconnect !== 'undefined') {
// eslint-disable-next-line no-undef
onconnect = evt => expose(EarthEngineWorker, evt.ports[0])
} else {
expose(EarthEngineWorker)
Expand Down
Loading
Loading