From 424a131f7e34413c4c67109b441eba51ee27ebc9 Mon Sep 17 00:00:00 2001 From: Elliott Rose Date: Fri, 24 Feb 2023 12:25:27 +0100 Subject: [PATCH 1/4] Add new zoom fit file to handle different aspect ratio and better zoom fit for coverage area of gifs --- conf/coords.json | 7 +++++++ tabs/forecast_callbacks.py | 1 + 2 files changed, 8 insertions(+) diff --git a/conf/coords.json b/conf/coords.json index 04f75bb..e220933 100644 --- a/conf/coords.json +++ b/conf/coords.json @@ -3,5 +3,12 @@ "zoom":"5", "lat":"34", "lon": "-7" + }, + "fit":{ + "zoom":"4", + "lat":"43.25", + "lon": "16.5" } } + + diff --git a/tabs/forecast_callbacks.py b/tabs/forecast_callbacks.py index c29dd84..9ae77f7 100644 --- a/tabs/forecast_callbacks.py +++ b/tabs/forecast_callbacks.py @@ -939,6 +939,7 @@ def show_timeseries(ts_button, mod, date, variable, coords, popups): State('country-lon', 'value'), State({'tag': 'model-map', 'index': ALL, "n_clicks": ALL}, 'id'), ], + prevent_initial_call=True ) def zoom_country(n_clicks, tstep, date, model, variable, static, view, zoom, lat, lon, ids): """Set zoom and center over coordinates entered""" -- GitLab From 9ae6bdee65ed8219b4b5e8502c2d07aa03881d61 Mon Sep 17 00:00:00 2001 From: Elliott Rose Date: Tue, 28 Feb 2023 10:10:48 +0100 Subject: [PATCH 2/4] Update coordinates for better zoomed images --- conf/coords.json | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/conf/coords.json b/conf/coords.json index e220933..56d2445 100644 --- a/conf/coords.json +++ b/conf/coords.json @@ -2,12 +2,23 @@ "spain":{ "zoom":"5", "lat":"34", - "lon": "-7" + "lon": "-7", + "width": "1280", + "height": "768" }, "fit":{ "zoom":"4", - "lat":"43.25", - "lon": "16.5" + "lat":"39.25", + "lon": "16.5", + "width": "1250", + "height": "1100" + }, + "monarch_fit":{ + "zoom":"3", + "lat":"41.25", + "lon": "16.5", + "width": "1180", + "height": "720" } } -- GitLab From 408a5ac3c6571368a310c8bee09bc736f72029c3 Mon Sep 17 00:00:00 2001 From: Elliott Rose Date: Wed, 1 Mar 2023 15:51:59 +0100 Subject: [PATCH 3/4] Add appropriate variables to the coords.json config file --- conf/coords.json | 22 ++- js/create_model_loop_zoom_fit.js | 234 +++++++++++++++++++++++++++++++ 2 files changed, 252 insertions(+), 4 deletions(-) create mode 100644 js/create_model_loop_zoom_fit.js diff --git a/conf/coords.json b/conf/coords.json index 56d2445..20b98df 100644 --- a/conf/coords.json +++ b/conf/coords.json @@ -1,25 +1,39 @@ { + "default":{ + "zoom":"2", + "lat":"34", + "lon": "-7", + "width": "1280", + "height": "768", + "paddingRight": "5px", + "marginTop": "0px" + }, "spain":{ "zoom":"5", "lat":"34", "lon": "-7", "width": "1280", - "height": "768" + "height": "768", + "paddingRight": "5px", + "marginTop": "0px" }, "fit":{ "zoom":"4", "lat":"39.25", "lon": "16.5", "width": "1250", - "height": "1100" + "height": "1100", + "paddingRight": "5px", + "marginTop": "0px" }, "monarch_fit":{ "zoom":"3", "lat":"41.25", "lon": "16.5", "width": "1180", - "height": "720" + "height": "720", + "paddingRight": "35px", + "marginTop": "20px" } } - diff --git a/js/create_model_loop_zoom_fit.js b/js/create_model_loop_zoom_fit.js new file mode 100644 index 0000000..2264eef --- /dev/null +++ b/js/create_model_loop_zoom_fit.js @@ -0,0 +1,234 @@ +// CREATE A GIF FOCUSED GIF OR PNG OVER SELECTED fit +// anim variable can be true(for gif) or a number for timestep, for png +// call from interactive-forecast-viewer folder +// date currently does not work, and defaults to current day, or most recent data +// node create_fit_loop true monarch 20220808 od550_dust spain + +const { Cluster } = require('puppeteer-cluster'); +const util = require('util'); +const path = require('path'); +// const url = 'http://127.0.0.1:9000/dashboard/' +const url = 'http://0.0.0.0:7778/dashboard/' +const modelDict = {'od550_dust':'AOD', + 'sconc_dust':'Concentration', + 'dust_depd':'Dry deposition', + 'dust_depw':'Wet deposition', + 'dust_load':'Load', + 'dust_ext_sfc':'Extinction'}; + +//READ COORDINATES AND CENTER FROM CONF FILE +const relpath = path.relative('js/create_model_loop_zoom_fit.js', 'interactive-forecast-viewer/conf/coords.json'); +const coords = require(relpath); + +function delay(time) { + return new Promise(function(resolve) { + setTimeout(resolve, time) + }); +} + +const RunCluster = async (anim, curmodel, seldate, variable, fit) => { + const cluster = await Cluster.launch({ + concurrency: Cluster.CONCURRENCY_CONTEXT, + puppeteerOptions: { + headless: true, + args: [ + '--no-sandbox', + '--disable-setuid-sandbox', + ] + }, + maxConcurrency: 4, + }); + + await cluster.task(async ({ page, data: args }) => { + process.stdout.write("ARGS: " + args + "\n"); + var tstep = args[0]; + var curmodel = args[1]; + var seldate = args[2]; + var variable = args[3]; + var fit = args[4].toLowerCase(); + process.stdout.write("TSTEP: " + tstep + " -- MOD: " + curmodel + " -- DATE: " + seldate + " -- VAR: " + variable +' fit: ' + fit + "\n"); + var width_aspect = parseInt(coords[fit].width); + var height_aspect = parseInt(coords[fit].height); + await page.setViewport({ width: width_aspect, height: height_aspect}); + await page.goto(url, { + waitUntil: 'networkidle0', + }); + await page.waitForSelector("#graph-collection"); + await page.waitForSelector(".graph-with-slider"); + // select variable + try { + const sel = await page.$('#variable-dropdown-forecast'); + await sel.click(); + await page.waitForSelector(".Select-menu-outer"); + const dropdown_variable = modelDict[variable]; + process.stdout.write("Selected dropdown variable: " + dropdown_variable + '\n'); + await page.evaluate((dropdown_variable) => { + const options = [...document.querySelectorAll('.VirtualizedSelectOption')]; + selected_variable = options.find(element => element.textContent === dropdown_variable); + selected_variable.click(); + }, dropdown_variable); + } catch (err) { + process.stdout.write("ERR0: " + err + "\n"); + } + // select all models + if (curmodel === "all") { + try { + process.stdout.write("SELECT ALL MODELS\n"); + for (const model of await page.$$('.custom-control-input')) { + const checked = await model.evaluate(elem => elem.checked); + process.stdout.write("CHECKED BEFORE: " + checked + "\n"); + if (!checked) { + await model.click(); + } + } + } catch (err) { + process.stdout.write("ERR1: " + err + "\n"); + } + } + // select only one model + else { + try { + process.stdout.write("SELECT MODEL: " + curmodel + "\n"); + for (const model of await page.$$('.custom-control-input')) { + const checked = await model.evaluate(elem => elem.checked); + process.stdout.write("CHECKED BEFORE: " + checked + "\n"); + const value = await model.evaluate(elem => elem.value); + process.stdout.write("VALUE: " + value + "\n"); + if (!checked && value === curmodel) { + await model.click(); + } + if (checked && value !== curmodel) { + await model.click(); + await delay(500); + } + } + } catch (err) { + process.stdout.write("ERR1: " + err + "\n"); + } + } +// CHANGE fit TO FIT AND COMBINE ALL FILES INTO ONE + // apply button + try { + for (const model of await page.$$('.custom-control-input')) { + const checked = await model.evaluate(elem => elem.checked); + process.stdout.write("CHECKED AFTER: " + checked + "\n"); + } + process.stdout.write("CLICK APPLY BUTTON\n"); + const btn = await page.$('#models-apply'); + await btn.click(); // "button#models-apply"); + await delay(500); + } catch (err) { + process.stdout.write("ERR2: " + err + "\n"); + } + // apply none fit button + try { + var zoom = coords[fit].zoom; + var lat = coords[fit].lat; + var lon = coords[fit].lon; + process.stdout.write("Zoom: " + zoom + " Lat " + lat + " Lon " + lon +"\n"); + + // Change hidden inputs and button to visible + let zoomInput = await page.$('#country-zoom'); + await zoomInput.evaluate((el) => el.style.display = 'block'); + let latInput = await page.$('#country-lat'); + await latInput.evaluate((el) => el.style.display = 'block'); + let lonInput = await page.$('#country-lon'); + await lonInput.evaluate((el) => el.style.display = 'block'); + let zoomButton = await page.$('#country-focus'); + await zoomButton.evaluate((el) => el.style.display = 'block'); + + // Add data and click + await page.type('#country-zoom', zoom); + await page.type('#country-lat', lat); + await page.type('#country-lon', lon); + process.stdout.write("CLICK HIDDEN country BUTTON\n"); + await page.click('#country-focus'); + + // Make elements invisible again + await zoomInput.evaluate((el) => el.style.display = 'none'); + await latInput.evaluate((el) => el.style.display = 'none'); + await lonInput.evaluate((el) => el.style.display = 'none'); + await zoomButton.evaluate((el) => el.style.display = 'none'); + + } catch (err) { + process.stdout.write("ERR3: " + err + "\n"); + } + var num = "00"; + if (tstep === "false") { + process.stdout.write("CURRENT SELECTION: " + tstep + "\n"); + num = "_curr"; + } else { + process.stdout.write("CURRENT TSTEP: " + tstep + "\n"); + const steps = await page.$$("span.rc-slider-dot"); + await steps[tstep].click(); + if (tstep<10) { + num = "0"+tstep; + } else { + num = tstep; + } + } + // wait for alert div to disappear to start grabbing elements + await page.waitForSelector("#alert-forecast", {hidden: true}); + await page.waitForSelector("#graph-collection"); + const graph = await page.$('#graph-collection'); + // remove timeslider + await page.waitForSelector(".layout-dropdown"); + process.stdout.write("REMOVING LAYOUT DROPDOWN" + "\n"); + await page.evaluate((sel) => { + let toRemove = document.querySelector(sel); + toRemove.parentNode.removeChild(toRemove); + }, '.layout-dropdown'); + // remove zoom panel + process.stdout.write("REMOVING ZOOM PANEL(S)" + "\n"); + await page.waitForSelector(".leaflet-bar"); + await page.evaluate((sel) => { + let toRemove = document.querySelectorAll(sel); + for (let j=0; j el.style.display = 'inline'); + await logos.evaluate((el, margin) => {el.style.marginTop = margin}, coords[fit].marginTop); + await logos.evaluate((el, padding) => {el.style.paddingRight = padding}, coords[fit].paddingRight); + + // Handle output and take screenshot + const outputFile = './tmp/' + variable.toLowerCase() + '/' + fit + '/' + seldate + '_' + curmodel + '_' + fit + '_' + num + '.png'; + process.stdout.write("TAKE SCREENSHOT => " + outputFile + "\n"); + await graph.screenshot({ path: outputFile }); + + }); + + if (anim === "true") { + for (let i=0; i<25; i++) { + try { + process.stdout.write("QUEUEING step:" + i + "\n"); + await cluster.queue([i, curmodel, seldate, variable, fit]); + } catch (err) { + console.log(err); + process.stdout.write("ERROR:" + err + "\n"); + } + } + } else { + process.stdout.write("QUEUEING current:" + anim + "\n"); + cluster.queue([anim, curmodel, seldate, variable, fit]); + } + + await cluster.idle(); + await cluster.close(); +} + +var anim = process.argv[2]; // default: false; +var curmodel = process.argv[3]; // default: "none"; +var seldate = process.argv[4]; // default: "none"; +var variable = process.argv[5]; // default: OD550_DUST +var fit = process.argv[6]; // default: Spain + +process.stdout.write("START -> ANIM: " + anim + " CURMODEL: " + curmodel + " DATE: " + seldate + " VAR: " + variable + " fit: " + fit + "\n"); +RunCluster(anim, curmodel, seldate, variable, fit); + -- GitLab From ac0e3341377aaac60396915de8ab81c8145636c0 Mon Sep 17 00:00:00 2001 From: Elliott Rose Date: Wed, 8 Mar 2023 15:37:00 +0100 Subject: [PATCH 4/4] Remove development enviroment. Add fixed size to download section buttons. --- assets/sidebar.css | 4 ++-- js/create_model_loop_zoom_fit.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/sidebar.css b/assets/sidebar.css index 7114bac..6a2da96 100644 --- a/assets/sidebar.css +++ b/assets/sidebar.css @@ -252,7 +252,7 @@ span>#was-apply { .sidebar-bottom .btn { border-radius: 0; background-repeat: no-repeat; - height: 2rem; + height: 10px; font-size: Medium; margin: 0 !important; width: -moz-fit-content; @@ -284,7 +284,7 @@ span>#was-apply { background-color: lightgrey !important; display: block; width: 98%; - height: 4vh; + height: 33px; margin-bottom: 1rem !important; padding-left: 1rem !important; text-align: center; diff --git a/js/create_model_loop_zoom_fit.js b/js/create_model_loop_zoom_fit.js index 2264eef..3af6a66 100644 --- a/js/create_model_loop_zoom_fit.js +++ b/js/create_model_loop_zoom_fit.js @@ -7,8 +7,8 @@ const { Cluster } = require('puppeteer-cluster'); const util = require('util'); const path = require('path'); -// const url = 'http://127.0.0.1:9000/dashboard/' -const url = 'http://0.0.0.0:7778/dashboard/' +const url = 'http://127.0.0.1:9000/dashboard/' +// const url = 'http://0.0.0.0:7778/dashboard/' const modelDict = {'od550_dust':'AOD', 'sconc_dust':'Concentration', 'dust_depd':'Dry deposition', @@ -227,7 +227,7 @@ var anim = process.argv[2]; // default: false; var curmodel = process.argv[3]; // default: "none"; var seldate = process.argv[4]; // default: "none"; var variable = process.argv[5]; // default: OD550_DUST -var fit = process.argv[6]; // default: Spain +var fit = process.argv[6]; process.stdout.write("START -> ANIM: " + anim + " CURMODEL: " + curmodel + " DATE: " + seldate + " VAR: " + variable + " fit: " + fit + "\n"); RunCluster(anim, curmodel, seldate, variable, fit); -- GitLab