diff --git a/web/js/components/layer/settings/layer-settings.js b/web/js/components/layer/settings/layer-settings.js
index 7997da04ed..f980dfc527 100644
--- a/web/js/components/layer/settings/layer-settings.js
+++ b/web/js/components/layer/settings/layer-settings.js
@@ -8,6 +8,7 @@ import { connect } from 'react-redux';
import Opacity from './opacity';
import Palette from './palette';
+import Size from './size';
import BandSelection from './band-selection/band-selection-parent-info-menu';
import AssociatedLayers from './associated-layers-toggle';
import VectorStyle from './vector-style';
@@ -37,6 +38,8 @@ import {
setThresholdRangeAndSquash,
setCustomPalette,
clearCustomPalette,
+ setSize,
+ clearSize,
setToggledClassification,
refreshDisabledClassification,
} from '../../../modules/palettes/actions';
@@ -337,14 +340,18 @@ class LayerSettings extends React.Component {
let renderCustomizations;
const {
setOpacity,
+ setSize,
+ clearSize,
customPalettesIsActive,
layer,
palettedAllowed,
zot,
+ groupName,
} = this.props;
const hasAssociatedLayers = layer.associatedLayers && layer.associatedLayers.length;
const hasTracks = layer.orbitTracks && layer.orbitTracks.length;
const titilerLayer = layer.id === 'HLS_Customizable_Sentinel' || layer.id === 'HLS_Customizable_Landsat';
+ const pointSizeLayer = layer.type === 'vector' && layer.id === 'MODIS_Aqua_Thermal_Anomalies_All';
const granuleMetadata = layer?.enableCMRDataFinder && !(zot?.underZoomValue > 0);
const layerGroup = layer.layergroup;
@@ -366,6 +373,16 @@ class LayerSettings extends React.Component {
setOpacity={setOpacity}
layer={layer}
/>
+ {pointSizeLayer && (
+
+ )}
{this.renderGranuleSettings()}
{renderCustomizations}
{titilerLayer && }
@@ -447,6 +464,12 @@ const mapDispatchToProps = (dispatch) => ({
setOpacity: (id, opacity) => {
dispatch(setOpacity(id, opacity));
},
+ setSize: (layerId, size, index, groupName) => {
+ dispatch(setSize(layerId, size, index, groupName));
+ },
+ clearSize: (layerId, index, groupName) => {
+ dispatch(clearSize(layerId, index, groupName));
+ },
updateGranuleLayerOptions: (dates, def, count) => {
dispatch(updateGranuleLayerOptions(dates, def, count));
},
@@ -485,6 +508,8 @@ LayerSettings.propTypes = {
screenHeight: PropTypes.number,
setCustomPalette: PropTypes.func,
setOpacity: PropTypes.func,
+ setSize: PropTypes.func,
+ clearSize: PropTypes.func,
setStyle: PropTypes.func,
setThresholdRange: PropTypes.func,
toggleClassification: PropTypes.func,
diff --git a/web/js/components/layer/settings/size.js b/web/js/components/layer/settings/size.js
new file mode 100644
index 0000000000..47e58730df
--- /dev/null
+++ b/web/js/components/layer/settings/size.js
@@ -0,0 +1,57 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import { debounce } from 'lodash';
+
+function SizeSelect(props) {
+ const {
+ layer,
+ start,
+ index,
+ groupName,
+ } = props;
+
+ const [value, setValue] = useState(start);
+
+ const debouncedSetSize = debounce((layerId, size, index, groupName) => {
+ const { setSize, clearSize } = props;
+ if (size === 0) {
+ clearSize(layerId, index, groupName);
+ } else {
+ setSize(layerId, size, index, groupName);
+ }
+ }, 100);
+
+ return (
+
+
Point Radius
+
{
+ const val = parseFloat(e.target.value);
+ debouncedSetSize(layer.id, val, index, groupName);
+ setValue(val);
+ }}
+ />
+
+ {value <= 0 ? 1 : value}
+ px
+
+
+ );
+}
+SizeSelect.defaultProps = {
+ start: 0,
+};
+SizeSelect.propTypes = {
+ layer: PropTypes.object,
+ setSize: PropTypes.func,
+ clearSize: PropTypes.func,
+ start: PropTypes.number,
+};
+
+export default SizeSelect;
diff --git a/web/js/map/layerbuilder.js b/web/js/map/layerbuilder.js
index 51f508747c..680e999e47 100644
--- a/web/js/map/layerbuilder.js
+++ b/web/js/map/layerbuilder.js
@@ -451,6 +451,8 @@ export default function mapLayerBuilder(config, cache, store) {
};
if (def.styles) {
parameters.STYLES = def.styles;
+ } else if (def.size && def.size >= 5) {
+ parameters.STYLES = `size${def.size}`;
}
urlParameters = '';
diff --git a/web/js/mapUI/components/update-projection/updateProjection.js b/web/js/mapUI/components/update-projection/updateProjection.js
index ba200ee3c0..48642936ab 100644
--- a/web/js/mapUI/components/update-projection/updateProjection.js
+++ b/web/js/mapUI/components/update-projection/updateProjection.js
@@ -335,6 +335,8 @@ function UpdateProjection(props) {
case paletteConstants.SET_CUSTOM:
case paletteConstants.SET_DISABLED_CLASSIFICATION:
case paletteConstants.CLEAR_CUSTOM:
+ case paletteConstants.SET_SIZE:
+ case paletteConstants.CLEAR_SIZE:
case layerConstants.ADD_LAYERS_FOR_EVENT:
return setTimeout(reloadLayers, 100);
case vectorStyleConstants.SET_FILTER_RANGE:
diff --git a/web/js/mapUI/mapUI.js b/web/js/mapUI/mapUI.js
index 4a1491e4b8..bb5906ddc3 100644
--- a/web/js/mapUI/mapUI.js
+++ b/web/js/mapUI/mapUI.js
@@ -128,6 +128,8 @@ function MapUI(props) {
case paletteConstants.SET_CUSTOM:
case paletteConstants.SET_DISABLED_CLASSIFICATION:
case paletteConstants.CLEAR_CUSTOM:
+ case paletteConstants.SET_SIZE:
+ case paletteConstants.CLEAR_SIZE:
case layerConstants.ADD_LAYERS_FOR_EVENT:
case vectorStyleConstants.SET_FILTER_RANGE:
case vectorStyleConstants.SET_VECTORSTYLE:
diff --git a/web/js/modules/layers/reducers.js b/web/js/modules/layers/reducers.js
index f25ec7a361..a1948ca732 100644
--- a/web/js/modules/layers/reducers.js
+++ b/web/js/modules/layers/reducers.js
@@ -29,6 +29,8 @@ import {
CLEAR_CUSTOM as CLEAR_CUSTOM_PALETTE,
SET_THRESHOLD_RANGE_AND_SQUASH,
SET_DISABLED_CLASSIFICATION,
+ SET_SIZE,
+ CLEAR_SIZE,
} from '../palettes/constants';
import {
CLEAR_VECTORSTYLE,
@@ -230,6 +232,29 @@ export function layerReducer(state = initialState, action) {
});
}
+ case CLEAR_SIZE: {
+ return update(state, {
+ [compareState]: {
+ layers: {
+ [getLayerIndex()]: {
+ size: { $set: undefined },
+ },
+ },
+ },
+ });
+ }
+
+ case SET_SIZE:
+ return update(state, {
+ [compareState]: {
+ layers: {
+ [getLayerIndex()]: {
+ size: { $set: action.size },
+ },
+ },
+ },
+ });
+
case SET_FILTER_RANGE: {
return update(state, {
[compareState]: {
diff --git a/web/js/modules/layers/reducers.test.js b/web/js/modules/layers/reducers.test.js
index 9a8da94232..79c0a50331 100644
--- a/web/js/modules/layers/reducers.test.js
+++ b/web/js/modules/layers/reducers.test.js
@@ -19,6 +19,8 @@ import {
SET_CUSTOM as SET_CUSTOM_PALETTE,
CLEAR_CUSTOM as CLEAR_CUSTOM_PALETTE,
SET_THRESHOLD_RANGE_AND_SQUASH,
+ SET_SIZE,
+ CLEAR_SIZE,
} from '../palettes/constants';
const config = fixtures.config();
@@ -238,6 +240,34 @@ describe('layer Reducer tests', () => {
expect(getTestLayer(response.active.layers).custom).toEqual(['custom-id']);
});
+ test('SET_SIZE action sets size for given layer [layers-reducer-set-size]', () => {
+ const response = layerReducer(initialState, {
+ type: SET_SIZE,
+ id: 'terra-cr',
+ activeString: 'active',
+ size: 15,
+ });
+
+ expect(getTestLayer(response.active.layers).size).toEqual(15);
+ });
+
+ test('CLEAR_SIZE action removed size value [layers-reducer-clear-size]', () => {
+ const customInitial = update(initialState, {
+ active: {
+ layers: {
+ 2: { size: { $set: 15 } },
+ },
+ },
+ });
+ const response = layerReducer(customInitial, {
+ type: CLEAR_SIZE,
+ id: 'terra-cr',
+ activeString: 'active',
+ });
+ expect(getTestLayer(customInitial.active.layers).size).toEqual(15);
+ expect(getTestLayer(response.active.layers).size).toEqual(undefined);
+ });
+
test('UPDATE_OPACITY action updates opacity for given layer [layers-reducer-update-opacity]', () => {
const response = layerReducer(initialState, {
type: UPDATE_OPACITY,
diff --git a/web/js/modules/layers/selectors.js b/web/js/modules/layers/selectors.js
index ab3e9b4304..94872a2aab 100644
--- a/web/js/modules/layers/selectors.js
+++ b/web/js/modules/layers/selectors.js
@@ -39,6 +39,7 @@ export function addLayer(id, spec = {}, layersParam, layerConfig, overlayLength,
def.squash = spec.squash || undefined;
def.disabled = spec.disabled || undefined;
def.count = spec.count || def.count || undefined;
+ def.size = spec.size || undefined;
if (Array.isArray(spec.bandCombo)) {
def.bandCombo = {
diff --git a/web/js/modules/layers/util.js b/web/js/modules/layers/util.js
index aaadc1f562..52343f7207 100644
--- a/web/js/modules/layers/util.js
+++ b/web/js/modules/layers/util.js
@@ -846,7 +846,7 @@ export function serializeLayers(layers, state, groupName) {
value: bandComboString,
});
}
- if (def.palette && (def.custom || def.min || def.max || def.squash || def.disabled || (palettes[def.id] && palettes[def.id].maps && palettes[def.id].maps.length > 1))) {
+ if (def.palette && (def.custom || def.min || def.max || def.squash || def.disabled || def.size || (palettes[def.id] && palettes[def.id].maps && palettes[def.id].maps.length > 1))) {
// If layer has palette and palette attributes
const paletteAttributeArray = getPaletteAttributeArray(
def.id,
@@ -913,6 +913,7 @@ const getLayerSpec = (attributes) => {
let custom;
let disabled;
let count;
+ let size;
let bandCombo;
lodashEach(attributes, (attr) => {
@@ -1008,6 +1009,9 @@ const getLayerSpec = (attributes) => {
if (attr.id === 'count') {
count = Number(attr.value);
}
+ if (attr.id === 'size') {
+ size = attr.value;
+ }
});
return {
@@ -1015,6 +1019,7 @@ const getLayerSpec = (attributes) => {
opacity,
count,
bandCombo,
+ size,
// only include palette attributes if Array.length condition
// is true: https://stackoverflow.com/a/40560953/4589331
diff --git a/web/js/modules/map/util.js b/web/js/modules/map/util.js
index 3a91177384..ce87475a06 100644
--- a/web/js/modules/map/util.js
+++ b/web/js/modules/map/util.js
@@ -337,6 +337,9 @@ export async function promiseImageryForTour(state, layers, dateString, activeStr
if (layer.squash) {
keys.push('squash');
}
+ if (layer.size) {
+ keys.push(`size=${layer.size}`);
+ }
if (keys.length > 0) {
options.style = keys.join(',');
}
diff --git a/web/js/modules/palettes/actions.js b/web/js/modules/palettes/actions.js
index 06060065a3..b780e1f29d 100644
--- a/web/js/modules/palettes/actions.js
+++ b/web/js/modules/palettes/actions.js
@@ -8,6 +8,8 @@ import {
SET_THRESHOLD_RANGE_AND_SQUASH,
CLEAR_CUSTOM,
SET_CUSTOM,
+ SET_SIZE,
+ CLEAR_SIZE,
SET_DISABLED_CLASSIFICATION,
LOADED_CUSTOM_PALETTES,
} from './constants';
@@ -17,6 +19,8 @@ import {
clearCustomSelector,
refreshDisabledSelector,
setDisabledSelector,
+ setSize as setSizeSelector,
+ clearSize as clearSizeSelector,
} from './selectors';
/**
@@ -122,6 +126,46 @@ export function clearCustomPalette(layerId, index, groupName) {
};
}
+export function setSize(layerId, size, index, groupName) {
+ return (dispatch, getState) => {
+ const state = getState();
+ const newActivePalettesObj = setSizeSelector(
+ layerId,
+ size,
+ index,
+ state.palettes[groupName],
+ state,
+ );
+ dispatch({
+ type: SET_SIZE,
+ groupName,
+ activeString: groupName,
+ layerId,
+ palettes: newActivePalettesObj,
+ size,
+ });
+ };
+}
+
+export function clearSize(layerId, index, groupName) {
+ return (dispatch, getState) => {
+ const state = getState();
+ const newActivePalettesObj = clearSizeSelector(
+ layerId,
+ index,
+ state.palettes[groupName],
+ state,
+ );
+ dispatch({
+ type: CLEAR_SIZE,
+ groupName,
+ activeString: groupName,
+ layerId,
+ palettes: newActivePalettesObj,
+ });
+ };
+}
+
export function setToggledClassification(layerId, classIndex, index, groupName) {
return (dispatch, getState) => {
const state = getState();
@@ -187,6 +231,9 @@ export function clearCustoms() {
if (colormap.disabled) {
dispatch(setToggledClassification(key, undefined, index, groupName));
}
+ if (colormap.size) {
+ dispatch(clearSize(key, index, groupName));
+ }
});
});
};
@@ -244,6 +291,9 @@ export function refreshPalettes(activePalettes) {
if (colormap.disabled) {
dispatch(refreshDisabledClassification(key, colormap.disabled, index, groupName));
}
+ if (colormap.size) {
+ dispatch(setSize(key, colormap.size, index, groupName));
+ }
});
});
};
diff --git a/web/js/modules/palettes/actions.test.js b/web/js/modules/palettes/actions.test.js
index 01a1516ad0..8ea4faf511 100644
--- a/web/js/modules/palettes/actions.test.js
+++ b/web/js/modules/palettes/actions.test.js
@@ -9,6 +9,7 @@ import {
setThresholdRangeAndSquash,
setCustomPalette,
clearCustomPalette,
+ setSize,
} from './actions';
import { addLayer } from '../layers/selectors';
import {
@@ -18,6 +19,7 @@ import {
SET_THRESHOLD_RANGE_AND_SQUASH,
SET_CUSTOM,
CLEAR_CUSTOM,
+ SET_SIZE,
} from './constants';
import fixtures from '../../fixtures';
@@ -256,4 +258,15 @@ describe('Test lookup actions [palettes-actions-lookup]', () => {
);
},
);
+ test(`test ${setSize} action [palettes-actions-set-size]`, () => {
+ const store = mockStore(stateWithLayers);
+ store.dispatch(setSize('terra-aod', 15, 0, 'active'));
+ const response = store.getActions()[0];
+
+ expect(response.type).toEqual(SET_SIZE);
+ expect(response.size).toEqual(15);
+ expect(response.groupName).toEqual('active');
+ expect(response.layerId).toEqual('terra-aod');
+ expect(response.activeString).toEqual('active');
+ });
});
diff --git a/web/js/modules/palettes/constants.js b/web/js/modules/palettes/constants.js
index 0fed270830..7cb30ee316 100644
--- a/web/js/modules/palettes/constants.js
+++ b/web/js/modules/palettes/constants.js
@@ -8,6 +8,8 @@ export const SET_THRESHOLD_RANGE_AND_SQUASH = 'PALETTES/SET_THRESHOLD_RANGE_AND_
export const CLEAR_CUSTOM = 'PALETTES/CLEAR_CUSTOM';
export const CLEAR_CUSTOMS = 'PALETTES/CLEAR_CUSTOMS';
export const SET_CUSTOM = 'PALETTES/SET_CUSTOM';
+export const SET_SIZE = 'PALETTES/SET_SIZE';
+export const CLEAR_SIZE = 'PALETTES/CLEAR_SIZE';
export const LOADED_CUSTOM_PALETTES = 'PALETTES/LOADED_CUSTOM_PALETTES';
export const BULK_PALETTE_RENDERING_SUCCESS = 'PALETTES/BULK_PALETTE_RENDERING_SUCCESS';
export const BULK_PALETTE_PRELOADING_SUCCESS = 'PALETTES/BULK_PALETTE_PRELOADING_SUCCESS';
@@ -18,5 +20,6 @@ export const PALETTE_STRINGS_PERMALINK_ARRAY = [
'min',
'max',
'disabled',
+ 'size',
];
-export const CUSTOM_PALETTE_TYPE_ARRAY = ['custom', 'squash', 'min', 'max', 'disabled'];
+export const CUSTOM_PALETTE_TYPE_ARRAY = ['custom', 'squash', 'min', 'max', 'disabled', 'size'];
diff --git a/web/js/modules/palettes/reducers.js b/web/js/modules/palettes/reducers.js
index 1bc7f31ade..2f1bd12105 100644
--- a/web/js/modules/palettes/reducers.js
+++ b/web/js/modules/palettes/reducers.js
@@ -15,6 +15,8 @@ import {
BULK_PALETTE_PRELOADING_SUCCESS,
CLEAR_CUSTOM,
SET_DISABLED_CLASSIFICATION,
+ SET_SIZE,
+ CLEAR_SIZE,
} from './constants';
import { INIT_SECOND_LAYER_GROUP } from '../layers/constants';
@@ -70,6 +72,8 @@ export function paletteReducer(state = defaultPaletteState, action) {
case SET_CUSTOM:
case SET_DISABLED_CLASSIFICATION:
case CLEAR_CUSTOM:
+ case SET_SIZE:
+ case CLEAR_SIZE:
return lodashAssign({}, state, {
[groupName]: action.palettes || {},
});
diff --git a/web/js/modules/palettes/selectors.js b/web/js/modules/palettes/selectors.js
index e3962bdba0..fb1ea71f80 100644
--- a/web/js/modules/palettes/selectors.js
+++ b/web/js/modules/palettes/selectors.js
@@ -134,7 +134,7 @@ const useLookup = function(layerId, palettesObj, state) {
use = true;
return false;
}
- } else if (palette.legend.colors.length > 1) {
+ } else if (palette.legend.colors.length > 1 || !lodashIsUndefined(palette.size)) {
use = true;
}
});
@@ -364,6 +364,9 @@ export function getKey(layerId, groupStr, state) {
if (def.squash) {
keys.push('squash');
}
+ if (def.size) {
+ keys.push(`size=${def.size}`);
+ }
return keys.join(',');
}
@@ -497,6 +500,39 @@ export function clearCustomSelector(layerId, index, palettes, state) {
return updateLookup(layerId, newPalettes, state);
}
+export function setSize(layerId, size, index, palettes, state) {
+ let newPalettes = prepare(layerId, palettes, state);
+ newPalettes = update(newPalettes, {
+ [layerId]: {
+ maps: {
+ [index]: {
+ $merge: {
+ size,
+ },
+ },
+ },
+ },
+ });
+ return updateLookup(layerId, newPalettes, state);
+}
+
+export function clearSize(layerId, index, palettes, state) {
+ index = lodashIsUndefined(index) ? 0 : index;
+ const active = palettes[layerId];
+ if (!active) {
+ return palettes;
+ }
+ const palette = active.maps[index];
+ if (!palette.size) {
+ return palettes;
+ }
+ delete palette.size;
+ const newPalettes = update(palettes, {
+ [layerId]: { maps: { [index]: { $set: palette } } },
+ }); // remove size key
+ return updateLookup(layerId, newPalettes, state);
+}
+
export function isPaletteAllowed(layerId, config) {
const { palette } = config.layers[layerId];
if (!palette || palette.immutable) {
diff --git a/web/js/modules/palettes/util.js b/web/js/modules/palettes/util.js
index 16fdecf4d9..ab9f910583 100644
--- a/web/js/modules/palettes/util.js
+++ b/web/js/modules/palettes/util.js
@@ -16,6 +16,7 @@ import {
setRange as setRangeSelector,
findIndex as findPaletteExtremeIndex,
initDisabledSelector,
+ setSize,
} from './selectors';
import util from '../../util/util';
@@ -290,6 +291,7 @@ export function getPaletteAttributeArray(layerId, palettes, state) {
let maxObj = lodashAssign({}, { key: 'max', array: [] }, DEFAULT_OBJ);
let squashObj = lodashAssign({}, { key: 'squash', array: [] }, DEFAULT_OBJ);
let disabledObj = lodashAssign({}, { key: 'disabled', array: [] }, DEFAULT_OBJ);
+ let sizeObj = lodashAssign({}, { key: 'size', array: [] }, DEFAULT_OBJ);
const attrArray = [];
for (let i = 0; i < count; i += 1) {
if (!palettes[layerId].maps[i]) {
@@ -309,6 +311,7 @@ export function getPaletteAttributeArray(layerId, palettes, state) {
const disabledValue = paletteDef.disabled && paletteDef.disabled.length
? paletteDef.disabled.join('-')
: undefined;
+ const sizeValue = paletteDef.size || undefined;
palObj = createPaletteAttributeObject(
paletteDef,
@@ -341,9 +344,15 @@ export function getPaletteAttributeArray(layerId, palettes, state) {
disabledObj,
count,
);
+ sizeObj = createPaletteAttributeObject(
+ paletteDef,
+ sizeValue,
+ sizeObj,
+ count,
+ );
}
- [palObj, minObj, maxObj, squashObj, disabledObj].forEach((obj) => {
+ [palObj, minObj, maxObj, squashObj, disabledObj, sizeObj].forEach((obj) => {
if (obj.isActive || (obj.key === 'disabled' && obj.value !== '')) {
attrArray.push({
id: obj.key === 'custom' ? 'palette' : obj.key,
@@ -450,10 +459,26 @@ export function loadPalettes(permlinkState, state) {
palettes: { [stateObj.groupStr]: { $set: newPalettes } },
});
} catch (error) {
- console.warn(` Invalid palette: ${value}`);
+ console.warn(`Invalid palette: ${value}`);
}
});
}
+ if (layerDef.size) {
+ try {
+ const newPalettes = setSize(
+ layerId,
+ layerDef.size,
+ 0,
+ state.palettes[stateObj.groupStr],
+ state,
+ );
+ state = update(state, {
+ palettes: { [stateObj.groupStr]: { $set: newPalettes } },
+ });
+ } catch (error) {
+ console.warn(`Unable to set point size: ${layerDef.size}`);
+ }
+ }
if (min.length > 0 || max.length > 0) {
count = getCount(layerId, state);
for (let i = 0; i < count; i += 1) {
diff --git a/web/js/modules/vector-styles/selectors.js b/web/js/modules/vector-styles/selectors.js
index 40c59b6476..e1e389dbf3 100644
--- a/web/js/modules/vector-styles/selectors.js
+++ b/web/js/modules/vector-styles/selectors.js
@@ -299,6 +299,9 @@ export function clearStyleFunction(def, vectorStyleId, vectorStyles, layer, stat
export const applyStyle = (def, olVectorLayer, state, options) => {
const { config } = state;
const { vectorStyles } = config;
+ if (def.size && def.size >= 5) {
+ def.vectorStyle.id += `_size${def.size}`;
+ }
const vectorStyleId = def.vectorStyle.id;
if (!vectorStyles || !vectorStyleId) {
diff --git a/web/scss/features/sidebar.scss b/web/scss/features/sidebar.scss
index 0e15394cec..4d040cc763 100644
--- a/web/scss/features/sidebar.scss
+++ b/web/scss/features/sidebar.scss
@@ -912,6 +912,7 @@ p.recharts-tooltip-label {
}
.wv-label-opacity,
+ .wv-label-size,
.wv-label-granule.count {
text-align: right;
}