Skip to content

Commit da23af4

Browse files
committed
introducing a histogram and a tooltip with more information
1 parent 24ec031 commit da23af4

File tree

14 files changed

+987
-45
lines changed

14 files changed

+987
-45
lines changed

src/store/store.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const useGlobeControlStore = defineStore("globeControl", {
3232
loading: false,
3333
varinfo: undefined as TVarInfo | undefined, // info about a dataset coming directly from the data
3434
selection: { low: 0, high: 0 } as TBounds, // all the knobs and buttons in GlobeControl which do not require a reload
35+
histogram: undefined as number[] | undefined, // histogram bins for colorbar visualization
3536
colormap: "turbo" as TColorMap,
3637
invertColormap: true,
3738
posterizeLevels: 0 as number,
@@ -72,9 +73,32 @@ export const useGlobeControlStore = defineStore("globeControl", {
7273

7374
this.varinfo = varinfo;
7475
},
76+
updateLowUserBound(low: number | string | undefined) {
77+
if (typeof low === "string") {
78+
if (low.trim() === "") {
79+
low = undefined;
80+
} else {
81+
low = parseFloat(low);
82+
}
83+
}
84+
this.userBoundsLow = low;
85+
},
86+
updateHighUserBound(high: number | string | undefined) {
87+
if (typeof high === "string") {
88+
if (high.trim() === "") {
89+
high = undefined;
90+
} else {
91+
high = parseFloat(high);
92+
}
93+
}
94+
this.userBoundsHigh = high;
95+
},
7596
updateBounds(bounds: TBounds) {
7697
this.selection = bounds;
7798
},
99+
updateHistogram(histogram: number[] | undefined) {
100+
this.histogram = histogram;
101+
},
78102
setControlPanelVisible(visible: boolean) {
79103
this.controlPanelVisible = visible;
80104
},

src/store/useUrlSync.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,8 @@ export function useUrlSync() {
5959
function handleUserBounds() {
6060
if (
6161
(userBoundsLow.value !== undefined &&
62-
(userBoundsLow.value as unknown as string) !== "" &&
63-
userBoundsHigh.value !== undefined &&
64-
(userBoundsHigh.value as unknown as string) !== "") ||
65-
((userBoundsLow.value as unknown as string) === "" &&
66-
(userBoundsHigh.value as unknown as string) === "")
62+
userBoundsHigh.value !== undefined) ||
63+
(userBoundsLow.value === undefined && userBoundsHigh.value === undefined)
6764
) {
6865
changeURLHash({
6966
[URL_PARAMETERS.USER_BOUNDS_LOW]: userBoundsLow.value as number,

src/ui/grids/Curvilinear.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ const {
6868
projectionHelper,
6969
canvas,
7070
box,
71+
updateHistogram,
7172
} = useSharedGridLogic();
7273
7374
watch(
@@ -486,6 +487,8 @@ async function fetchAndRenderData(
486487
487488
const dimInfo = await getDimensionValues(dimensionRanges, indices);
488489
490+
updateHistogram(rawData, min, max, missingValue, fillValue);
491+
489492
store.updateVarInfo(
490493
{
491494
attrs: datavar.attrs,

src/ui/grids/GaussianReduced.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ const {
6767
projectionHelper,
6868
canvas,
6969
box,
70+
updateHistogram,
7071
} = useSharedGridLogic();
7172
7273
watch(
@@ -418,6 +419,7 @@ async function fetchAndRenderData(
418419
}
419420
420421
const dimInfo = await getDimensionValues(dimensionRanges, indices);
422+
updateHistogram(rawData, min, max, missingValue, fillValue);
421423
422424
store.updateVarInfo(
423425
{

src/ui/grids/Healpix.vue

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ import {
2323
useGlobeControlStore,
2424
type TUpdateMode,
2525
} from "@/store/store.ts";
26+
import {
27+
HISTOGRAM_SUMMARY_BINS,
28+
buildHistogramSummary,
29+
type THistogramSummary,
30+
} from "@/utils/histogram.ts";
2631
import { useLog } from "@/utils/logging.ts";
2732
2833
const props = defineProps<{
@@ -58,6 +63,7 @@ const {
5863
fetchDimensionDetails,
5964
updateLandSeaMask,
6065
updateColormap,
66+
updateHistogram,
6167
projectionHelper,
6268
canvas,
6369
box,
@@ -327,8 +333,17 @@ async function getHealpixData(
327333
}
328334
329335
let { min, max, missingValue, fillValue } = getDataBounds(datavar, dataSlice);
336+
// Filter out missing and fill values before building histogram
330337
return {
331338
texture: data2texture(dataSlice, {}),
339+
histogramSummary: buildHistogramSummary(
340+
dataSlice,
341+
min,
342+
max,
343+
fillValue,
344+
missingValue,
345+
HISTOGRAM_SUMMARY_BINS
346+
),
332347
min,
333348
max,
334349
missingValue,
@@ -561,19 +576,20 @@ async function getDimensionValues(
561576
return dimValues;
562577
}
563578
564-
async function fetchAndRenderData(
579+
async function processHealpixChunks(
565580
datavar: zarr.Array<zarr.DataType, zarr.FetchStore>,
566-
updateMode: TUpdateMode
567-
) {
568-
const { dimensionRanges, indices } = await prepareDimensionData(
569-
datavar,
570-
updateMode
571-
);
572-
581+
cellCoord: number[] | undefined,
582+
nside: number,
583+
indices: (number | zarr.Slice | null)[]
584+
): Promise<{
585+
dataMin: number;
586+
dataMax: number;
587+
histogramSummaries: THistogramSummary[];
588+
}> {
573589
let dataMin = Number.POSITIVE_INFINITY;
574590
let dataMax = Number.NEGATIVE_INFINITY;
575-
const cellCoord = await getCells();
576-
const nside = await getNside();
591+
const histogramSummaries: THistogramSummary[] = [];
592+
577593
await Promise.all(
578594
[...Array(HEALPIX_NUMCHUNKS).keys()].map(async (ipix) => {
579595
const texData = await getHealpixData(
@@ -590,7 +606,7 @@ async function fetchAndRenderData(
590606
return;
591607
}
592608
593-
// Update global data range
609+
histogramSummaries.push(texData.histogramSummary);
594610
dataMin = dataMin > texData.min ? texData.min : dataMin;
595611
dataMax = dataMax < texData.max ? texData.max : dataMax;
596612
@@ -604,6 +620,29 @@ async function fetchAndRenderData(
604620
})
605621
);
606622
623+
return { dataMin, dataMax, histogramSummaries };
624+
}
625+
626+
async function fetchAndRenderData(
627+
datavar: zarr.Array<zarr.DataType, zarr.FetchStore>,
628+
updateMode: TUpdateMode
629+
) {
630+
const { dimensionRanges, indices } = await prepareDimensionData(
631+
datavar,
632+
updateMode
633+
);
634+
635+
const cellCoord = await getCells();
636+
const nside = await getNside();
637+
const { dataMin, dataMax, histogramSummaries } = await processHealpixChunks(
638+
datavar,
639+
cellCoord,
640+
nside,
641+
indices
642+
);
643+
644+
updateHistogram(histogramSummaries, dataMin, dataMax);
645+
607646
const dimInfo = await getDimensionValues(dimensionRanges, indices);
608647
609648
store.updateVarInfo(

src/ui/grids/Irregular.vue

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ const {
7070
redraw,
7171
canvas,
7272
box,
73+
updateHistogram,
7374
} = useSharedGridLogic();
7475
7576
watch(
@@ -326,13 +327,11 @@ async function getDimensionValues(
326327
return dimValues;
327328
}
328329
329-
async function fetchAndRenderData(
330+
function getGeographicDimensionIndices(
330331
datavar: zarr.Array<zarr.DataType, zarr.FetchStore>,
331-
updateMode: TUpdateMode
332+
latitudesAttrs: zarr.Attributes,
333+
longitudesAttrs: zarr.Attributes
332334
) {
333-
// Load latitudes and longitudes arrays (1D)
334-
const { latitudes, longitudes, latitudesAttrs, longitudesAttrs } =
335-
await getLatLonData(datavar, props.datasources);
336335
const dimensions = datavar.attrs._ARRAY_DIMENSIONS as string[];
337336
const geoDims: number[] = [];
338337
for (let i = 0; i < dimensions.length; i++) {
@@ -344,6 +343,21 @@ async function fetchAndRenderData(
344343
geoDims.push(i);
345344
}
346345
}
346+
return geoDims;
347+
}
348+
349+
async function fetchAndRenderData(
350+
datavar: zarr.Array<zarr.DataType, zarr.FetchStore>,
351+
updateMode: TUpdateMode
352+
) {
353+
// Load latitudes and longitudes arrays (1D)
354+
const { latitudes, longitudes, latitudesAttrs, longitudesAttrs } =
355+
await getLatLonData(datavar, props.datasources);
356+
const geoDims: number[] = getGeographicDimensionIndices(
357+
datavar,
358+
latitudesAttrs,
359+
longitudesAttrs
360+
);
347361
const { dimensionRanges, indices } = buildDimensionRangesAndIndices(
348362
datavar,
349363
paramDimIndices.value,
@@ -371,6 +385,8 @@ async function fetchAndRenderData(
371385
getGrid(latitudes, longitudes, rawData);
372386
373387
const dimInfo = await getDimensionValues(dimensionRanges, indices);
388+
updateHistogram(rawData, min, max, missingValue, fillValue);
389+
374390
store.updateVarInfo(
375391
{
376392
attrs: datavar.attrs,

src/ui/grids/Regular.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ const {
5757
fetchDimensionDetails,
5858
updateLandSeaMask,
5959
updateColormap,
60+
updateHistogram,
6061
projectionHelper,
6162
canvas,
6263
box,
@@ -481,6 +482,7 @@ async function fetchAndRenderData(
481482
// Set missing/fill values as uniforms for the shader
482483
material.uniforms.missingValue.value = missingValue;
483484
material.uniforms.fillValue.value = fillValue;
485+
updateHistogram(rawData, min, max, missingValue, fillValue);
484486
485487
mainMesh!.material = material;
486488
mainMesh!.material.needsUpdate = true;

src/ui/grids/Triangular.vue

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const {
6262
projectionHelper,
6363
canvas,
6464
box,
65+
updateHistogram,
6566
} = useSharedGridLogic();
6667
6768
watch(
@@ -345,6 +346,29 @@ async function getDimensionValues(
345346
return dimValues;
346347
}
347348
349+
function distributeDataToMeshes(dataBuffer: {
350+
dataValues: Float32Array;
351+
dataMin: number;
352+
dataMax: number;
353+
missingValue: number;
354+
fillValue: number;
355+
}) {
356+
let offset = 0;
357+
for (const mesh of meshes) {
358+
const nVerts = mesh.geometry.getAttribute("position").count;
359+
// Each triangle has 3 vertices, each vertex has a value
360+
const meshData = dataBuffer.dataValues.subarray(offset, offset + nVerts);
361+
mesh.geometry.setAttribute(
362+
"data_value",
363+
new THREE.BufferAttribute(meshData, 1)
364+
);
365+
const material = mesh.material as THREE.ShaderMaterial;
366+
material.uniforms.missingValue.value = dataBuffer.missingValue;
367+
material.uniforms.fillValue.value = dataBuffer.fillValue;
368+
offset += nVerts;
369+
}
370+
}
371+
348372
async function fetchAndRenderData(
349373
datavar: zarr.Array<zarr.DataType, zarr.FetchStore>,
350374
updateMode: TUpdateMode
@@ -366,22 +390,17 @@ async function fetchAndRenderData(
366390
);
367391
const dataBuffer = data2valueBuffer(rawData, datavar);
368392
// Distribute data values to each mesh
369-
let offset = 0;
370-
for (const mesh of meshes) {
371-
const nVerts = mesh.geometry.getAttribute("position").count;
372-
// Each triangle has 3 vertices, each vertex has a value
373-
const meshData = dataBuffer.dataValues.subarray(offset, offset + nVerts);
374-
mesh.geometry.setAttribute(
375-
"data_value",
376-
new THREE.BufferAttribute(meshData, 1)
377-
);
378-
const material = mesh.material as THREE.ShaderMaterial;
379-
material.uniforms.missingValue.value = dataBuffer.missingValue;
380-
material.uniforms.fillValue.value = dataBuffer.fillValue;
381-
offset += nVerts;
382-
}
393+
distributeDataToMeshes(dataBuffer);
383394
384395
const dimInfo = await getDimensionValues(dimensionRanges, indices);
396+
updateHistogram(
397+
dataBuffer.dataValues,
398+
dataBuffer.dataMin,
399+
dataBuffer.dataMax,
400+
dataBuffer.missingValue,
401+
dataBuffer.fillValue
402+
);
403+
385404
store.updateVarInfo(
386405
{
387406
attrs: datavar.attrs,

0 commit comments

Comments
 (0)