Skip to content

Commit 3e35215

Browse files
authored
feat: added popup to show values in dashboard (#5230)
1 parent 8cd35f2 commit 3e35215

File tree

8 files changed

+109
-24
lines changed

8 files changed

+109
-24
lines changed

.changeset/quiet-poets-sip.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"geohub": minor
3+
---
4+
5+
feat: added popup to show values in electricity dashboard

sites/geohub/src/routes/(app)/dashboards/electricity/[email protected]

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,8 @@
302302
} else {
303303
electricitySelected = NONE_ID;
304304
}
305+
306+
console.log('electricitySelected', electricitySelected);
305307
};
306308
307309
const setTimeSliderActive = () => {

sites/geohub/src/routes/(app)/dashboards/electricity/components/AnalyzeBivariate.svelte

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import { initTooltipTippy } from '@undp-data/svelte-undp-components';
33
import { onDestroy, onMount } from 'svelte';
4-
import { map } from '../stores';
4+
import { map, selectedAdminDataset } from '../stores';
55
import { unloadAdmin, upsertBivariateAdmin } from '../utils/adminLayer';
66
import { UNDP_DASHBOARD_RASTER_LAYER_ID } from './TimeSlider.svelte';
77
import type { ExpressionSpecification } from 'maplibre-gl';
@@ -91,6 +91,7 @@
9191
};
9292
9393
onMount(() => {
94+
selectedAdminDataset.set('Electricity Access with Wealth indicator');
9495
if ($map) {
9596
const style = $map.getStyle();
9697
for (const layer of style.layers) {
@@ -101,12 +102,10 @@
101102
}
102103
colorExpression = updateColorExpression(propertyA, propertyB, selectedRow, selectedCol);
103104
upsertBivariateAdmin(colorExpression as ExpressionSpecification);
104-
// $map.on('zoomend', () => {
105-
// upsertBivariateAdmin(colorExpression as ExpressionSpecification)
106-
// });
107105
});
108106
109107
onDestroy(() => {
108+
selectedAdminDataset.set(undefined);
110109
unloadAdmin();
111110
});
112111
</script>

sites/geohub/src/routes/(app)/dashboards/electricity/components/ColormapChanger.svelte

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,4 @@
1717
<div class="mt-2 p-4 has-background-light">
1818
<p class="mb-2">Electricity access</p>
1919
<ColorMapPicker colorMapName={$colorMap.value} onchange={colorPickerChangeHandler} />
20-
<!-- <label class="checkbox mt-2">-->
21-
<!-- <input type="checkbox" onchange={showLabelsHandler} checked={showMapLabels} />-->
22-
<!-- Show Labels-->
23-
<!-- </label>-->
2420
</div>

sites/geohub/src/routes/(app)/dashboards/electricity/components/ElectricityDataExplore.svelte

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import ElectricityDataTimelineControl from './ElectricityDataTimelineControl.svelte';
33
import ColormapChanger from './ColormapChanger.svelte';
44
import { ELECTRICITY_DATATYPE_CONTEXT_KEY } from '../stores/electricityDataType';
5-
65
import { onDestroy, onMount, setContext } from 'svelte';
76
import { loadAdmin, unloadAdmin } from '../utils/adminLayer';
87

sites/geohub/src/routes/(app)/dashboards/electricity/components/ElectricityDataTimelineControl.svelte

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
<script lang="ts">
22
import { electricityDataTypes } from '../stores/electricityDataType';
3+
import { onDestroy, onMount } from 'svelte';
4+
import { selectedAdminDataset } from '../stores';
35
46
interface Props {
57
electricityDataType: number[];
68
}
79
810
let { electricityDataType = $bindable() }: Props = $props();
11+
12+
onMount(() => {
13+
if (electricityDataType.includes(2012)) {
14+
selectedAdminDataset.set('Settlement-Level Electricity Access');
15+
} else {
16+
selectedAdminDataset.set('Electricity Access Forecast');
17+
}
18+
});
19+
onDestroy(() => selectedAdminDataset.set(undefined));
920
</script>
1021

1122
<div>
@@ -15,6 +26,11 @@
1526
class="button data-option pl-3 {`${JSON.stringify(choice.value) === JSON.stringify(electricityDataType) ? 'is-active' : ''}`}"
1627
onclick={() => {
1728
electricityDataType = choice.value as number[];
29+
if (choice.value.includes(2012)) {
30+
selectedAdminDataset.set('Settlement-Level Electricity Access');
31+
} else {
32+
selectedAdminDataset.set('Electricity Access Forecast');
33+
}
1834
}}
1935
>
2036
<span class="is-size-7">{choice.title}</span>

sites/geohub/src/routes/(app)/dashboards/electricity/stores/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,10 @@ export const colorMap = writable({
1313
value: 'pubu',
1414
isReversed: false
1515
});
16+
17+
export const selectedAdminDataset = writable<
18+
| 'Settlement-Level Electricity Access'
19+
| 'Electricity Access Forecast'
20+
| 'Electricity Access with Wealth indicator'
21+
| undefined
22+
>(undefined);

sites/geohub/src/routes/(app)/dashboards/electricity/utils/adminLayer.ts

Lines changed: 76 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@ import maplibregl, {
22
type ExpressionSpecification,
33
type FillLayerSpecification,
44
type LineLayerSpecification,
5+
type Popup,
56
type SourceSpecification
67
} from 'maplibre-gl';
7-
import { admin, colorMap, map as mapStore } from '../stores';
8+
import { admin, colorMap, selectedAdminDataset, map as mapStore } from '../stores';
89
import { get } from 'svelte/store';
910
import chroma from 'chroma-js';
1011

1112
const ADM_ID = 'admin';
1213
const ADM0_ID = 'adm0';
13-
let hoveredStateId: string;
14+
let hoverPopup: Popup | null = null;
15+
let lastFeatureId: null;
1416
const choropleth = true;
1517

1618
let adminUrl = '';
@@ -44,48 +46,106 @@ export const offInteraction = () => {
4446
map.off('mouseleave', ADM_ID, onMouseLeave);
4547
};
4648

49+
let lastUpdate = 0;
50+
51+
const getAdminLevelName = () => {
52+
const map = getMap();
53+
const zoom = map.getZoom();
54+
if (zoom < 3) return 'Country';
55+
if (zoom < 7) return 'State';
56+
return 'District';
57+
};
58+
4759
const onMouseMove = (e) => {
60+
if (!maplibregl) return;
61+
62+
const now = performance.now();
63+
if (now - lastUpdate < 50) {
64+
if (hoverPopup) hoverPopup.setLngLat(e.lngLat);
65+
return;
66+
}
67+
lastUpdate = now;
68+
4869
const map = getMap();
4970
const lvl = getAdminLevelForZoom(map.getZoom());
5071
const promoteId = `adm${lvl}_id`;
5172
const sourceLayer = `adm${lvl}_polygons`;
52-
5373
const feature = e.features?.[0];
54-
if (!feature) return;
5574

56-
const featureId = feature.properties[promoteId];
57-
if (!featureId) return;
75+
if (!feature) {
76+
if (hoverPopup) hoverPopup.remove();
5877

59-
if (hoveredStateId && hoveredStateId !== featureId) {
60-
map.setFeatureState({ source: ADM_ID, sourceLayer, id: hoveredStateId }, { hover: false });
78+
if (lastFeatureId !== null) {
79+
map.setFeatureState({ source: ADM_ID, sourceLayer, id: lastFeatureId }, { hover: false });
80+
lastFeatureId = null;
81+
admin.set({});
82+
}
83+
return;
84+
}
85+
86+
if (!hoverPopup) {
87+
hoverPopup = new maplibregl.Popup({
88+
closeButton: false,
89+
closeOnClick: false
90+
}).trackPointer();
6191
}
6292

63-
hoveredStateId = featureId;
93+
const nameColumn = feature.properties[`adm${lvl}_name`];
94+
const valueColumn = feature.properties[`hrea_${year}`];
95+
const currentId = feature.properties[promoteId];
96+
97+
if (currentId !== lastFeatureId) {
98+
hoverPopup
99+
.setHTML(
100+
`
101+
<div style="padding:10px 12px; background:#fff; border-radius:10px; box-shadow:0 2px 6px rgba(0,0,0,0.15); font-size:13px;">
102+
<div style="font-weight:600; margin-bottom:4px;">${get(selectedAdminDataset)}</div>
103+
<hr style="border:0; border-top:1px solid #e0e0e0; margin:6px 0;">
104+
<div style="font-weight:400; margin-bottom:4px;">${getAdminLevelName()}: ${nameColumn}</div>
105+
<div>${year}: ${(valueColumn * 100).toFixed(1)}%</div>
106+
</div>
107+
`
108+
)
109+
.addTo(map);
110+
111+
if (lastFeatureId || lastFeatureId === 0) {
112+
map.setFeatureState({ source: ADM_ID, sourceLayer, id: lastFeatureId }, { hover: false });
113+
}
114+
115+
map.setFeatureState({ source: ADM_ID, sourceLayer, id: currentId }, { hover: true });
64116

65-
map.setFeatureState({ source: ADM_ID, sourceLayer, id: hoveredStateId }, { hover: true });
117+
const el = hoverPopup.getElement().querySelector('.maplibregl-popup-content');
118+
el.style.cssText = 'background:transparent;border:none;box-shadow:none;padding:0;';
66119

67-
admin.set(feature.properties);
120+
lastFeatureId = currentId;
121+
admin.set(feature.properties);
122+
} else {
123+
hoverPopup.setLngLat(e.lngLat);
124+
}
68125
};
69126

70127
const onMouseLeave = () => {
71128
const map = getMap();
72129
const lvl = getAdminLevelForZoom(map.getZoom());
73130
const sourceLayer = `adm${lvl}_polygons`;
74131

75-
if (hoveredStateId) {
76-
map.setFeatureState({ source: ADM_ID, sourceLayer, id: hoveredStateId }, { hover: false });
132+
if (lastFeatureId !== null) {
133+
map.setFeatureState({ source: ADM_ID, sourceLayer, id: lastFeatureId }, { hover: false });
134+
lastFeatureId = null;
77135
admin.set({});
78136
}
79137

80-
hoveredStateId = null;
138+
if (hoverPopup) {
139+
hoverPopup.remove();
140+
}
81141
};
82142

83143
const onZoom = async ({ originalEvent }) => {
84144
if (!originalEvent) return;
85145
loadAdmin();
86146
};
87147

88-
const getMap = () => get(mapStore);
148+
export const getMap = () => get(mapStore);
89149

90150
export const loadAdmin = () => {
91151
const map = getMap();
@@ -96,6 +156,7 @@ export const loadAdmin = () => {
96156
}
97157
loadAdminChoropleth(null);
98158
onInteraction();
159+
99160
map.on('zoom', onZoom);
100161
};
101162

0 commit comments

Comments
 (0)