- {program ? (
+ {eventHeatmap ? (
+
+ ) : program ? (
@@ -559,6 +579,7 @@ EventDialog.propTypes = {
eventClustering: PropTypes.bool,
eventCoordinateField: PropTypes.string,
eventCoordinateFieldType: PropTypes.string,
+ eventHeatmap: PropTypes.bool,
eventPointColor: PropTypes.string,
eventPointRadius: PropTypes.number,
eventStatus: PropTypes.string,
diff --git a/src/components/layers/overlays/HeatSlider.jsx b/src/components/layers/overlays/HeatSlider.jsx
new file mode 100644
index 0000000000..6074df4cb0
--- /dev/null
+++ b/src/components/layers/overlays/HeatSlider.jsx
@@ -0,0 +1,45 @@
+import PropTypes from 'prop-types'
+import React, { useCallback } from 'react'
+import styles from './styles/HeatSlider.module.css'
+
+const HeatSlider = ({ heat, disabled, onChange }) => {
+ const lowerFill = `var(--colors-grey${disabled ? 400 : 600})`
+ const upperFill = `var(--colors-grey${disabled ? 300 : 400})`
+
+ const onSliderChange = useCallback(
+ (evt) => onChange(Number(evt.target.value)),
+ [onChange]
+ )
+
+ return (
+
+
+
+ )
+}
+
+HeatSlider.propTypes = {
+ heat: PropTypes.number.isRequired,
+ onChange: PropTypes.func.isRequired,
+ disabled: PropTypes.bool,
+}
+
+export default HeatSlider
diff --git a/src/components/layers/overlays/OverlayCard.jsx b/src/components/layers/overlays/OverlayCard.jsx
index a490f28cda..1f85d270d5 100644
--- a/src/components/layers/overlays/OverlayCard.jsx
+++ b/src/components/layers/overlays/OverlayCard.jsx
@@ -11,6 +11,8 @@ import {
removeLayer,
duplicateLayer,
changeLayerOpacity,
+ changeLayerIntensity,
+ changeLayerRadius,
toggleLayerExpand,
toggleLayerVisibility,
} from '../../../actions/layers.js'
@@ -34,6 +36,7 @@ import Legend from '../../legend/Legend.jsx'
import LegendAlert from '../../legend/LegendAlert.jsx'
import DataDownloadDialog from '../download/DataDownloadDialog.jsx'
import LayerCard from '../LayerCard.jsx'
+import HeatSlider from './HeatSlider.jsx'
import styles from './styles/OverlayCard.module.css'
const OverlayCard = ({
@@ -42,6 +45,8 @@ const OverlayCard = ({
removeLayer,
duplicateLayer,
changeLayerOpacity,
+ changeLayerIntensity,
+ changeLayerRadius,
toggleLayerExpand,
toggleLayerVisibility,
toggleDataTable,
@@ -57,6 +62,8 @@ const OverlayCard = ({
legend,
isExpanded = true,
opacity,
+ heatIntensity = 0.5,
+ heatRadius = 0.5,
isVisible,
layer: layerType,
isLoaded,
@@ -85,6 +92,58 @@ const OverlayCard = ({
legend && (
+ {layer.eventHeatmap && (
+
+
+
+ Intensity:
+
+
+ changeLayerIntensity(id, newIntensity)
+ }
+ disabled={false}
+ />
+
+
+
+ Radius:
+
+
+ changeLayerRadius(id, newRadius)
+ }
+ disabled={false}
+ />
+
+
+ )}
)
)
@@ -155,7 +214,9 @@ const OverlayCard = ({
}
OverlayCard.propTypes = {
+ changeLayerIntensity: PropTypes.func.isRequired,
changeLayerOpacity: PropTypes.func.isRequired,
+ changeLayerRadius: PropTypes.func.isRequired,
duplicateLayer: PropTypes.func.isRequired,
editLayer: PropTypes.func.isRequired,
layer: PropTypes.object.isRequired,
@@ -170,6 +231,8 @@ export default connect(null, {
removeLayer,
duplicateLayer,
changeLayerOpacity,
+ changeLayerIntensity,
+ changeLayerRadius,
toggleLayerExpand,
toggleLayerVisibility,
toggleDataTable,
diff --git a/src/components/layers/overlays/styles/HeatSlider.module.css b/src/components/layers/overlays/styles/HeatSlider.module.css
new file mode 100644
index 0000000000..3849198964
--- /dev/null
+++ b/src/components/layers/overlays/styles/HeatSlider.module.css
@@ -0,0 +1,56 @@
+.container {
+ width: 100px;
+ height: 28px;
+}
+
+.slider {
+ width: 100%;
+ -webkit-appearance: none;
+ appearance: none;
+ height: 2px;
+ transition: background 450ms ease-in;
+ outline: none;
+ border-radius: 2px;
+ cursor: pointer;
+}
+
+.slider:disabled {
+ cursor: not-allowed;
+}
+
+.slider::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 12px;
+ height: 12px;
+ background: var(--colors-grey700);
+ border-radius: 50%;
+}
+
+.slider::-moz-range-thumb {
+ width: 12px;
+ height: 12px;
+ background: var(--colors-grey700);
+ border: 0;
+ border-radius: 50%;
+}
+
+.slider::-webkit-slider-thumb:hover {
+ background: var(--colors-grey900);
+}
+
+.slider::-moz-range-thumb:hover {
+ background: var(--colors-grey900);
+}
+
+.slider::-moz-focus-outer {
+ border: 0;
+}
+
+.slider:disabled::-webkit-slider-thumb {
+ background: var(--colors-grey400);
+}
+
+.slider:disabled::-moz-range-thumb {
+ background: var(--colors-grey400);
+}
diff --git a/src/components/map/layers/EventLayer.jsx b/src/components/map/layers/EventLayer.jsx
index b132e09e74..daeb1bd8ca 100644
--- a/src/components/map/layers/EventLayer.jsx
+++ b/src/components/map/layers/EventLayer.jsx
@@ -38,10 +38,13 @@ class EventLayer extends Layer {
id,
index,
opacity,
+ heatIntensity,
+ heatRadius,
isVisible,
bounds,
data,
engine,
+ eventHeatmap,
eventClustering,
eventCoordinateField,
eventPointColor,
@@ -62,6 +65,8 @@ class EventLayer extends Layer {
labelFontSize,
labelFontWeight,
labelFontStyle,
+ classes,
+ colorScale,
} = this.props
const analyticsEngine = Analytics.getAnalytics(engine)
@@ -122,6 +127,8 @@ class EventLayer extends Layer {
id,
index,
opacity,
+ heatIntensity,
+ heatRadius,
isVisible,
data: labeledData,
fillColor,
@@ -155,6 +162,9 @@ class EventLayer extends Layer {
engine,
analyticsEngine,
geometryCentroid,
+ eventHeatmap,
+ classes,
+ colorScale,
})
if (program && programStage) {
@@ -194,6 +204,9 @@ class EventLayer extends Layer {
engine,
analyticsEngine,
geometryCentroid,
+ eventHeatmap,
+ classes,
+ colorScale,
}
) {
let eventRequest
@@ -238,6 +251,23 @@ class EventLayer extends Layer {
config.type = 'clientCluster'
}
}
+ } else if (eventHeatmap) {
+ config.type = 'heat'
+ config.heatWeight = 1
+
+ const step = 1 / classes
+ const colorScaleReady = colorScale.flatMap((heatColor, i) => [
+ (i + 1) * step,
+ heatColor,
+ ])
+ config.heatColor = [
+ 'interpolate',
+ ['linear'],
+ ['heatmap-density'],
+ 0,
+ 'rgba(33,102,172,0)',
+ ...colorScaleReady,
+ ]
} else if (areaRadius) {
config.buffer = areaRadius
config.bufferStyle = {
diff --git a/src/components/map/layers/Layer.js b/src/components/map/layers/Layer.js
index c8bff13635..f274536fac 100644
--- a/src/components/map/layers/Layer.js
+++ b/src/components/map/layers/Layer.js
@@ -21,6 +21,8 @@ class Layer extends PureComponent {
editCounter: PropTypes.number,
externalPeriod: PropTypes.object, // eslint-disable-line react/no-unused-prop-types
feature: PropTypes.object,
+ heatIntensity: PropTypes.number,
+ heatRadius: PropTypes.number,
index: PropTypes.number,
isVisible: PropTypes.bool,
opacity: PropTypes.number,
@@ -29,6 +31,8 @@ class Layer extends PureComponent {
static defaultProps = {
opacity: 1,
+ heatIntensity: 0.5,
+ heatRadius: 0.5,
isVisible: true,
}
@@ -46,6 +50,8 @@ class Layer extends PureComponent {
data,
index,
opacity,
+ heatIntensity,
+ heatRadius,
isVisible,
editCounter,
dataFilters,
@@ -79,6 +85,14 @@ class Layer extends PureComponent {
this.setLayerOpacity()
}
+ if (heatIntensity !== prevProps.heatIntensity) {
+ this.setLayerIntensity()
+ }
+
+ if (heatRadius !== prevProps.heatRadius) {
+ this.setLayerRadius()
+ }
+
if (isVisible !== prevProps.isVisible) {
this.setLayerVisibility()
}
@@ -94,7 +108,15 @@ class Layer extends PureComponent {
// Create new layer from config object (override in subclasses)
async createLayer() {
- const { id, index = 0, config, opacity, isVisible } = this.props
+ const {
+ id,
+ index = 0,
+ config,
+ opacity,
+ heatIntensity,
+ heatRadius,
+ isVisible,
+ } = this.props
const { map } = this.context
this.layer = map.createLayer({
@@ -102,6 +124,8 @@ class Layer extends PureComponent {
id,
index,
opacity,
+ heatIntensity,
+ heatRadius,
isVisible,
})
@@ -131,6 +155,14 @@ class Layer extends PureComponent {
this.layer.setOpacity(this.props.opacity)
}
+ setLayerIntensity() {
+ this.layer.setIntensity(this.props.heatIntensity)
+ }
+
+ setLayerRadius() {
+ this.layer.setRadius(this.props.heatRadius)
+ }
+
setLayerOrder() {
if (this.layer) {
this.layer.setIndex(this.props.index)
diff --git a/src/constants/actionTypes.js b/src/constants/actionTypes.js
index 04bc55e885..918707340e 100644
--- a/src/constants/actionTypes.js
+++ b/src/constants/actionTypes.js
@@ -24,6 +24,8 @@ export const LAYER_ADD = 'LAYER_ADD'
export const LAYER_REMOVE = 'LAYER_REMOVE'
export const LAYER_DUPLICATE = 'LAYER_DUPLICATE'
export const LAYER_CHANGE_OPACITY = 'LAYER_CHANGE_OPACITY'
+export const LAYER_CHANGE_INTENSITY = 'LAYER_CHANGE_INTENSITY'
+export const LAYER_CHANGE_RADIUS = 'LAYER_CHANGE_RADIUS'
export const LAYER_EDIT = 'LAYER_EDIT'
export const LAYER_EDIT_SUCCESS = 'LAYER_EDIT_SUCCESS'
export const LAYER_CANCEL = 'LAYER_CANCEL'
@@ -92,6 +94,7 @@ export const LAYER_EDIT_DATA_ITEM_SET = 'LAYER_EDIT_DATA_ITEM_SET'
export const LAYER_EDIT_EVENT_STATUS_SET = 'LAYER_EDIT_EVENT_STATUS_SET'
export const LAYER_EDIT_EVENT_COORDINATE_FIELD_SET =
'LAYER_EDIT_EVENT_COORDINATE_FIELD_SET'
+export const LAYER_EDIT_EVENT_HEATMAP_SET = 'LAYER_EDIT_EVENT_HEATMAP_SET'
export const LAYER_EDIT_EVENT_CLUSTERING_SET = 'LAYER_EDIT_EVENT_CLUSTERING_SET'
export const LAYER_EDIT_EVENT_POINT_RADIUS_SET =
'LAYER_EDIT_EVENT_POINT_RADIUS_SET'
diff --git a/src/constants/colorbrewer.js b/src/constants/colorbrewer.js
index 687cf637dd..bc747e6332 100644
--- a/src/constants/colorbrewer.js
+++ b/src/constants/colorbrewer.js
@@ -1520,6 +1520,915 @@ const colorbrewer = {
'#ffed6f',
],
},
+ YlOrBr_reverse: {
+ 3: ['#d95f0e', '#fec44f', '#fff7bc'],
+ 4: ['#cc4c02', '#fe9929', '#fed98e', '#ffffd4'],
+ 5: ['#993404', '#d95f0e', '#fe9929', '#fed98e', '#ffffd4'],
+ 6: ['#993404', '#d95f0e', '#fe9929', '#fec44f', '#fee391', '#ffffd4'],
+ 7: [
+ '#8c2d04',
+ '#cc4c02',
+ '#ec7014',
+ '#fe9929',
+ '#fec44f',
+ '#fee391',
+ '#ffffd4',
+ ],
+ 8: [
+ '#8c2d04',
+ '#cc4c02',
+ '#ec7014',
+ '#fe9929',
+ '#fec44f',
+ '#fee391',
+ '#fff7bc',
+ '#ffffe5',
+ ],
+ 9: [
+ '#662506',
+ '#993404',
+ '#cc4c02',
+ '#ec7014',
+ '#fe9929',
+ '#fec44f',
+ '#fee391',
+ '#fff7bc',
+ '#ffffe5',
+ ],
+ },
+ Reds_reverse: {
+ 3: ['#de2d26', '#fc9272', '#fee0d2'],
+ 4: ['#cb181d', '#fb6a4a', '#fcae91', '#fee5d9'],
+ 5: ['#a50f15', '#de2d26', '#fb6a4a', '#fcae91', '#fee5d9'],
+ 6: ['#a50f15', '#de2d26', '#fb6a4a', '#fc9272', '#fcbba1', '#fee5d9'],
+ 7: [
+ '#99000d',
+ '#cb181d',
+ '#ef3b2c',
+ '#fb6a4a',
+ '#fc9272',
+ '#fcbba1',
+ '#fee5d9',
+ ],
+ 8: [
+ '#99000d',
+ '#cb181d',
+ '#ef3b2c',
+ '#fb6a4a',
+ '#fc9272',
+ '#fcbba1',
+ '#fee0d2',
+ '#fff5f0',
+ ],
+ 9: [
+ '#67000d',
+ '#a50f15',
+ '#cb181d',
+ '#ef3b2c',
+ '#fb6a4a',
+ '#fc9272',
+ '#fcbba1',
+ '#fee0d2',
+ '#fff5f0',
+ ],
+ },
+ YlGn_reverse: {
+ 3: ['#31a354', '#addd8e', '#f7fcb9'],
+ 4: ['#238443', '#78c679', '#c2e699', '#ffffcc'],
+ 5: ['#006837', '#31a354', '#78c679', '#c2e699', '#ffffcc'],
+ 6: ['#006837', '#31a354', '#78c679', '#addd8e', '#d9f0a3', '#ffffcc'],
+ 7: [
+ '#005a32',
+ '#238443',
+ '#41ab5d',
+ '#78c679',
+ '#addd8e',
+ '#d9f0a3',
+ '#ffffcc',
+ ],
+ 8: [
+ '#005a32',
+ '#238443',
+ '#41ab5d',
+ '#78c679',
+ '#addd8e',
+ '#d9f0a3',
+ '#f7fcb9',
+ '#ffffe5',
+ ],
+ 9: [
+ '#004529',
+ '#006837',
+ '#238443',
+ '#41ab5d',
+ '#78c679',
+ '#addd8e',
+ '#d9f0a3',
+ '#f7fcb9',
+ '#ffffe5',
+ ],
+ },
+ Purples_reverse: {
+ 3: ['#756bb1', '#bcbddc', '#efedf5'],
+ 4: ['#6a51a3', '#9e9ac8', '#cbc9e2', '#f2f0f7'],
+ 5: ['#54278f', '#756bb1', '#9e9ac8', '#cbc9e2', '#f2f0f7'],
+ 6: ['#54278f', '#756bb1', '#9e9ac8', '#bcbddc', '#dadaeb', '#f2f0f7'],
+ 7: [
+ '#4a1486',
+ '#6a51a3',
+ '#807dba',
+ '#9e9ac8',
+ '#bcbddc',
+ '#dadaeb',
+ '#f2f0f7'
+ ],
+ 8: [
+ '#4a1486',
+ '#6a51a3',
+ '#807dba',
+ '#9e9ac8',
+ '#bcbddc',
+ '#dadaeb',
+ '#efedf5',
+ '#fcfbfd'
+ ],
+ 9: [
+ '#3f007d',
+ '#54278f',
+ '#6a51a3',
+ '#807dba',
+ '#9e9ac8',
+ '#bcbddc',
+ '#dadaeb',
+ '#efedf5',
+ '#fcfbfd'
+ ]
+ },
+ Greens_reverse: {
+ 3: ['#31a354', '#a1d99b', '#e5f5e0'],
+ 4: ['#238b45', '#74c476', '#bae4b3', '#edf8e9'],
+ 5: ['#006d2c', '#31a354', '#74c476', '#bae4b3', '#edf8e9'],
+ 6: ['#006d2c', '#31a354', '#74c476', '#a1d99b', '#c7e9c0', '#edf8e9'],
+ 7: [
+ '#005a32',
+ '#238b45',
+ '#41ab5d',
+ '#74c476',
+ '#a1d99b',
+ '#c7e9c0',
+ '#edf8e9',
+ ],
+ 8: [
+ '#005a32',
+ '#238b45',
+ '#41ab5d',
+ '#74c476',
+ '#a1d99b',
+ '#c7e9c0',
+ '#e5f5e0',
+ '#f7fcf5',
+ ],
+ 9: [
+ '#00441b',
+ '#006d2c',
+ '#238b45',
+ '#41ab5d',
+ '#74c476',
+ '#a1d99b',
+ '#c7e9c0',
+ '#e5f5e0',
+ '#f7fcf5',
+ ],
+ },
+ Blues_reverse: {
+ 3: ['#3182bd', '#9ecae1', '#deebf7'],
+ 4: ['#2171b5', '#6baed6', '#bdd7e7', '#eff3ff'],
+ 5: ['#08519c', '#3182bd', '#6baed6', '#bdd7e7', '#eff3ff'],
+ 6: ['#08519c', '#3182bd', '#6baed6', '#9ecae1', '#c6dbef', '#eff3ff'],
+ 7: [
+ '#084594',
+ '#2171b5',
+ '#4292c6',
+ '#6baed6',
+ '#9ecae1',
+ '#c6dbef',
+ '#eff3ff',
+ ],
+ 8: [
+ '#084594',
+ '#2171b5',
+ '#4292c6',
+ '#6baed6',
+ '#9ecae1',
+ '#c6dbef',
+ '#deebf7',
+ '#f7fbff',
+ ],
+ 9: [
+ '#08306b',
+ '#08519c',
+ '#2171b5',
+ '#4292c6',
+ '#6baed6',
+ '#9ecae1',
+ '#c6dbef',
+ '#deebf7',
+ '#f7fbff',
+ ],
+ },
+ BuPu_reverse: {
+ 3: ['#8856a7', '#9ebcda', '#e0ecf4'],
+ 4: ['#88419d', '#8c96c6', '#b3cde3', '#edf8fb'],
+ 5: ['#810f7c', '#8856a7', '#8c96c6', '#b3cde3', '#edf8fb'],
+ 6: ['#810f7c', '#8856a7', '#8c96c6', '#9ebcda', '#bfd3e6', '#edf8fb'],
+ 7: [
+ '#6e016b',
+ '#88419d',
+ '#8c6bb1',
+ '#8c96c6',
+ '#9ebcda',
+ '#bfd3e6',
+ '#edf8fb',
+ ],
+ 8: [
+ '#6e016b',
+ '#88419d',
+ '#8c6bb1',
+ '#8c96c6',
+ '#9ebcda',
+ '#bfd3e6',
+ '#e0ecf4',
+ '#f7fcfd',
+ ],
+ 9: [
+ '#4d004b',
+ '#810f7c',
+ '#88419d',
+ '#8c6bb1',
+ '#8c96c6',
+ '#9ebcda',
+ '#bfd3e6',
+ '#e0ecf4',
+ '#f7fcfd',
+ ],
+ },
+ RdPu_reverse: {
+ 3: ['#c51b8a', '#fa9fb5', '#fde0dd'],
+ 4: ['#ae017e', '#f768a1', '#fbb4b9', '#feebe2'],
+ 5: ['#7a0177', '#c51b8a', '#f768a1', '#fbb4b9', '#feebe2'],
+ 6: ['#7a0177', '#c51b8a', '#f768a1', '#fa9fb5', '#fcc5c0', '#feebe2'],
+ 7: [
+ '#7a0177',
+ '#ae017e',
+ '#dd3497',
+ '#f768a1',
+ '#fa9fb5',
+ '#fcc5c0',
+ '#feebe2',
+ ],
+ 8: [
+ '#7a0177',
+ '#ae017e',
+ '#dd3497',
+ '#f768a1',
+ '#fa9fb5',
+ '#fcc5c0',
+ '#fde0dd',
+ '#fff7f3',
+ ],
+ 9: [
+ '#49006a',
+ '#7a0177',
+ '#ae017e',
+ '#dd3497',
+ '#f768a1',
+ '#fa9fb5',
+ '#fcc5c0',
+ '#fde0dd',
+ '#fff7f3',
+ ],
+ },
+ PuRd_reverse: {
+ 3: ['#dd1c77', '#c994c7', '#e7e1ef'],
+ 4: ['#ce1256', '#df65b0', '#d7b5d8', '#f1eef6'],
+ 5: ['#980043', '#dd1c77', '#df65b0', '#d7b5d8', '#f1eef6'],
+ 6: ['#980043', '#dd1c77', '#df65b0', '#c994c7', '#d4b9da', '#f1eef6'],
+ 7: [
+ '#91003f',
+ '#ce1256',
+ '#e7298a',
+ '#df65b0',
+ '#c994c7',
+ '#d4b9da',
+ '#f1eef6',
+ ],
+ 8: [
+ '#91003f',
+ '#ce1256',
+ '#e7298a',
+ '#df65b0',
+ '#c994c7',
+ '#d4b9da',
+ '#e7e1ef',
+ '#f7f4f9',
+ ],
+ 9: [
+ '#67001f',
+ '#980043',
+ '#ce1256',
+ '#e7298a',
+ '#df65b0',
+ '#c994c7',
+ '#d4b9da',
+ '#e7e1ef',
+ '#f7f4f9',
+ ],
+ },
+ Greys_reverse: {
+ 3: ['#636363', '#bdbdbd', '#f0f0f0'],
+ 4: ['#525252', '#969696', '#cccccc', '#f7f7f7'],
+ 5: ['#252525', '#636363', '#969696', '#cccccc', '#f7f7f7'],
+ 6: ['#252525', '#636363', '#969696', '#bdbdbd', '#d9d9d9', '#f7f7f7'],
+ 7: [
+ '#252525',
+ '#525252',
+ '#737373',
+ '#969696',
+ '#bdbdbd',
+ '#d9d9d9',
+ '#f7f7f7',
+ ],
+ 8: [
+ '#252525',
+ '#525252',
+ '#737373',
+ '#969696',
+ '#bdbdbd',
+ '#d9d9d9',
+ '#f0f0f0',
+ '#ffffff',
+ ],
+ 9: [
+ '#000000',
+ '#252525',
+ '#525252',
+ '#737373',
+ '#969696',
+ '#bdbdbd',
+ '#d9d9d9',
+ '#f0f0f0',
+ '#ffffff',
+ ],
+ },
+ PuOr_reverse: {
+ 3: ['#998ec3', '#f7f7f7', '#f1a340'],
+ 4: ['#5e3c99', '#b2abd2', '#fdb863', '#e66101'],
+ 5: ['#5e3c99', '#b2abd2', '#f7f7f7', '#fdb863', '#e66101'],
+ 6: ['#542788', '#998ec3', '#d8daeb', '#fee0b6', '#f1a340', '#b35806'],
+ 7: [
+ '#542788',
+ '#998ec3',
+ '#d8daeb',
+ '#f7f7f7',
+ '#fee0b6',
+ '#f1a340',
+ '#b35806',
+ ],
+ 8: [
+ '#542788',
+ '#8073ac',
+ '#b2abd2',
+ '#d8daeb',
+ '#fee0b6',
+ '#fdb863',
+ '#e08214',
+ '#b35806',
+ ],
+ 9: [
+ '#542788',
+ '#8073ac',
+ '#b2abd2',
+ '#d8daeb',
+ '#f7f7f7',
+ '#fee0b6',
+ '#fdb863',
+ '#e08214',
+ '#b35806',
+ ],
+ 10: [
+ '#2d004b',
+ '#542788',
+ '#8073ac',
+ '#b2abd2',
+ '#d8daeb',
+ '#fee0b6',
+ '#fdb863',
+ '#e08214',
+ '#b35806',
+ '#7f3b08',
+ ],
+ 11: [
+ '#2d004b',
+ '#542788',
+ '#8073ac',
+ '#b2abd2',
+ '#d8daeb',
+ '#f7f7f7',
+ '#fee0b6',
+ '#fdb863',
+ '#e08214',
+ '#b35806',
+ '#7f3b08',
+ ],
+ },
+ BrBG_reverse: {
+ 3: ['#5ab4ac', '#f5f5f5', '#d8b365'],
+ 4: ['#018571', '#80cdc1', '#dfc27d', '#a6611a'],
+ 5: ['#018571', '#80cdc1', '#f5f5f5', '#dfc27d', '#a6611a'],
+ 6: ['#01665e', '#5ab4ac', '#c7eae5', '#f6e8c3', '#d8b365', '#8c510a'],
+ 7: [
+ '#01665e',
+ '#5ab4ac',
+ '#c7eae5',
+ '#f5f5f5',
+ '#f6e8c3',
+ '#d8b365',
+ '#8c510a',
+ ],
+ 8: [
+ '#01665e',
+ '#35978f',
+ '#80cdc1',
+ '#c7eae5',
+ '#f6e8c3',
+ '#dfc27d',
+ '#bf812d',
+ '#8c510a',
+ ],
+ 9: [
+ '#01665e',
+ '#35978f',
+ '#80cdc1',
+ '#c7eae5',
+ '#f5f5f5',
+ '#f6e8c3',
+ '#dfc27d',
+ '#bf812d',
+ '#8c510a',
+ ],
+ 10: [
+ '#003c30',
+ '#01665e',
+ '#35978f',
+ '#80cdc1',
+ '#c7eae5',
+ '#f6e8c3',
+ '#dfc27d',
+ '#bf812d',
+ '#8c510a',
+ '#543005',
+ ],
+ 11: [
+ '#003c30',
+ '#01665e',
+ '#35978f',
+ '#80cdc1',
+ '#c7eae5',
+ '#f5f5f5',
+ '#f6e8c3',
+ '#dfc27d',
+ '#bf812d',
+ '#8c510a',
+ '#543005',
+ ],
+ },
+ PRGn_reverse: {
+ 3: ['#7fbf7b', '#f7f7f7', '#af8dc3'],
+ 4: ['#008837', '#a6dba0', '#c2a5cf', '#7b3294'],
+ 5: ['#008837', '#a6dba0', '#f7f7f7', '#c2a5cf', '#7b3294'],
+ 6: ['#1b7837', '#7fbf7b', '#d9f0d3', '#e7d4e8', '#af8dc3', '#762a83'],
+ 7: [
+ '#1b7837',
+ '#7fbf7b',
+ '#d9f0d3',
+ '#f7f7f7',
+ '#e7d4e8',
+ '#af8dc3',
+ '#762a83',
+ ],
+ 8: [
+ '#1b7837',
+ '#5aae61',
+ '#a6dba0',
+ '#d9f0d3',
+ '#e7d4e8',
+ '#c2a5cf',
+ '#9970ab',
+ '#762a83',
+ ],
+ 9: [
+ '#1b7837',
+ '#5aae61',
+ '#a6dba0',
+ '#d9f0d3',
+ '#f7f7f7',
+ '#e7d4e8',
+ '#c2a5cf',
+ '#9970ab',
+ '#762a83',
+ ],
+ 10: [
+ '#00441b',
+ '#1b7837',
+ '#5aae61',
+ '#a6dba0',
+ '#d9f0d3',
+ '#e7d4e8',
+ '#c2a5cf',
+ '#9970ab',
+ '#762a83',
+ '#40004b',
+ ],
+ 11: [
+ '#00441b',
+ '#1b7837',
+ '#5aae61',
+ '#a6dba0',
+ '#d9f0d3',
+ '#f7f7f7',
+ '#e7d4e8',
+ '#c2a5cf',
+ '#9970ab',
+ '#762a83',
+ '#40004b',
+ ],
+ },
+ PiYG_reverse: {
+ 3: ['#a1d76a', '#f7f7f7', '#e9a3c9'],
+ 4: ['#4dac26', '#b8e186', '#f1b6da', '#d01c8b'],
+ 5: ['#4dac26', '#b8e186', '#f7f7f7', '#f1b6da', '#d01c8b'],
+ 6: ['#4d9221', '#a1d76a', '#e6f5d0', '#fde0ef', '#e9a3c9', '#c51b7d'],
+ 7: [
+ '#4d9221',
+ '#a1d76a',
+ '#e6f5d0',
+ '#f7f7f7',
+ '#fde0ef',
+ '#e9a3c9',
+ '#c51b7d',
+ ],
+ 8: [
+ '#4d9221',
+ '#7fbc41',
+ '#b8e186',
+ '#e6f5d0',
+ '#fde0ef',
+ '#f1b6da',
+ '#de77ae',
+ '#c51b7d',
+ ],
+ 9: [
+ '#4d9221',
+ '#7fbc41',
+ '#b8e186',
+ '#e6f5d0',
+ '#f7f7f7',
+ '#fde0ef',
+ '#f1b6da',
+ '#de77ae',
+ '#c51b7d',
+ ],
+ 10: [
+ '#276419',
+ '#4d9221',
+ '#7fbc41',
+ '#b8e186',
+ '#e6f5d0',
+ '#fde0ef',
+ '#f1b6da',
+ '#de77ae',
+ '#c51b7d',
+ '#8e0152',
+ ],
+ 11: [
+ '#276419',
+ '#4d9221',
+ '#7fbc41',
+ '#b8e186',
+ '#e6f5d0',
+ '#f7f7f7',
+ '#fde0ef',
+ '#f1b6da',
+ '#de77ae',
+ '#c51b7d',
+ '#8e0152',
+ ],
+ },
+ RdBu_reverse: {
+ 3: ['#67a9cf', '#f7f7f7', '#ef8a62'],
+ 4: ['#0571b0', '#92c5de', '#f4a582', '#ca0020'],
+ 5: ['#0571b0', '#92c5de', '#f7f7f7', '#f4a582', '#ca0020'],
+ 6: ['#2166ac', '#67a9cf', '#d1e5f0', '#fddbc7', '#ef8a62', '#b2182b'],
+ 7: [
+ '#2166ac',
+ '#67a9cf',
+ '#d1e5f0',
+ '#f7f7f7',
+ '#fddbc7',
+ '#ef8a62',
+ '#b2182b',
+ ],
+ 8: [
+ '#2166ac',
+ '#4393c3',
+ '#92c5de',
+ '#d1e5f0',
+ '#fddbc7',
+ '#f4a582',
+ '#d6604d',
+ '#b2182b',
+ ],
+ 9: [
+ '#2166ac',
+ '#4393c3',
+ '#92c5de',
+ '#d1e5f0',
+ '#f7f7f7',
+ '#fddbc7',
+ '#f4a582',
+ '#d6604d',
+ '#b2182b',
+ ],
+ 10: [
+ '#053061',
+ '#2166ac',
+ '#4393c3',
+ '#92c5de',
+ '#d1e5f0',
+ '#fddbc7',
+ '#f4a582',
+ '#d6604d',
+ '#b2182b',
+ '#67001f',
+ ],
+ 11: [
+ '#053061',
+ '#2166ac',
+ '#4393c3',
+ '#92c5de',
+ '#d1e5f0',
+ '#f7f7f7',
+ '#fddbc7',
+ '#f4a582',
+ '#d6604d',
+ '#b2182b',
+ '#67001f',
+ ],
+ },
+ RdGy_reverse: {
+ 3: ['#999999', '#ffffff', '#ef8a62'],
+ 4: ['#404040', '#bababa', '#f4a582', '#ca0020'],
+ 5: ['#404040', '#bababa', '#ffffff', '#f4a582', '#ca0020'],
+ 6: ['#4d4d4d', '#999999', '#e0e0e0', '#fddbc7', '#ef8a62', '#b2182b'],
+ 7: [
+ '#4d4d4d',
+ '#999999',
+ '#e0e0e0',
+ '#ffffff',
+ '#fddbc7',
+ '#ef8a62',
+ '#b2182b',
+ ],
+ 8: [
+ '#4d4d4d',
+ '#878787',
+ '#bababa',
+ '#e0e0e0',
+ '#fddbc7',
+ '#f4a582',
+ '#d6604d',
+ '#b2182b',
+ ],
+ 9: [
+ '#4d4d4d',
+ '#878787',
+ '#bababa',
+ '#e0e0e0',
+ '#ffffff',
+ '#fddbc7',
+ '#f4a582',
+ '#d6604d',
+ '#b2182b',
+ ],
+ 10: [
+ '#1a1a1a',
+ '#4d4d4d',
+ '#878787',
+ '#bababa',
+ '#e0e0e0',
+ '#fddbc7',
+ '#f4a582',
+ '#d6604d',
+ '#b2182b',
+ '#67001f',
+ ],
+ 11: [
+ '#1a1a1a',
+ '#4d4d4d',
+ '#878787',
+ '#bababa',
+ '#e0e0e0',
+ '#ffffff',
+ '#fddbc7',
+ '#f4a582',
+ '#d6604d',
+ '#b2182b',
+ '#67001f',
+ ],
+ },
+ RdYlBu_reverse: {
+ 3: ['#91bfdb', '#ffffbf', '#fc8d59'],
+ 4: ['#2c7bb6', '#abd9e9', '#fdae61', '#d7191c'],
+ 5: ['#2c7bb6', '#abd9e9', '#ffffbf', '#fdae61', '#d7191c'],
+ 6: ['#4575b4', '#91bfdb', '#e0f3f8', '#fee090', '#fc8d59', '#d73027'],
+ 7: [
+ '#4575b4',
+ '#91bfdb',
+ '#e0f3f8',
+ '#ffffbf',
+ '#fee090',
+ '#fc8d59',
+ '#d73027',
+ ],
+ 8: [
+ '#4575b4',
+ '#74add1',
+ '#abd9e9',
+ '#e0f3f8',
+ '#fee090',
+ '#fdae61',
+ '#f46d43',
+ '#d73027',
+ ],
+ 9: [
+ '#4575b4',
+ '#74add1',
+ '#abd9e9',
+ '#e0f3f8',
+ '#ffffbf',
+ '#fee090',
+ '#fdae61',
+ '#f46d43',
+ '#d73027',
+ ],
+ 10: [
+ '#313695',
+ '#4575b4',
+ '#74add1',
+ '#abd9e9',
+ '#e0f3f8',
+ '#fee090',
+ '#fdae61',
+ '#f46d43',
+ '#d73027',
+ '#a50026',
+ ],
+ 11: [
+ '#313695',
+ '#4575b4',
+ '#74add1',
+ '#abd9e9',
+ '#e0f3f8',
+ '#ffffbf',
+ '#fee090',
+ '#fdae61',
+ '#f46d43',
+ '#d73027',
+ '#a50026',
+ ],
+ },
+ Spectral_reverse: {
+ 3: ['#99d594', '#ffffbf', '#fc8d59'],
+ 4: ['#2b83ba', '#abdda4', '#fdae61', '#d7191c'],
+ 5: ['#2b83ba', '#abdda4', '#ffffbf', '#fdae61', '#d7191c'],
+ 6: ['#3288bd', '#99d594', '#e6f598', '#fee08b', '#fc8d59', '#d53e4f'],
+ 7: [
+ '#3288bd',
+ '#99d594',
+ '#e6f598',
+ '#ffffbf',
+ '#fee08b',
+ '#fc8d59',
+ '#d53e4f',
+ ],
+ 8: [
+ '#3288bd',
+ '#66c2a5',
+ '#abdda4',
+ '#e6f598',
+ '#fee08b',
+ '#fdae61',
+ '#f46d43',
+ '#d53e4f',
+ ],
+ 9: [
+ '#3288bd',
+ '#66c2a5',
+ '#abdda4',
+ '#e6f598',
+ '#ffffbf',
+ '#fee08b',
+ '#fdae61',
+ '#f46d43',
+ '#d53e4f',
+ ],
+ 10: [
+ '#5e4fa2',
+ '#3288bd',
+ '#66c2a5',
+ '#abdda4',
+ '#e6f598',
+ '#fee08b',
+ '#fdae61',
+ '#f46d43',
+ '#d53e4f',
+ '#9e0142',
+ ],
+ 11: [
+ '#5e4fa2',
+ '#3288bd',
+ '#66c2a5',
+ '#abdda4',
+ '#e6f598',
+ '#ffffbf',
+ '#fee08b',
+ '#fdae61',
+ '#f46d43',
+ '#d53e4f',
+ '#9e0142',
+ ],
+ },
+ RdYlGn_reverse: {
+ 3: ['#91cf60', '#ffffbf', '#fc8d59'],
+ 4: ['#1a9641', '#a6d96a', '#fdae61', '#d7191c'],
+ 5: ['#1a9641', '#a6d96a', '#ffffbf', '#fdae61', '#d7191c'],
+ 6: ['#1a9850', '#91cf60', '#d9ef8b', '#fee08b', '#fc8d59', '#d73027'],
+ 7: [
+ '#1a9850',
+ '#91cf60',
+ '#d9ef8b',
+ '#ffffbf',
+ '#fee08b',
+ '#fc8d59',
+ '#d73027',
+ ],
+ 8: [
+ '#1a9850',
+ '#66bd63',
+ '#a6d96a',
+ '#d9ef8b',
+ '#fee08b',
+ '#fdae61',
+ '#f46d43',
+ '#d73027',
+ ],
+ 9: [
+ '#1a9850',
+ '#66bd63',
+ '#a6d96a',
+ '#d9ef8b',
+ '#ffffbf',
+ '#fee08b',
+ '#fdae61',
+ '#f46d43',
+ '#d73027',
+ ],
+ 10: [
+ '#006837',
+ '#1a9850',
+ '#66bd63',
+ '#a6d96a',
+ '#d9ef8b',
+ '#fee08b',
+ '#fdae61',
+ '#f46d43',
+ '#d73027',
+ '#a50026',
+ ],
+ 11: [
+ '#006837',
+ '#1a9850',
+ '#66bd63',
+ '#a6d96a',
+ '#d9ef8b',
+ '#ffffbf',
+ '#fee08b',
+ '#fdae61',
+ '#f46d43',
+ '#d73027',
+ '#a50026',
+ ],
+ },
Vegetation: {
3: [
"#f1eda9",
diff --git a/src/loaders/eventLoader.js b/src/loaders/eventLoader.js
index da3b847c16..5b104e921a 100644
--- a/src/loaders/eventLoader.js
+++ b/src/loaders/eventLoader.js
@@ -161,6 +161,9 @@ const loadEventLayer = async ({
unclassifiedLegend: unclassifiedLegendFromConfig,
noDataLegend: noDataLegendFromConfig,
labelDataItem,
+ eventHeatmap: eventHeatmapFromConfig,
+ heatIntensity: heatIntensityFromConfig,
+ heatRadius: heatRadiusFromConfig,
} = parseJsonConfig(config.config)
if (countFeaturesWithoutCoordinates) {
config.countFeaturesWithoutCoordinates = true
@@ -202,6 +205,15 @@ const loadEventLayer = async ({
color: config.noDataColor,
}
}
+ if (eventHeatmapFromConfig !== undefined) {
+ config.eventHeatmap = eventHeatmapFromConfig
+ }
+ if (heatIntensityFromConfig !== undefined) {
+ config.heatIntensity = heatIntensityFromConfig
+ }
+ if (heatRadiusFromConfig !== undefined) {
+ config.heatRadius = heatRadiusFromConfig
+ }
delete config.noDataColor
delete config.config
diff --git a/src/reducers/layerEdit.js b/src/reducers/layerEdit.js
index 35ed32059d..63520bb8e7 100644
--- a/src/reducers/layerEdit.js
+++ b/src/reducers/layerEdit.js
@@ -365,6 +365,12 @@ const layerEdit = (state = null, action) => {
return newState
+ case types.LAYER_EDIT_EVENT_HEATMAP_SET:
+ return {
+ ...state,
+ eventHeatmap: action.checked,
+ }
+
case types.LAYER_EDIT_EVENT_CLUSTERING_SET:
return {
...state,
diff --git a/src/reducers/map.js b/src/reducers/map.js
index 7794693f07..4ddb2e8457 100644
--- a/src/reducers/map.js
+++ b/src/reducers/map.js
@@ -102,6 +102,26 @@ const layer = (state, action) => {
opacity: action.opacity,
}
+ case types.LAYER_CHANGE_INTENSITY:
+ if (state.id !== action.id) {
+ return state
+ }
+
+ return {
+ ...state,
+ heatIntensity: action.heatIntensity,
+ }
+
+ case types.LAYER_CHANGE_RADIUS:
+ if (state.id !== action.id) {
+ return state
+ }
+
+ return {
+ ...state,
+ heatRadius: action.heatRadius,
+ }
+
case types.LAYER_LOADING_SET:
if (state.id !== action.id) {
return state
@@ -295,6 +315,8 @@ const map = (state = defaultState, action) => {
case types.LAYER_UPDATE:
case types.LAYER_EDIT:
case types.LAYER_CHANGE_OPACITY:
+ case types.LAYER_CHANGE_INTENSITY:
+ case types.LAYER_CHANGE_RADIUS:
case types.LAYER_TOGGLE_VISIBILITY:
case types.LAYER_TOGGLE_EXPAND:
case types.DATA_FILTER_SET:
diff --git a/src/util/colors.js b/src/util/colors.js
index 810f5a4799..8e83f07061 100644
--- a/src/util/colors.js
+++ b/src/util/colors.js
@@ -53,13 +53,34 @@ export const colorScales = [
'Set3',
]
+export const heatScales = [
+ 'YlOrBr',
+ 'Reds',
+ 'YlGn',
+ 'Greens',
+ 'Blues',
+ 'BuPu',
+ 'RdPu',
+ 'PuRd',
+ 'Greys',
+ 'PuOr_reverse',
+ 'BrBG_reverse',
+ 'PRGn_reverse',
+ 'PiYG_reverse',
+ 'RdBu_reverse',
+ 'RdGy_reverse',
+ 'RdYlBu_reverse',
+ 'Spectral_reverse',
+ 'RdYlGn_reverse',
+]
+
// Returns a color brewer scale for a number of classes
export const getColorPalette = (scale, classes) => colorbrewer[scale][classes]
// Returns color scale name for a palette
// join(',') is used to compare two arrays of colors
-export const getColorScale = (palette) =>
- colorScales.find(
+export const getColorScale = (palette, heat) =>
+ (heat ? heatScales : colorScales).find(
(name) =>
colorbrewer[name][palette.length].join(',') === palette.join(',')
)
diff --git a/src/util/favorites.js b/src/util/favorites.js
index 520c919873..05c17a6381 100644
--- a/src/util/favorites.js
+++ b/src/util/favorites.js
@@ -42,9 +42,12 @@ const validLayerProperties = [
'endDate',
'eventCoordinateField',
'eventClustering',
+ 'eventHeatmap',
'eventPointColor',
'eventPointRadius',
'eventStatus',
+ 'heatIntensity',
+ 'heatRadius',
'featureStyle', // used by GEOJSON_URL_LAYER, stored in layer config
'filter',
'filters',
@@ -180,6 +183,15 @@ const buildCommonLayerConfigData = (layer) => {
if (layer.labelDataItem) {
configData.labelDataItem = layer.labelDataItem
}
+ if (layer.eventHeatmap !== undefined) {
+ configData.eventHeatmap = layer.eventHeatmap
+ }
+ if (layer.heatIntensity !== undefined) {
+ configData.heatIntensity = layer.heatIntensity
+ }
+ if (layer.heatRadius !== undefined) {
+ configData.heatRadius = layer.heatRadius
+ }
return configData
}
@@ -194,6 +206,9 @@ const deleteCommonLayerConfigProps = (layer) => {
delete layer.countFeaturesWithoutCoordinates
delete layer.countEventsOutsideOrgUnits
delete layer.labelDataItem
+ delete layer.eventHeatmap
+ delete layer.heatIntensity
+ delete layer.heatRadius
}
const buildEarthEngineLayerConfigData = (layer) => {
diff --git a/src/util/getDefaultLayerTypes.js b/src/util/getDefaultLayerTypes.js
index 2c7e85db82..6e5f530327 100644
--- a/src/util/getDefaultLayerTypes.js
+++ b/src/util/getDefaultLayerTypes.js
@@ -19,6 +19,8 @@ export const getDefaultLayerTypes = () => [
type: i18n.t('Events'),
img: 'images/events.png',
opacity: 0.8,
+ heatIntensity: 0.5,
+ heatRadius: 0.5,
eventClustering: true,
},
{
diff --git a/yarn.lock b/yarn.lock
index 90512c02d9..9ef55844c8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2371,10 +2371,9 @@
resolved "https://registry.yarnpkg.com/@dhis2/data-engine/-/data-engine-3.17.3.tgz#0347416e9919efbf4d9739c4141fa543f89669ad"
integrity sha512-hLXt7LFrFitR7QgKfGQ3ComTLrY5IAdtERonhdo/SIrsRYWoeVaMiCOkUUzC48pEaeo1/BL5qwA7Tw7jZgROQw==
-"@dhis2/maps-gl@^4.4.3":
- version "4.4.3"
- resolved "https://registry.yarnpkg.com/@dhis2/maps-gl/-/maps-gl-4.4.3.tgz#5584caee92c3fd164e3ab2cb67b78a428c7d1cb3"
- integrity sha512-C2aDFNV3l3v5wbo0LJHr1lydQE6St04U11kCXtzU1WGqnEMEZKzuHyEoCfhIa/gas3e7HpNVfAbL1ZHI1OqDSw==
+"@dhis2/maps-gl@git+https://github.com/d2-ci/maps-gl.git#a0c0a650843414776d42d94d38b476499823cffb":
+ version "4.2.8"
+ resolved "git+https://github.com/d2-ci/maps-gl.git#a0c0a650843414776d42d94d38b476499823cffb"
dependencies:
"@mapbox/sphericalmercator" "^1.2.0"
"@turf/area" "^7.3.5"