Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ export default [
document: 'readonly',
console: 'readonly',
fetch: 'readonly',
AbortController: 'readonly',
URL: 'readonly',
URLSearchParams: 'readonly',
DOMParser: 'readonly',
HTMLDialogElement: 'readonly',
Event: 'readonly',
alert: 'readonly',
setTimeout: 'readonly',
Expand All @@ -42,4 +44,4 @@ export default [
'no-prototype-builtins': 'off'
}
}
];
];
4 changes: 1 addition & 3 deletions viewer/js/app.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
TIME_EXPAND_LIMIT,
NCSS_WARN_TIMESTEPS,
DEFAULT_VARIABLE_LABELS,
CRS_OPTIONS,
DEFAULT_CANADA_BBOX_4326,
Expand Down Expand Up @@ -326,8 +325,7 @@ const subsettingController = createSubsettingController({
subsetDrawSource,
subsetDrawLayer,
getCurrentCrs,
},
NCSS_WARN_TIMESTEPS,
}
});

({
Expand Down
146 changes: 107 additions & 39 deletions viewer/js/portal/datasets.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {
WMS_VERSION,
FALLBACK_PALETTES,
DEFAULT_CANADA_BBOX_4326
} from '../core/config.js';
DEFAULT_CANADA_BBOX_4326,
} from "../core/config.js";

export function variableLabelForGroup(varCode, group, defaultLabels = {}) {
const v = String(varCode || '');
const v = String(varCode || "");
const fromGroup = group?.variable?.labels?.[v];
return fromGroup || defaultLabels[v] || v;
}
Expand All @@ -20,19 +20,38 @@ export function createDatasetController({
time,
map: mapDeps,
layer,
render
render,
}) {
const { legendPanel, crsSelect, subsetTimeStart, subsetTimeEnd, timeSlider } = ui;
const { legendPanel, crsSelect, subsetTimeStart, subsetTimeEnd, timeSlider } =
ui;
const { setStatus, stopStatusSpinner, cancelPendingSubsetStatus } = status;
const { fetchText } = services;
const { parseWmsCapabilities, fetchLayerDetails, deriveTimesFromLayerDetails, fetchLayerTimesteps, updateTimeUI, toDateInputValue } = time;
const { getCurrentCrs, setMapProjection, pickBestCrsForLayer, fitMapToBbox4326 } = mapDeps;
const { deriveScaleRangeFromMetadata, applyLayerScaleDefaults, syncPaletteEnabled, populatePaletteSelect, pickDefaultPaletteForVar } = layer;
const {
parseWmsCapabilities,
fetchLayerDetails,
deriveTimesFromLayerDetails,
fetchLayerTimesteps,
updateTimeUI,
toDateInputValue,
} = time;
const {
getCurrentCrs,
setMapProjection,
pickBestCrsForLayer,
fitMapToBbox4326,
} = mapDeps;
const {
deriveScaleRangeFromMetadata,
applyLayerScaleDefaults,
syncPaletteEnabled,
populatePaletteSelect,
pickDefaultPaletteForVar,
} = layer;
const { refreshInfoPanel, updateMap } = render;

function threddsRoot() {
const root = String(portal.threddsRoot || '/thredds/');
return root.endsWith('/') ? root : `${root}/`;
const root = String(portal.threddsRoot || "/thredds/");
return root.endsWith("/") ? root : `${root}/`;
}

function fileServerUrlForUrlPath(urlPath) {
Expand All @@ -48,25 +67,39 @@ export function createDatasetController({
}

function ncpartitionerBase() {
return ' /pdp-next/ncpartitioner/partition/';
return "/pdp-next/ncpartitioner/";
}

function normalizePortalTimeValue(value) {
const raw = String(value || '').trim();
if (!raw) return '';
const isoLike = raw.includes('T') ? raw : raw.replace(' ', 'T');
const withZone = /(?:Z|[+-]\d{2}:\d{2})$/.test(isoLike) ? isoLike : `${isoLike}Z`;
const raw = String(value || "").trim();
if (!raw) return "";
const isoLike = raw.includes("T") ? raw : raw.replace(" ", "T");
const withZone = /(?:Z|[+-]\d{2}:\d{2})$/.test(isoLike)
? isoLike
: `${isoLike}Z`;
const dt = new Date(withZone);
return Number.isNaN(dt.getTime()) ? raw : dt.toISOString();
}

async function resolveLayersFromCapabilities() {
const capsUrl = `${state.currentDataset.wmsBase}?service=WMS&request=GetCapabilities&version=${encodeURIComponent(WMS_VERSION)}`;
const capsUrl = `${
state.currentDataset.wmsBase
}?service=WMS&request=GetCapabilities&version=${encodeURIComponent(
WMS_VERSION,
)}`;
const capsText = await fetchText(capsUrl);
const layers = parseWmsCapabilities(capsText);
if (!layers.length) throw new Error('No layers found in GetCapabilities');
if (!layers.length) throw new Error("No layers found in GetCapabilities");
const preferred = state.variable
? layers.find((l) => String(l.name || '').trim().toLowerCase() === String(state.variable || '').trim().toLowerCase())
? layers.find(
(l) =>
String(l.name || "")
.trim()
.toLowerCase() ===
String(state.variable || "")
.trim()
.toLowerCase(),
)
: null;
state.selectedLayer = preferred || layers[0];
state.layers = layers;
Expand Down Expand Up @@ -97,12 +130,18 @@ export function createDatasetController({
}

function initTimesFromLayer(timeMetadata) {
const timeStart = state.selectedLayer.time?.start || '';
const timeEnd = state.selectedLayer.time?.end || '';
subsetTimeStart.value = timeStart ? toDateInputValue(timeStart) : '';
subsetTimeEnd.value = timeEnd ? toDateInputValue(timeEnd) : '';
state.times = Array.isArray(state.selectedLayer.time?.times) ? state.selectedLayer.time.times : [];
if (!state.times.length && Number(timeMetadata?.count || 0) === 1 && timeMetadata?.start) {
const timeStart = state.selectedLayer.time?.start || "";
const timeEnd = state.selectedLayer.time?.end || "";
subsetTimeStart.value = timeStart ? toDateInputValue(timeStart) : "";
subsetTimeEnd.value = timeEnd ? toDateInputValue(timeEnd) : "";
state.times = Array.isArray(state.selectedLayer.time?.times)
? state.selectedLayer.time.times
: [];
if (
!state.times.length &&
Number(timeMetadata?.count || 0) === 1 &&
timeMetadata?.start
) {
const singleTime = normalizePortalTimeValue(timeMetadata.start);
if (singleTime) {
state.times = [singleTime];
Expand All @@ -124,7 +163,10 @@ export function createDatasetController({

async function expandTimesFromMetadata() {
if (state.times.length > 1) return;
const metadataTimes = await fetchLayerTimesteps(state.currentDataset.wmsBase, state.selectedLayer.name);
const metadataTimes = await fetchLayerTimesteps(
state.currentDataset.wmsBase,
state.selectedLayer.name,
);
if (metadataTimes.times.length > state.times.length) {
state.times = metadataTimes.times;
setSubsetTimeInputs(metadataTimes.start, metadataTimes.end);
Expand All @@ -139,29 +181,52 @@ export function createDatasetController({
state.times = [singleTime];
subsetTimeStart.value = toDateInputValue(singleTime);
subsetTimeEnd.value = toDateInputValue(singleTime);
timeSlider.value = '0';
timeSlider.value = "0";
updateTimeUI();
}

function applyPaletteAndScale(details, rendering) {
const paletteCandidates = details?.palettes?.length ? details.palettes : [...FALLBACK_PALETTES];
const paletteDefault = pickDefaultPaletteForVar(state.variable, paletteCandidates, details?.defaultPalette || FALLBACK_PALETTES[0]);
const paletteCandidates = details?.palettes?.length
? details.palettes
: [...FALLBACK_PALETTES];
const paletteDefault = pickDefaultPaletteForVar(
state.variable,
paletteCandidates,
details?.defaultPalette || FALLBACK_PALETTES[0],
);
populatePaletteSelect(paletteCandidates, paletteDefault);
const detailsRange = deriveScaleRangeFromMetadata(details);
const fileRange = (rendering && Number.isFinite(rendering.min) && Number.isFinite(rendering.max))
? { min: Number(rendering.min), max: Number(rendering.max) }
: null;
const fileRange =
rendering &&
Number.isFinite(rendering.min) &&
Number.isFinite(rendering.max)
? { min: Number(rendering.min), max: Number(rendering.max) }
: null;
applyLayerScaleDefaults(fileRange || detailsRange);
syncPaletteEnabled();
}

async function loadDatasetFromUrlPath({ name, urlPath, variable, metadata = null, rendering = null, timeMetadata = null }) {
async function loadDatasetFromUrlPath({
name,
urlPath,
variable,
metadata = null,
rendering = null,
timeMetadata = null,
}) {
try {
cancelPendingSubsetStatus?.();
stopStatusSpinner();
setStatus('Loading dataset…');
legendPanel?.classList.add('hidden');
state.currentDataset = { name, urlPath, wmsBase: wmsBaseForUrlPath(urlPath), metadata, rendering, timeMetadata };
setStatus("Loading dataset…");
legendPanel?.classList.add("hidden");
state.currentDataset = {
name,
urlPath,
wmsBase: wmsBaseForUrlPath(urlPath),
metadata,
rendering,
timeMetadata,
};
state.variable = variable || null;
state.layers = [];
state.selectedLayer = null;
Expand All @@ -175,9 +240,12 @@ export function createDatasetController({

let details = null;
try {
details = await fetchLayerDetails(state.currentDataset.wmsBase, state.selectedLayer.name);
details = await fetchLayerDetails(
state.currentDataset.wmsBase,
state.selectedLayer.name,
);
} catch (err) {
console.warn('layerDetails unavailable:', err?.message || err);
console.warn("layerDetails unavailable:", err?.message || err);
}
state.layerDetails = details;

Expand All @@ -188,7 +256,7 @@ export function createDatasetController({
applyPaletteAndScale(details, rendering);
refreshInfoPanel();
updateMap();
setStatus('Ready');
setStatus("Ready");
} catch (err) {
console.error(err);
setStatus(`Error: ${err.message}`, true);
Expand All @@ -201,6 +269,6 @@ export function createDatasetController({
wmsBaseForUrlPath,
dodsBaseForUrlPath,
ncpartitionerBase,
loadDatasetFromUrlPath
loadDatasetFromUrlPath,
};
}
2 changes: 1 addition & 1 deletion viewer/js/portal/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export function createMenuController({
setStatus('Loading portal metadata…');
const metaPayload = await loadPortalMeta(portal.id);
renderMenuFromPortalMeta(metaPayload);
setStatus('Ready - Select a dataset');
setStatus('Ready')
}

function populatePortalSelect() {
Expand Down
4 changes: 1 addition & 3 deletions viewer/js/subsetting.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ export function createSubsettingController({
status,
services,
time,
mapDeps,
NCSS_WARN_TIMESTEPS
mapDeps
}) {
const logger = createSubsetLogger();
const drawController = createSubsetDrawController({
Expand All @@ -30,7 +29,6 @@ export function createSubsettingController({
status,
services,
time,
NCSS_WARN_TIMESTEPS,
logger,
drawController,
indexController
Expand Down
Loading
Loading