From ce4f06c88d60966b7559781bed5c3433f52d44a2 Mon Sep 17 00:00:00 2001 From: Elliott Rose Date: Fri, 20 Jan 2023 18:05:56 +0100 Subject: [PATCH 1/6] Add hidden button, callback, and js for creating spain zoom pngs and gifs --- js/create_spain_loop.js | 220 +++++++++++++++++++++++++++++++++++++ tabs/forecast.py | 4 + tabs/forecast_callbacks.py | 28 +++++ 3 files changed, 252 insertions(+) create mode 100644 js/create_spain_loop.js diff --git a/js/create_spain_loop.js b/js/create_spain_loop.js new file mode 100644 index 0000000..b8a3a1a --- /dev/null +++ b/js/create_spain_loop.js @@ -0,0 +1,220 @@ +// I have no idea why this will run if I put it in the create_model_loop file, but not here + +const { Cluster } = require('puppeteer-cluster'); +const util = require('util'); +//const url = 'http://127.0.0.1:9000/daily_dashboard/' +const url = 'http://0.0.0.0:7778/dashboard/' +//const uniqueId = Date.now().toString(36) + Math.random().toString(36).substring(2); +// const imageTemplate = '/data/daily_dashboard/comparison/'; // + uniqueId + '_image'; +const modelDict = {'od550_dust':'AOD', + 'sconc_dust':'Concentration', + 'dust_depd':'Dry deposition', + 'dust_depw':'Wet deposition', + 'dust_load':'Load', + 'dust_ext_sfc':'Extinction'}; + +function delay(time) { + return new Promise(function(resolve) { + setTimeout(resolve, time) + }); +} + +const RunCluster = async (anim, curmodel, seldate, variable, spainZoom) => { + const cluster = await Cluster.launch({ + concurrency: Cluster.CONCURRENCY_CONTEXT, + puppeteerOptions: { + headless: true, + args: [ + '--no-sandbox', + '--disable-setuid-sandbox', + // '--start-maximized', + ] + }, + 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 spainZoom = args[4]; + process.stdout.write("TSTEP: " + tstep + " -- MOD: " + curmodel + " -- DATE: " + seldate + " -- VAR: " + variable +'spainZoom: ' + spainZoom + "\n"); + await page.setViewport({ width: 1280, height: 768}); + 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"); + } + } + // 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 hidden spain button + if (spainZoom == "true") { + try { + process.stdout.write("CLICK HIDDEN SPAIN BUTTON\n"); + await page.evaluate(() => { + document.querySelector('#spain').click(); + }); + await delay(500); + } catch (err) { + process.stdout.write("ERR2: " + err + "\n"); + } + } + // select date +// if (seldate !== "none") { +// try { +// await page.waitForSelector('input#date'); +// const curdate = await page.evaluate(() => document.querySelector('input[id=date]').value); +// process.stdout.write("CURDATE: " + curdate + "\n"); +// // const input = await page.$('input[id=date]'); +// await page.focus('input[id=date]'); // input.click(); +// await page.keyboard.type(""); +// await page.keyboard.type(seldate); +// // await page.$eval('input[id=date]', (el, seldate) => el.setAttribute("value", seldate), seldate); +// process.stdout.write("DATE: " + seldate + "\n"); +// // await page.evaluate(() => document.querySelector('input[id=date]').value = seldate); +// // await page.$eval('input[id=date]', (e, seldate) => { +// // e.setAttribute("value", seldate), +// // seldate +// // }); +// await delay(500); +// const curdate2 = await page.evaluate(() => document.querySelector('input[id=date]').value); +// process.stdout.write("CURDATE2: " + curdate2 + "\n"); +// } catch (err) { +// process.stdout.write("ERR3: " + err + "\n"); +// } +// } + // select timestep + 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 " + 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, spainZoom]); + } 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, spainZoom]); + } + + 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 spainZoom = process.argv[6]; // default: OD550_DUST + +process.stdout.write("START -> ANIM: " + anim + " CURMODEL: " + curmodel + " DATE: " + seldate + " VAR: " + variable + "spainZoom: " + spainZoom + "\n"); +RunCluster(anim, curmodel, seldate, variable, spainZoom); + diff --git a/tabs/forecast.py b/tabs/forecast.py index 0ac932f..648e9a2 100644 --- a/tabs/forecast.py +++ b/tabs/forecast.py @@ -393,6 +393,10 @@ def sidebar_forecast(variables, default_var, models, default_model): ), html.Span([ html.Button('APPLY', id='models-apply', n_clicks=0), + ]), + #add hidden spain button for gifs over spain + html.Span([ + html.Button('Spain', id='spain', n_clicks=0, style={'display':'none'}), ], )], ) diff --git a/tabs/forecast_callbacks.py b/tabs/forecast_callbacks.py index 78d5273..7619d78 100644 --- a/tabs/forecast_callbacks.py +++ b/tabs/forecast_callbacks.py @@ -866,6 +866,13 @@ def register_callbacks(app, cache, cache_timeout): return {}, {}, dash.no_update # raise PreventUpdate +# ) +# @cache.memoize(timeout=cache_timeout) +# @app.callback( +# [ +# Output({'tag': 'model-map', 'index': ALL, "n_clicks": ALL}, 'zoom'), +# Output({'tag': 'model-map', 'index': ALL, "n_clicks": ALL}, 'center')], +# [Input('spain', 'n_clicks')], # retrieve timeseries according to coordinates selected @app.callback( @@ -922,6 +929,27 @@ def register_callbacks(app, cache, cache_timeout): return dash.no_update, False, [0 for _ in ts_button] # raise PreventUpdate + @app.callback( + Output({'tag': 'model-map', 'index': ALL, "n_clicks": ALL}, 'zoom'), + Output({'tag': 'model-map', 'index': ALL, "n_clicks": ALL}, 'center'), + Input('spain', 'n_clicks'), + [State('slider-graph', 'value'), + State('model-date-picker', 'date'), + State('model-dropdown', 'value'), + State('variable-dropdown-forecast', 'value'), + State('slider-interval', 'disabled'), + State({'tag': 'view-style', 'index': ALL}, 'active'), + State({'tag': 'model-map', 'index': ALL, "n_clicks": ALL}, 'zoom'), + State({'tag': 'model-map', 'index': ALL, "n_clicks": ALL}, 'center'), + State({'tag': 'model-map', 'index': ALL, "n_clicks": ALL}, 'id'), + ], + ) + def zoom_spain(n_clicks, tstep, date, model, variable, static, view, zoom, center, ids): + """zoom maps over spain""" + count = len(model) + zoom=[5] + center=[[40, -4]] + return zoom*count, center*count # start/stop animation @app.callback( -- GitLab From b8f1da70924e69062aeb462e12ddc6cbf747d1ce Mon Sep 17 00:00:00 2001 From: Elliott Rose Date: Mon, 23 Jan 2023 09:37:02 +0100 Subject: [PATCH 2/6] Increase zoom value and add context comment --- js/create_spain_loop.js | 2 +- tabs/forecast_callbacks.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/create_spain_loop.js b/js/create_spain_loop.js index b8a3a1a..7ff1654 100644 --- a/js/create_spain_loop.js +++ b/js/create_spain_loop.js @@ -1,4 +1,4 @@ -// I have no idea why this will run if I put it in the create_model_loop file, but not here +// create a gif focused over Spain const { Cluster } = require('puppeteer-cluster'); const util = require('util'); diff --git a/tabs/forecast_callbacks.py b/tabs/forecast_callbacks.py index 7619d78..d3d6d7b 100644 --- a/tabs/forecast_callbacks.py +++ b/tabs/forecast_callbacks.py @@ -947,7 +947,7 @@ def register_callbacks(app, cache, cache_timeout): def zoom_spain(n_clicks, tstep, date, model, variable, static, view, zoom, center, ids): """zoom maps over spain""" count = len(model) - zoom=[5] + zoom=[6] center=[[40, -4]] return zoom*count, center*count -- GitLab From df3b66b69ee685ba99b46882d156fbd5b8609ce5 Mon Sep 17 00:00:00 2001 From: Elliott Rose Date: Wed, 25 Jan 2023 11:37:31 +0100 Subject: [PATCH 3/6] Rework create_spain_loop to accept a country as a variable and zoom on the coordinates noted in json file --- .gitignore | 1 + conf/coords.json | 7 ++ ...e_spain_loop.js => create_country_loop.js} | 97 +++++++++---------- tabs/forecast.py | 7 +- tabs/forecast_callbacks.py | 23 ++--- 5 files changed, 68 insertions(+), 67 deletions(-) create mode 100644 conf/coords.json rename js/{create_spain_loop.js => create_country_loop.js} (72%) diff --git a/.gitignore b/.gitignore index abea78e..b831a6a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ log *.log *.swp *.swo +/js/tmp/ diff --git a/conf/coords.json b/conf/coords.json new file mode 100644 index 0000000..2de3a39 --- /dev/null +++ b/conf/coords.json @@ -0,0 +1,7 @@ +{ + "Spain":{ + "zoom":"5", + "lat":"34", + "lon": "-7" + } +} diff --git a/js/create_spain_loop.js b/js/create_country_loop.js similarity index 72% rename from js/create_spain_loop.js rename to js/create_country_loop.js index 7ff1654..998bd86 100644 --- a/js/create_spain_loop.js +++ b/js/create_country_loop.js @@ -1,11 +1,8 @@ -// create a gif focused over Spain - +// create a gif focused over Selected Country const { Cluster } = require('puppeteer-cluster'); const util = require('util'); -//const url = 'http://127.0.0.1:9000/daily_dashboard/' -const url = 'http://0.0.0.0:7778/dashboard/' -//const uniqueId = Date.now().toString(36) + Math.random().toString(36).substring(2); -// const imageTemplate = '/data/daily_dashboard/comparison/'; // + uniqueId + '_image'; +const url = 'http://127.0.0.1:9000/daily_dashboard/' +//const url = 'http://0.0.0.0:7778/dashboard/' const modelDict = {'od550_dust':'AOD', 'sconc_dust':'Concentration', 'dust_depd':'Dry deposition', @@ -13,13 +10,15 @@ const modelDict = {'od550_dust':'AOD', 'dust_load':'Load', 'dust_ext_sfc':'Extinction'}; +const coords = require('../conf/coords.json'); + function delay(time) { return new Promise(function(resolve) { setTimeout(resolve, time) }); } -const RunCluster = async (anim, curmodel, seldate, variable, spainZoom) => { +const RunCluster = async (anim, curmodel, seldate, variable, country) => { const cluster = await Cluster.launch({ concurrency: Cluster.CONCURRENCY_CONTEXT, puppeteerOptions: { @@ -39,8 +38,8 @@ const RunCluster = async (anim, curmodel, seldate, variable, spainZoom) => { var curmodel = args[1]; var seldate = args[2]; var variable = args[3]; - var spainZoom = args[4]; - process.stdout.write("TSTEP: " + tstep + " -- MOD: " + curmodel + " -- DATE: " + seldate + " -- VAR: " + variable +'spainZoom: ' + spainZoom + "\n"); + var country = args[4]; + process.stdout.write("TSTEP: " + tstep + " -- MOD: " + curmodel + " -- DATE: " + seldate + " -- VAR: " + variable +' country: ' + country + "\n"); await page.setViewport({ width: 1280, height: 768}); await page.goto(url, { waitUntil: 'networkidle0', @@ -111,43 +110,41 @@ const RunCluster = async (anim, curmodel, seldate, variable, spainZoom) => { } catch (err) { process.stdout.write("ERR2: " + err + "\n"); } - // apply hidden spain button - if (spainZoom == "true") { - try { - process.stdout.write("CLICK HIDDEN SPAIN BUTTON\n"); - await page.evaluate(() => { - document.querySelector('#spain').click(); - }); - await delay(500); - } catch (err) { - process.stdout.write("ERR2: " + err + "\n"); - } + // apply none country button + try { + var zoom =coords[country].zoom; + var lat = coords[country].lat; + var lon = coords[country].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); + //await page.screenshot({path: 'screenshot.png'}); + 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'); + //await page.screenshot({path: '2screenshot.png'}); + + }catch (err) { + process.stdout.write("ERR2: " + err + "\n"); } - // select date -// if (seldate !== "none") { -// try { -// await page.waitForSelector('input#date'); -// const curdate = await page.evaluate(() => document.querySelector('input[id=date]').value); -// process.stdout.write("CURDATE: " + curdate + "\n"); -// // const input = await page.$('input[id=date]'); -// await page.focus('input[id=date]'); // input.click(); -// await page.keyboard.type(""); -// await page.keyboard.type(seldate); -// // await page.$eval('input[id=date]', (el, seldate) => el.setAttribute("value", seldate), seldate); -// process.stdout.write("DATE: " + seldate + "\n"); -// // await page.evaluate(() => document.querySelector('input[id=date]').value = seldate); -// // await page.$eval('input[id=date]', (e, seldate) => { -// // e.setAttribute("value", seldate), -// // seldate -// // }); -// await delay(500); -// const curdate2 = await page.evaluate(() => document.querySelector('input[id=date]').value); -// process.stdout.write("CURDATE2: " + curdate2 + "\n"); -// } catch (err) { -// process.stdout.write("ERR3: " + err + "\n"); -// } -// } - // select timestep var num = "00"; if (tstep === "false") { process.stdout.write("CURRENT SELECTION: " + tstep + "\n"); @@ -194,7 +191,7 @@ const RunCluster = async (anim, curmodel, seldate, variable, spainZoom) => { for (let i=0; i<25; i++) { try { process.stdout.write("QUEUEING step:" + i + "\n"); - await cluster.queue([i, curmodel, seldate, variable, spainZoom]); + await cluster.queue([i, curmodel, seldate, variable, country]); } catch (err) { console.log(err); process.stdout.write("ERROR:" + err + "\n"); @@ -202,7 +199,7 @@ const RunCluster = async (anim, curmodel, seldate, variable, spainZoom) => { } } else { process.stdout.write("QUEUEING current:" + anim + "\n"); - cluster.queue([anim, curmodel, seldate, variable, spainZoom]); + cluster.queue([anim, curmodel, seldate, variable, country]); } await cluster.idle(); @@ -213,8 +210,8 @@ 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 spainZoom = process.argv[6]; // default: OD550_DUST +var country = process.argv[6]; // default: OD550_DUST -process.stdout.write("START -> ANIM: " + anim + " CURMODEL: " + curmodel + " DATE: " + seldate + " VAR: " + variable + "spainZoom: " + spainZoom + "\n"); -RunCluster(anim, curmodel, seldate, variable, spainZoom); +process.stdout.write("START -> ANIM: " + anim + " CURMODEL: " + curmodel + " DATE: " + seldate + " VAR: " + variable + "country: " + country + "\n"); +RunCluster(anim, curmodel, seldate, variable, country); diff --git a/tabs/forecast.py b/tabs/forecast.py index 648e9a2..2cf7730 100644 --- a/tabs/forecast.py +++ b/tabs/forecast.py @@ -394,9 +394,12 @@ def sidebar_forecast(variables, default_var, models, default_model): html.Span([ html.Button('APPLY', id='models-apply', n_clicks=0), ]), - #add hidden spain button for gifs over spain + #add hidden country fields and button for gifs over entered coords html.Span([ - html.Button('Spain', id='spain', n_clicks=0, style={'display':'none'}), + dcc.Input(id="country-zoom",type="text",style={'display':'none'}), + dcc.Input(id="country-lat", type="text",style={'display':'none'}), + dcc.Input(id="country-lon", type="text",style={'display':'none'}), + html.Button(id="country-focus", n_clicks=0,style={'display':'none'}), ], )], ) diff --git a/tabs/forecast_callbacks.py b/tabs/forecast_callbacks.py index d3d6d7b..04e4cf3 100644 --- a/tabs/forecast_callbacks.py +++ b/tabs/forecast_callbacks.py @@ -866,14 +866,6 @@ def register_callbacks(app, cache, cache_timeout): return {}, {}, dash.no_update # raise PreventUpdate -# ) -# @cache.memoize(timeout=cache_timeout) -# @app.callback( -# [ -# Output({'tag': 'model-map', 'index': ALL, "n_clicks": ALL}, 'zoom'), -# Output({'tag': 'model-map', 'index': ALL, "n_clicks": ALL}, 'center')], -# [Input('spain', 'n_clicks')], - # retrieve timeseries according to coordinates selected @app.callback( [Output('ts-modal', 'children'), @@ -932,23 +924,24 @@ def register_callbacks(app, cache, cache_timeout): @app.callback( Output({'tag': 'model-map', 'index': ALL, "n_clicks": ALL}, 'zoom'), Output({'tag': 'model-map', 'index': ALL, "n_clicks": ALL}, 'center'), - Input('spain', 'n_clicks'), + Input('country-focus', 'n_clicks'), [State('slider-graph', 'value'), State('model-date-picker', 'date'), State('model-dropdown', 'value'), State('variable-dropdown-forecast', 'value'), State('slider-interval', 'disabled'), State({'tag': 'view-style', 'index': ALL}, 'active'), - State({'tag': 'model-map', 'index': ALL, "n_clicks": ALL}, 'zoom'), - State({'tag': 'model-map', 'index': ALL, "n_clicks": ALL}, 'center'), + State('country-zoom', 'value'), + State('country-lat', 'value'), + State('country-lon', 'value'), State({'tag': 'model-map', 'index': ALL, "n_clicks": ALL}, 'id'), ], ) - def zoom_spain(n_clicks, tstep, date, model, variable, static, view, zoom, center, ids): - """zoom maps over spain""" + def zoom_country(n_clicks, tstep, date, model, variable, static, view, zoom, lat, lon, ids): + """Set zoom and center over coordinates entered""" count = len(model) - zoom=[6] - center=[[40, -4]] + zoom=[int(zoom)] + center=[[int(lat),int(lon)]] return zoom*count, center*count # start/stop animation -- GitLab From f0ad5c40429cc0b7c5f73cbf81aa328482dd269e Mon Sep 17 00:00:00 2001 From: Elliott Rose Date: Mon, 6 Feb 2023 17:50:35 +0100 Subject: [PATCH 4/6] Change scroll level sensitivity --- data_handler.py | 4 +++- tabs/forecast_callbacks.py | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/data_handler.py b/data_handler.py index da3b67a..0167138 100644 --- a/data_handler.py +++ b/data_handler.py @@ -1009,7 +1009,7 @@ class FigureHandler(object): if center is None: center = self.get_center(center) if zoom is None: - zoom = 3.5-(aspect[0]-aspect[0]*0.4) + zoom = 3.5 -(aspect[0]-aspect[0]*0.4) if colorbar is not None: colorbar.width = 320 - 25 * aspect[0] if DEBUG: print("ZOOM", zoom) @@ -1080,6 +1080,7 @@ class FigureHandler(object): colorbar, info ] + layer, + zoomSnap=0.1, zoom=zoom, center=center, id=dict( @@ -1089,6 +1090,7 @@ class FigureHandler(object): inertia=True, preferCanvas=True, animate=False, + minZoom=2, className="graph-with-slider", ) # if DEBUG: print("---", fig) diff --git a/tabs/forecast_callbacks.py b/tabs/forecast_callbacks.py index 04e4cf3..ef4f475 100644 --- a/tabs/forecast_callbacks.py +++ b/tabs/forecast_callbacks.py @@ -940,8 +940,8 @@ def register_callbacks(app, cache, cache_timeout): def zoom_country(n_clicks, tstep, date, model, variable, static, view, zoom, lat, lon, ids): """Set zoom and center over coordinates entered""" count = len(model) - zoom=[int(zoom)] - center=[[int(lat),int(lon)]] + zoom=[float(zoom)] + center=[[float(lat),float(lon)]] return zoom*count, center*count # start/stop animation -- GitLab From 35fe064a707966336bd2847c06f68f96a29b7edc Mon Sep 17 00:00:00 2001 From: Elliott Rose Date: Wed, 8 Feb 2023 12:57:02 +0100 Subject: [PATCH 5/6] Refactor and remove testing code --- bin/gen_country_loop.sh | 92 +++++++++++++++++++++++++++++++++++++++ js/create_country_loop.js | 28 +++++++----- 2 files changed, 108 insertions(+), 12 deletions(-) create mode 100755 bin/gen_country_loop.sh diff --git a/bin/gen_country_loop.sh b/bin/gen_country_loop.sh new file mode 100755 index 0000000..634a0e9 --- /dev/null +++ b/bin/gen_country_loop.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash + +########################################## +# country should be written with first letter as capital, or how appears in coords.conf +# date currently does not function and will return always today's date (or most recent available) +# only monarch accepts all variables, the rest get od550_dust and sconc_dust +# ./bin/gen_country_loop.sh monarch true sconc_dust 20220808 spain + +# generate daily maps for specified country +# usage: script.sh [model] [anim] [variable] [date] [country] + +if [ "$1" != "" ]; then + model="$1" +else + model="all" +fi + +if [ "$2" != "" ]; then + anim="$2" +else + anim="true" +fi + +if [ "$3" != "" ]; then + curvar="$3" +else + curvar="all" +fi + +if [ "$4" != "" ]; then + curdate="$4" +else + curdate=`date '+%y%m%d' -d '-1day'` +fi + +if [ "$5" != "" ]; then + country="$5" +else + country="Spain" +fi + +curyear=`date '+%y' -d ${curdate}` +curmon=`date '+%m' -d ${curdate}` +repodir='/data/daily_dashboard/comparison/' + +for var in od550_dust sconc_dust dust_depd dust_depw dust_load dust_ext_sfc +do + variable=$var + echo "************ $model $anim $curvar $variable $curdate $country****************" + if [ "$curvar" != "$variable" ] && [ "$curvar" != "all" ]; then + continue + fi + tmpdir=${home}/tmp/${var}/${country} + mkdir -p $tmpdir + rm -rf $tmpdir/* + if [ "$variable" != "od550_dust" ] && [ "$variable" != "sconc_dust" ]; then + model="monarch" + node js/create_country_loop.js $anim $model $curdate $variable $country + wait + sleep 1 + convert -loop 0 -delay 25 ${tmpdir}/${curdate}_${model}_${country}_??.png ${tmpdir}/${curdate}_${model}_${country}_loop.gif + currepo=${repodir}/${model}/${variable}/${curyear}/${curmon}/${country}/ + mkdir -p $currepo + wait + mv ${tmpdir}/${curdate}_${model}_${country}_* $currepo + + elif [ "$model" == "all" ]; then + for mod in `cat interactive-forecast-viewer/conf/models.json | grep '": {' | sed 's/^.*"\(.*\)".*$/\1/g'` + do + node js/create_country_loop.js $anim $mod $curdate $variable $country + wait + sleep 1 + convert -loop 0 -delay 25 ${tmpdir}/${curdate}_${mod}_${country}_??.png ${tmpdir}/${curdate}_${mod}_${country}_loop.gif + currepo=${repodir}/${mod}/${variable}/${curyear}/${curmon}/${country}/ + mkdir -p $currepo + wait + mv ${tmpdir}/${curdate}_${mod}_${country}_* $currepo + done + else + node js/create_country_loop.js $anim $model $curdate $variable $country + wait + sleep 1 + convert -loop 0 -delay 25 ${tmpdir}/${curdate}_${model}_${country}_??.png ${tmpdir}/${curdate}_${model}_${country}_loop.gif + currepo=${repodir}/${model}/${variable}/${curyear}/${curmon}/${country}/ + mkdir -p $currepo + wait + mv ${tmpdir}/${curdate}_${model}_${country}_* $currepo + fi + + wait + sleep 1 +done diff --git a/js/create_country_loop.js b/js/create_country_loop.js index 998bd86..3733ed2 100644 --- a/js/create_country_loop.js +++ b/js/create_country_loop.js @@ -1,8 +1,14 @@ -// create a gif focused over Selected Country +// CREATE A GIF FOCUSED GIF OR PNG OVER SELECTED COUNTRY +// 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 +// country should have first letter capitalized, or coincide to coords.conf +// node create_country_loop true monarch 20220808 od550_dust Spain + const { Cluster } = require('puppeteer-cluster'); const util = require('util'); -const url = 'http://127.0.0.1:9000/daily_dashboard/' -//const url = 'http://0.0.0.0:7778/dashboard/' +const path = require('path'); +const url = 'http://0.0.0.0:7778/dashboard/' const modelDict = {'od550_dust':'AOD', 'sconc_dust':'Concentration', 'dust_depd':'Dry deposition', @@ -10,7 +16,9 @@ const modelDict = {'od550_dust':'AOD', 'dust_load':'Load', 'dust_ext_sfc':'Extinction'}; -const coords = require('../conf/coords.json'); +//READ COORDINATES AND CENTER FROM CONF FILE +const relpath = path.relative('create_country_loop.js', 'conf/coords.json'); +const coords = require(relpath); function delay(time) { return new Promise(function(resolve) { @@ -26,7 +34,6 @@ const RunCluster = async (anim, curmodel, seldate, variable, country) => { args: [ '--no-sandbox', '--disable-setuid-sandbox', - // '--start-maximized', ] }, maxConcurrency: 4, @@ -38,7 +45,7 @@ const RunCluster = async (anim, curmodel, seldate, variable, country) => { var curmodel = args[1]; var seldate = args[2]; var variable = args[3]; - var country = args[4]; + var country = args[4]; process.stdout.write("TSTEP: " + tstep + " -- MOD: " + curmodel + " -- DATE: " + seldate + " -- VAR: " + variable +' country: ' + country + "\n"); await page.setViewport({ width: 1280, height: 768}); await page.goto(url, { @@ -131,7 +138,6 @@ const RunCluster = async (anim, curmodel, seldate, variable, country) => { await page.type('#country-zoom', zoom); await page.type('#country-lat', lat); await page.type('#country-lon', lon); - //await page.screenshot({path: 'screenshot.png'}); process.stdout.write("CLICK HIDDEN COUNTRY BUTTON\n"); await page.click('#country-focus'); @@ -140,7 +146,6 @@ const RunCluster = async (anim, curmodel, seldate, variable, country) => { await latInput.evaluate((el) => el.style.display = 'none'); await lonInput.evaluate((el) => el.style.display = 'none'); await zoomButton.evaluate((el) => el.style.display = 'none'); - //await page.screenshot({path: '2screenshot.png'}); }catch (err) { process.stdout.write("ERR2: " + err + "\n"); @@ -180,8 +185,7 @@ const RunCluster = async (anim, curmodel, seldate, variable, country) => { } }, '.leaflet-bar'); - //await page.waitForSelector(".graph-with-slider"); - const outputFile = './tmp/' + variable.toLowerCase() + '/' + seldate + '_' + curmodel + '_' + num + '.png'; + const outputFile = './tmp/' + variable.toLowerCase() + '/' + seldate + '_' + curmodel + '_' + country + '_' + num + '.png'; process.stdout.write("TAKE SCREENSHOT => " + outputFile + "\n"); await graph.screenshot({ path: outputFile }); @@ -210,8 +214,8 @@ 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 country = process.argv[6]; // default: OD550_DUST +var country = process.argv[6]; // default: Spain -process.stdout.write("START -> ANIM: " + anim + " CURMODEL: " + curmodel + " DATE: " + seldate + " VAR: " + variable + "country: " + country + "\n"); +process.stdout.write("START -> ANIM: " + anim + " CURMODEL: " + curmodel + " DATE: " + seldate + " VAR: " + variable + " country: " + country + "\n"); RunCluster(anim, curmodel, seldate, variable, country); -- GitLab From 1ac69cb13e91ad7c5fe85c1e7566f8314969147d Mon Sep 17 00:00:00 2001 From: Elliott Rose Date: Wed, 8 Feb 2023 13:50:34 +0100 Subject: [PATCH 6/6] Add lines to remove pngs after gif generation --- bin/gen_country_loop.sh | 12 ++++++++---- js/create_country_loop.js | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/bin/gen_country_loop.sh b/bin/gen_country_loop.sh index 634a0e9..7fe2c59 100755 --- a/bin/gen_country_loop.sh +++ b/bin/gen_country_loop.sh @@ -4,9 +4,10 @@ # country should be written with first letter as capital, or how appears in coords.conf # date currently does not function and will return always today's date (or most recent available) # only monarch accepts all variables, the rest get od550_dust and sconc_dust +# anim should only be called with true here, as the pngs are automatically deleted # ./bin/gen_country_loop.sh monarch true sconc_dust 20220808 spain -# generate daily maps for specified country +# generate daily maps for specified couuntry # usage: script.sh [model] [anim] [variable] [date] [country] if [ "$1" != "" ]; then @@ -59,10 +60,11 @@ do wait sleep 1 convert -loop 0 -delay 25 ${tmpdir}/${curdate}_${model}_${country}_??.png ${tmpdir}/${curdate}_${model}_${country}_loop.gif - currepo=${repodir}/${model}/${variable}/${curyear}/${curmon}/${country}/ + currepo=${repodir}/${model}/${variable}/${curyear}/${curmon}/${country} mkdir -p $currepo wait mv ${tmpdir}/${curdate}_${model}_${country}_* $currepo + rm $currepo/${curdate}_${model}_${country}_??.png elif [ "$model" == "all" ]; then for mod in `cat interactive-forecast-viewer/conf/models.json | grep '": {' | sed 's/^.*"\(.*\)".*$/\1/g'` @@ -71,20 +73,22 @@ do wait sleep 1 convert -loop 0 -delay 25 ${tmpdir}/${curdate}_${mod}_${country}_??.png ${tmpdir}/${curdate}_${mod}_${country}_loop.gif - currepo=${repodir}/${mod}/${variable}/${curyear}/${curmon}/${country}/ + currepo=${repodir}/${mod}/${variable}/${curyear}/${curmon}/${country} mkdir -p $currepo wait mv ${tmpdir}/${curdate}_${mod}_${country}_* $currepo + rm $currepo/${curdate}_${mod}_${country}_??.png done else node js/create_country_loop.js $anim $model $curdate $variable $country wait sleep 1 convert -loop 0 -delay 25 ${tmpdir}/${curdate}_${model}_${country}_??.png ${tmpdir}/${curdate}_${model}_${country}_loop.gif - currepo=${repodir}/${model}/${variable}/${curyear}/${curmon}/${country}/ + currepo=${repodir}/${model}/${variable}/${curyear}/${curmon}/${country} mkdir -p $currepo wait mv ${tmpdir}/${curdate}_${model}_${country}_* $currepo + rm $currepo/${curdate}_${model}_${country}_??.png fi wait diff --git a/js/create_country_loop.js b/js/create_country_loop.js index 3733ed2..416d8e5 100644 --- a/js/create_country_loop.js +++ b/js/create_country_loop.js @@ -8,7 +8,7 @@ const { Cluster } = require('puppeteer-cluster'); const util = require('util'); const path = require('path'); -const url = 'http://0.0.0.0:7778/dashboard/' +const url = 'http://127.0.0.1:9000/daily_dashboard/' const modelDict = {'od550_dust':'AOD', 'sconc_dust':'Concentration', 'dust_depd':'Dry deposition', -- GitLab