From 777315dbb0f897b82fd8ba9730d49f6337386fa5 Mon Sep 17 00:00:00 2001 From: Molly Smith Date: Wed, 12 Jun 2024 14:00:30 -0600 Subject: [PATCH] Y2Y can now plot RI stats --- .../server/dataFunctions/data_yeartoyear.js | 129 ++++++++++++------ apps/met-cyclone/server/main.js | 1 + 2 files changed, 87 insertions(+), 43 deletions(-) diff --git a/apps/met-cyclone/server/dataFunctions/data_yeartoyear.js b/apps/met-cyclone/server/dataFunctions/data_yeartoyear.js index 7516ac60c..b54b13af7 100644 --- a/apps/met-cyclone/server/dataFunctions/data_yeartoyear.js +++ b/apps/met-cyclone/server/dataFunctions/data_yeartoyear.js @@ -52,31 +52,89 @@ dataYearToYear = function (plotParams, plotFunction) { const database = curve.database.replace(/___/g, "."); const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[database][curve["data-source"]][0]; - const modelClause = `and h.amodel = '${model}'`; + let modelClause; // the model field in mysql is called different things for RI and non-RI stats + + const truthStr = curve.truth; + const truth = Object.keys( + matsCollections.truth.findOne({ name: "truth" }).valuesMap + ).find( + (key) => + matsCollections.truth.findOne({ name: "truth" }).valuesMap[key] === truthStr + ); + let truthClause; // the truth field in mysql is called different things for RI and non-RI stats + const selectorPlotType = curve["plot-type"]; - const { statistic } = curve; + let { statistic } = curve; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, { optionsMap: 1 } ).optionsMap[database][curve["data-source"]][selectorPlotType]; const statLineType = statisticOptionsMap[statistic][0]; let statisticClause = ""; + let statHeaderType = ""; let lineDataType = ""; - if (statLineType === "precalculated") { + + const { basin } = curve; + let variableClause = ""; + let thresholdClause = ""; + let stormClause = ""; + let levelsClause = ""; + if (statLineType === "ctc") { + // set up fields specific to ctc scores + statisticClause = + "sum(ld.fy_oy) as fy_oy, " + + "sum(ld.fy_on) as fy_on, " + + "sum(ld.fn_oy) as fn_oy, " + + "sum(ld.fn_on) as fn_on, " + + "group_concat(distinct ld.fy_oy, ';', ld.fy_on, ';', ld.fn_oy, ';', ld.fn_on, ';', ld.total, ';', unix_timestamp(ld.fcst_valid), ';', h.fcst_lev order by unix_timestamp(ld.fcst_valid), h.fcst_lev) as sub_data"; + statHeaderType = "stat_header"; + lineDataType = "line_data_ctc"; + modelClause = `and h.model = '${model}'`; + truthClause = `and h.obtype = '${truth}'`; + stormClause = `and h.vx_mask = '${basin}'`; + statistic = statistic.replace("Rapid Intensification ", ""); + const { variable } = curve; + variableClause = `and h.fcst_var = '${variable}'`; + const { threshold } = curve; + thresholdClause = `and h.fcst_thresh = '${threshold}'`; + } else if (statLineType === "precalculated") { + // set up fields specific to precalculated stats statisticClause = `avg(${statisticOptionsMap[statistic][2]}) as stat, group_concat(distinct ${statisticOptionsMap[statistic][2]}, ';', 9999, ';', unix_timestamp(ld.fcst_valid) order by unix_timestamp(ld.fcst_valid)) as sub_data`; + statHeaderType = "tcst_header"; [, lineDataType] = statisticOptionsMap[statistic]; + modelClause = `and h.amodel = '${model}'`; + truthClause = `and h.bmodel = '${truth}'`; + stormClause = `and h.basin = '${basin}'`; + let levels = + curve.level === undefined || curve.level === matsTypes.InputTypes.unused + ? [] + : curve.level; + const levelValuesMap = matsCollections.level.findOne( + { name: "level" }, + { valuesMap: 1 } + ).valuesMap; + const levelKeys = Object.keys(levelValuesMap); + let levelKey; + levels = Array.isArray(levels) ? levels : [levels]; + if (levels.length > 0) { + levels = levels + .map(function (l) { + for (let lidx = 0; lidx < levelKeys.length; lidx += 1) { + levelKey = levelKeys[lidx]; + if (levelValuesMap[levelKey].name === l) { + return `'${levelKey}'`; + } + } + return null; + }) + .join(","); + levelsClause = `and ld.level IN(${levels})`; + } } - const queryTableClause = `from ${database}.tcst_header h, ${database}.${lineDataType} ld`; - const { basin } = curve; - const stormClause = `and h.basin = '${basin}'`; - const truthStr = curve.truth; - const truth = Object.keys( - matsCollections.truth.findOne({ name: "truth" }).valuesMap - ).find( - (key) => - matsCollections.truth.findOne({ name: "truth" }).valuesMap[key] === truthStr - ); - const truthClause = `and h.bmodel = '${truth}'`; + + const queryTableClause = `from ${database}.${statHeaderType} h, ${database}.${lineDataType} ld`; + const statHeaderClause = `and h.${statHeaderType}_id = ld.${statHeaderType}_id`; + let vts = ""; // start with an empty string that we can pass to the python script if there aren't vts. let validTimeClause = ""; if ( @@ -110,32 +168,6 @@ dataYearToYear = function (plotParams, plotFunction) { .join(","); forecastLengthsClause = `and ld.fcst_lead IN(${fcsts})`; } - let levels = - curve.level === undefined || curve.level === matsTypes.InputTypes.unused - ? [] - : curve.level; - const levelValuesMap = matsCollections.level.findOne( - { name: "level" }, - { valuesMap: 1 } - ).valuesMap; - const levelKeys = Object.keys(levelValuesMap); - let levelKey; - let levelsClause = ""; - levels = Array.isArray(levels) ? levels : [levels]; - if (levels.length > 0 && lineDataType !== "line_data_probrirw") { - levels = levels - .map(function (l) { - for (let lidx = 0; lidx < levelKeys.length; lidx += 1) { - levelKey = levelKeys[lidx]; - if (levelValuesMap[levelKey].name === l) { - return `'${levelKey}'`; - } - } - return null; - }) - .join(","); - levelsClause = `and ld.level IN(${levels})`; - } let descrs = curve.description === undefined || curve.description === matsTypes.InputTypes.unused @@ -158,14 +190,17 @@ dataYearToYear = function (plotParams, plotFunction) { // This axisKeySet object is used like a set and if a curve has the same // variable + statistic (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - const axisKey = statistic; + const axisKey = statistic + .replace("Truth ", "") + .replace("Model ", "") + .replace("Model-truth ", ""); curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options if (!diffFrom) { // this is a database driven curve, not a difference curve // prepare the query from the above parameters statement = - "select substring(h.storm_id, -4) as year, " + + "select date_format(ld.fcst_valid, '%Y') as year, " + "count(distinct unix_timestamp(ld.fcst_valid)) as N_times, " + "min(unix_timestamp(ld.fcst_valid)) as min_secs, " + "max(unix_timestamp(ld.fcst_valid)) as max_secs, " + @@ -174,12 +209,14 @@ dataYearToYear = function (plotParams, plotFunction) { "where 1=1 " + "{{modelClause}} " + "{{stormClause}} " + + "{{variableClause}} " + + "{{thresholdClause}} " + "{{truthClause}} " + "{{validTimeClause}} " + "{{forecastLengthsClause}} " + "{{levelsClause}} " + "{{descrsClause}} " + - "and h.tcst_header_id = ld.tcst_header_id " + + "{{statHeaderClause}} " + "group by year " + "order by year" + ";"; @@ -188,11 +225,17 @@ dataYearToYear = function (plotParams, plotFunction) { statement = statement.replace("{{queryTableClause}}", queryTableClause); statement = statement.replace("{{modelClause}}", modelClause); statement = statement.replace("{{stormClause}}", stormClause); + statement = statement.replace("{{variableClause}}", variableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); statement = statement.replace("{{truthClause}}", truthClause); statement = statement.replace("{{validTimeClause}}", validTimeClause); statement = statement.replace("{{forecastLengthsClause}}", forecastLengthsClause); statement = statement.replace("{{levelsClause}}", levelsClause); statement = statement.replace("{{descrsClause}}", descrsClause); + statement = statement.replace("{{statHeaderClause}}", statHeaderClause); + if (statLineType !== "precalculated") { + statement = statement.replace(/fcst_valid/g, "fcst_valid_beg"); + } dataRequests[label] = statement; queryArray.push({ diff --git a/apps/met-cyclone/server/main.js b/apps/met-cyclone/server/main.js index 8ec613272..96a4e25ef 100644 --- a/apps/met-cyclone/server/main.js +++ b/apps/met-cyclone/server/main.js @@ -283,6 +283,7 @@ const doCurveParams = function () { matsTypes.PlotTypes.dieoff, matsTypes.PlotTypes.validtime, matsTypes.PlotTypes.histogram, + matsTypes.PlotTypes.yearToYear, ], };