diff --git a/src/actions/externalLayers.js b/src/actions/externalLayers.js index 2748b7e06..e5eeff0e8 100644 --- a/src/actions/externalLayers.js +++ b/src/actions/externalLayers.js @@ -3,12 +3,8 @@ import * as types from '../constants/actionTypes.js' import { createExternalLayer } from '../util/external.js' import { fetchExternalLayers } from '../util/requests.js' import { addBasemaps } from './basemap.js' -import { EARTH_ENGINE_LAYER } from '../constants/layers.js' -export const EXTERNAL_LAYERS_NAMESPACE = 'EXTERNAL_LAYERS' - -// const isBasemap = (layer) => layer.mapLayerPosition === 'BASEMAP' -const isBasemap = (layer) => layer.position === 'basemap' +const isBasemap = (layer) => layer.mapLayerPosition === 'BASEMAP' const isOverlay = (layer) => !isBasemap(layer) // Add external overlay @@ -18,79 +14,18 @@ export const addExternalLayer = (layer) => ({ }) export const tSetExternalLayers = (engine) => async (dispatch) => { - engine - .query({ - dataStore: { - resource: 'dataStore', - }, - }) - .then(({ dataStore }) => { - if (dataStore.includes(EXTERNAL_LAYERS_NAMESPACE)) { - engine - .query({ - layerIds: { - resource: `dataStore/${EXTERNAL_LAYERS_NAMESPACE}`, - }, - }) - .then(({ layerIds }) => { - // TODO: Possible to load all layers at once? - Promise.all( - layerIds.map((layerId) => - engine - .query({ - layer: { - resource: `dataStore/${EXTERNAL_LAYERS_NAMESPACE}/${layerId}`, - }, - }) - .then(({ layer }) => layer) - ) - ).then((layers) => { - layers.sort((a, b) => a.name.localeCompare(b.name)) - - const basemaps = layers - .filter(isBasemap) - .map(createExternalLayer) - - if (basemaps.length) { - dispatch(addBasemaps(basemaps)) - } - - // console.log('basemaps', basemaps) - - layers.filter(isOverlay).forEach((layer) => { - const layerId = layer.id // TODO - delete layer.id // TODO - - dispatch( - addExternalLayer({ - ...layer, - layer: EARTH_ENGINE_LAYER, // TODO - layerId, // TODO - }) - ) - }) - }) - }) - } - }) - try { const externalLayers = await fetchExternalLayers(engine) const externalBasemaps = externalLayers.externalLayers.externalMapLayers - .filter((layer) => layer.mapLayerPosition === 'BASEMAP') + .filter(isBasemap) .map(createExternalLayer) - // console.log('externalBasemaps', externalBasemaps) - - /* dispatch(addBasemaps(externalBasemaps)) - externalLayers.externalLayers.externalMapLayers .filter(isOverlay) .map(createExternalLayer) .map((layer) => dispatch(addExternalLayer(layer))) - */ } catch (e) { log.error('Could not load external map layers') return e diff --git a/src/components/edit/earthEngine/EarthEngineDialog.js b/src/components/edit/earthEngine/EarthEngineDialog.js index ab85f8c97..6be648166 100644 --- a/src/components/edit/earthEngine/EarthEngineDialog.js +++ b/src/components/edit/earthEngine/EarthEngineDialog.js @@ -8,7 +8,7 @@ import { setFilter, setBufferRadius, } from '../../../actions/layerEdit.js' -import { getEarthEngineLayer } from '../../../constants/earthEngine.js' +// import { getEarthEngineLayer } from '../../../constants/earthEngine.js' import { DEFAULT_ORG_UNIT_LEVEL, EE_BUFFER, @@ -35,7 +35,7 @@ const EarthEngineDialog = (props) => { const [error, setError] = useState() const { - layerId, + // layerId, datasetId, band, rows, @@ -59,11 +59,12 @@ const EarthEngineDialog = (props) => { periodType, periodReducer, bands, - filters = defaultFilters, + filters, // = defaultFilters, unit, source, sourceUrl, aggregations, + defaultAggregations, } = props // dataset const period = getPeriodFromFilter(filter) @@ -121,6 +122,7 @@ const EarthEngineDialog = (props) => { const noBandSelected = Array.isArray(bands) && (!band || !band.length) + const hasAggregations = !!(aggregations || defaultAggregations) const hasMultipleAggregations = !aggregations || aggregations.length > 1 const hasOrgUnitField = !!orgUnitField && orgUnitField !== NONE @@ -182,6 +184,12 @@ const EarthEngineDialog = (props) => { } }, [hasOrgUnitField, areaRadius, setBufferRadius]) + useEffect(() => { + if (!periodType && filters) { + setFilter(filters) + } + }, [periodType, filters, setFilter]) + useEffect(() => { if (validateLayer) { const isValid = !noBandSelected && (!periodType || period) @@ -258,7 +266,7 @@ const EarthEngineDialog = (props) => { } /> )} - + {hasAggregations && } {unit && (
{i18n.t('Unit')}: {unit} @@ -306,7 +314,7 @@ const EarthEngineDialog = (props) => { EarthEngineDialog.propTypes = { datasetId: PropTypes.string.isRequired, - layerId: PropTypes.string.isRequired, + // layerId: PropTypes.string.isRequired, setBufferRadius: PropTypes.func.isRequired, setFilter: PropTypes.func.isRequired, setOrgUnits: PropTypes.func.isRequired, @@ -318,10 +326,10 @@ EarthEngineDialog.propTypes = { legend: PropTypes.object, orgUnitField: PropTypes.string, orgUnits: PropTypes.object, - params: PropTypes.shape({ - max: PropTypes.number.isRequired, - min: PropTypes.number.isRequired, - palette: PropTypes.array.isRequired, + style: PropTypes.shape({ + max: PropTypes.number, + min: PropTypes.number, + palette: PropTypes.array, }), rows: PropTypes.array, } diff --git a/src/components/edit/earthEngine/StyleTab.js b/src/components/edit/earthEngine/StyleTab.js index 130b4c6fc..a705854e9 100644 --- a/src/components/edit/earthEngine/StyleTab.js +++ b/src/components/edit/earthEngine/StyleTab.js @@ -6,26 +6,34 @@ import styles from '../styles/LayerDialog.module.css' import LegendPreview from './LegendPreview.js' import StyleSelect from './StyleSelect.js' -const StyleTab = ({ unit, style, hasOrgUnitField }) => ( -
-
- {style && } - +const StyleTab = ({ unit, style, hasOrgUnitField }) => { + const { min, max, palette } = style + const isClassStyle = + min !== undefined && max !== undefined && palette !== undefined + + return ( +
+
+ {isClassStyle && } + +
+ {isClassStyle && }
- {style && } -
-) + ) +} StyleTab.propTypes = { hasOrgUnitField: PropTypes.bool.isRequired, unit: PropTypes.string, style: PropTypes.shape({ - max: PropTypes.number.isRequired, - min: PropTypes.number.isRequired, - palette: PropTypes.array.isRequired, + max: PropTypes.number, + min: PropTypes.number, + palette: PropTypes.array, + color: PropTypes.string, + strokeWidth: PropTypes.number, }), } diff --git a/src/components/map/layers/earthEngine/EarthEngineLayer.js b/src/components/map/layers/earthEngine/EarthEngineLayer.js index 7b4bb62a0..491b3012f 100644 --- a/src/components/map/layers/earthEngine/EarthEngineLayer.js +++ b/src/components/map/layers/earthEngine/EarthEngineLayer.js @@ -144,6 +144,7 @@ export default class EarthEngineLayer extends Layer { config.getAuthToken = getAuthToken try { + console.log('config', config) this.layer = map.createLayer(config) await map.addLayer(this.layer) } catch (error) { diff --git a/src/constants/earthEngine.js b/src/constants/earthEngine.js index 94880cd81..5ca2e1076 100644 --- a/src/constants/earthEngine.js +++ b/src/constants/earthEngine.js @@ -1,7 +1,331 @@ -import i18n from '@dhis2/d2-i18n' -import { defaultFilters } from '../util/earthEngine.js' +// import i18n from '@dhis2/d2-i18n' +// import { defaultFilters } from '../util/earthEngine.js' import { EARTH_ENGINE_LAYER } from './layers.js' +console.log('EARTH_ENGINE_LAYER', EARTH_ENGINE_LAYER) + +export const earthEngineLayers = [ + { + layer: EARTH_ENGINE_LAYER, // TODO: Remove? + // layerId: 'WorldPop/GP/100m/pop_age_sex_cons_unadj_TOTAL', // TODO: Remove? + img: 'images/population.png', // TODO: Remove? + service: 'earthengine', + // id: 'earthengine_population', // TODO + datasetId: 'WorldPop/GP/100m/pop_age_sex_cons_unadj', + format: 'ImageCollection', + name: 'Population', + description: 'Estimated number of people living in an area', + source: 'WorldPop / Google Earth Engine', + unit: 'people per hectare', + defaultAggregations: ['sum', 'mean'], + periodType: 'yearly', + band: 'population', + filters: [ + { + type: 'eq', + arguments: ['year', '$1'], + }, + ], + mosaic: true, + style: { + min: 0, + max: 25, + palette: [ + '#fee5d9', + '#fcbba1', + '#fc9272', + '#fb6a4a', + '#de2d26', + '#a50f15', + ], + }, + opacity: 0.9, + }, + { + layer: EARTH_ENGINE_LAYER, // TODO: Remove? + // layerId: 'WorldPop/GP/100m/pop_age_sex_cons_unadj_TOTAL', // TODO: Remove? + img: 'images/population.png', // TODO: Remove? + service: 'earthengine', + // id: 'earthengine_population', // TODO + datasetId: 'WorldPop/GP/100m/pop_age_sex_cons_unadj/SLE_2020', + format: 'Image', + name: 'Population Sierra Leone 2000', + description: 'Estimated number of people living in an area', + source: 'WorldPop / Google Earth Engine', + unit: 'people per hectare', + defaultAggregations: ['sum', 'mean'], + band: 'population', + style: { + min: 0, + max: 25, + palette: [ + '#fee5d9', + '#fcbba1', + '#fc9272', + '#fb6a4a', + '#de2d26', + '#a50f15', + ], + }, + opacity: 0.9, + }, + { + layer: EARTH_ENGINE_LAYER, // TODO: Remove? + service: 'earthengine', + datasetId: 'RESOLVE/ECOREGIONS/2017', + format: 'FeatureCollection', + name: 'Ecoregions', + description: 'Estimated number of people living in an area', + source: 'NASA / USGS / JPL-Caltech / Google Earth Engine', + style: { + byProperty: 'COLOR', + // byProperty: { + // color: 'COLOR_NNH', + // }, + }, + opacity: 0.9, + }, + { + layer: EARTH_ENGINE_LAYER, // TODO: Remove? + img: 'images/buildings.png', + service: 'earthengine', + // id: 'earthengine_building-footprints', // TODO + datasetId: 'GOOGLE/Research/open-buildings/v1/polygons', + format: 'FeatureCollection', + name: 'Building footprints', + description: 'Estimated number of people living in an area', + // notice: 'Building counts are only available for smaller organisation unit areas.', + error: 'Select a smaller area or single organization unit to see the count of buildings.', + source: 'NASA / USGS / JPL-Caltech / Google Earth Engine', + unit: 'Number of buildings', + aggregations: ['count'], + defaultAggregations: ['count'], + filters: [ + { + type: 'gt', + arguments: ['area_in_meters', 500], + }, + ], + style: { + color: '#FFA500', + width: 1, + }, + opacity: 0.9, + }, + { + layer: EARTH_ENGINE_LAYER, // TODO: Remove? + img: 'images/buildings.png', + service: 'earthengine', + // id: 'earthengine_building-footprints', // TODO + datasetId: 'GOOGLE/Research/open-buildings/v1/polygons', + format: 'FeatureCollection', + name: 'Buildings > 500 m²', + description: 'Estimated number of people living in an area', + // notice: 'Building counts are only available for smaller organisation unit areas.', + error: 'Select a smaller area or single organization unit to see the count of buildings.', + source: 'NASA / USGS / JPL-Caltech / Google Earth Engine', + unit: 'Number of buildings', + aggregations: ['count'], + defaultAggregations: ['count'], + filters: [ + { + type: 'gt', + arguments: ['area_in_meters', 500], + }, + ], + style: { + color: '#FFA500', + width: 1, + }, + opacity: 0.9, + }, + { + layer: EARTH_ENGINE_LAYER, // TODO: Remove? + img: 'images/temperature.png', + service: 'earthengine', + // id: 'earthengine_temperature-era5', + datasetId: 'ECMWF/ERA5_LAND/DAILY_AGGR', + format: 'ImageCollection', + name: 'Temperature daily', + description: 'Temperature at 2m above the surface', + source: 'Copernicus Climate Data Store / Google Earth Engine', + unit: '°C', + aggregations: ['min', 'max', 'mean', 'median', 'stdDev', 'variance'], + defaultAggregations: ['mean', 'min', 'max'], + periodType: 'daily', + periodReducer: 'mean', + band: 'temperature_2m', + filters: [ + { + type: 'date', + arguments: ['$1', '$2'], + }, + ], + methods: [ + { + name: 'toFloat', + arguments: [], + }, + { + name: 'subtract', + arguments: [273.15], + }, + ], + style: { + min: 0, + max: 40, + palette: [ + '#fff5f0', + '#fee0d2', + '#fcbba1', + '#fc9272', + '#fb6a4a', + '#ef3b2c', + '#cb181d', + '#a50f15', + '#67000d', + ], + }, + opacity: 0.9, + }, + { + layer: EARTH_ENGINE_LAYER, // TODO: Remove? + img: 'images/temperature.png', + service: 'earthengine', + // layerId: 'ECMWF/ERA5_LAND/MONTHLY_AGGR/temperature_2m', + // id: 'earthengine_temperature-era5', + datasetId: 'ECMWF/ERA5_LAND/MONTHLY_AGGR', + format: 'ImageCollection', + name: 'Temperature monthly', + description: 'Temperature at 2m above the surface', + source: 'Copernicus Climate Data Store / Google Earth Engine', + unit: '°C', + aggregations: ['min', 'max', 'mean', 'median', 'stdDev', 'variance'], + defaultAggregations: ['mean', 'min', 'max'], + periodType: 'byYear', + // periodReducer: 'mean', + band: 'temperature_2m', + filters: [ + { + type: 'eq', + arguments: ['system:index', '$1'], + }, + ], + methods: [ + { + name: 'toFloat', + arguments: [], + }, + { + name: 'subtract', + arguments: [273.15], + }, + ], + style: { + min: 0, + max: 40, + palette: [ + '#fff5f0', + '#fee0d2', + '#fcbba1', + '#fc9272', + '#fb6a4a', + '#ef3b2c', + '#cb181d', + '#a50f15', + '#67000d', + ], + }, + opacity: 0.9, + }, + { + layer: EARTH_ENGINE_LAYER, // TODO: Remove? + img: 'images/precipitation.png', + service: 'earthengine', + // id: 'earthengine_precipitation-era5', + datasetId: 'ECMWF/ERA5_LAND/DAILY_AGGR', + format: 'ImageCollection', + name: 'Precipitation daily', + description: 'Precipitation', + source: 'Copernicus Climate Data Store / Google Earth Engine', + unit: 'millimeter', + aggregations: ['min', 'max', 'mean', 'median', 'stdDev', 'variance'], + defaultAggregations: ['mean', 'min', 'max'], + periodType: 'daily', + periodReducer: 'sum', + band: 'total_precipitation_sum', + filters: [ + { + type: 'date', + arguments: ['$1', '$2'], + }, + ], + methods: [ + { + name: 'multiply', + arguments: [1000], + }, + ], + style: { + min: 0, + max: 10, + palette: [ + '#eff3ff', + '#c6dbef', + '#9ecae1', + '#6baed6', + '#3182bd', + '#08519c', + ], + }, + opacity: 0.9, + }, + { + layer: EARTH_ENGINE_LAYER, // TODO: Remove? + img: 'images/precipitation.png', + service: 'earthengine', + // id: 'earthengine_precipitation-era5', + datasetId: 'ECMWF/ERA5_LAND/MONTHLY_AGGR', + format: 'ImageCollection', + name: 'Precipitation monthly', + description: 'Precipitation', + source: 'Copernicus Climate Data Store / Google Earth Engine', + unit: 'millimeter', + aggregations: ['min', 'max', 'mean', 'median', 'stdDev', 'variance'], + defaultAggregations: ['mean', 'min', 'max'], + periodType: 'byYear', + band: 'total_precipitation_sum', + filters: [ + { + type: 'eq', + arguments: ['system:index', '$1'], + }, + ], + methods: [ + { + name: 'multiply', + arguments: [1000], + }, + ], + style: { + min: 0, + max: 700, + palette: [ + '#f7fbff', + '#deebf7', + '#c6dbef', + '#9ecae1', + '#6baed6', + '#4292c6', + '#2171b5', + '#084594', + ], + }, + opacity: 0.9, + }, +] + +/* // layerId should be unique // datasetId is the Earth Engine dataset id export const earthEngineLayers = () => [ @@ -574,6 +898,7 @@ export const earthEngineLayers = () => [ opacity: 0.9, }, ] +*/ export const getEarthEngineLayer = (id) => - earthEngineLayers().find((l) => l.layerId === id) + earthEngineLayers.find((l) => l.layerId === id) diff --git a/src/loaders/earthEngineLoader.js b/src/loaders/earthEngineLoader.js index 6fd2cc823..061f74090 100644 --- a/src/loaders/earthEngineLoader.js +++ b/src/loaders/earthEngineLoader.js @@ -20,7 +20,7 @@ const earthEngineLoader = async (config) => { const alerts = [] let layerConfig = {} - let dataset + // let dataset let features if (orgUnits && orgUnits.length) { @@ -113,26 +113,31 @@ const earthEngineLoader = async (config) => { layerConfig.params.palette = layerConfig.params.palette.split(',') } + /* dataset = getEarthEngineLayer(layerConfig.id) if (dataset) { delete layerConfig.id } + */ delete config.config } else { - dataset = getEarthEngineLayer(layerConfig.id) + // dataset = getEarthEngineLayer(layerConfig.id) + // console.log('getEarthEngineLayer', layerConfig.id, dataset) } + // console.log('###', dataset, config, layerConfig) + const layer = { - ...dataset, + // ...dataset, ...config, ...layerConfig, } const { unit, filter, description, source, sourceUrl, band, bands, style } = layer - const { name } = dataset || config + const { name } = config // dataset || config const period = getPeriodNameFromFilter(filter) const data = Array.isArray(features) && features.length ? features : undefined diff --git a/src/reducers/layers.js b/src/reducers/layers.js index e6c162a2e..247f9d00d 100644 --- a/src/reducers/layers.js +++ b/src/reducers/layers.js @@ -41,7 +41,7 @@ const defaultLayers = () => [ img: 'images/orgunits.png', opacity: 1, }, - // ...earthEngineLayers().filter((l) => !l.legacy), + ...earthEngineLayers.filter((l) => !l.legacy), ] const layers = (state, action) => { diff --git a/src/util/earthEngine.js b/src/util/earthEngine.js index 9082fa972..c191f872c 100644 --- a/src/util/earthEngine.js +++ b/src/util/earthEngine.js @@ -140,6 +140,8 @@ export const getPeriods = async (eeId, periodType, filters) => { // console.log('ERROR', error) // } + console.log('features', features) + return features.map(getPeriod) }