Small tweaks
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
File diff suppressed because one or more lines are too long
@@ -12,20 +12,48 @@
|
|||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
export function checkStargazing(rows) {
|
export function checkStargazing(rows) {
|
||||||
// Great stargazing: mostly clear night hours with low cloud
|
// Great stargazing: mostly clear night hours with low cloud.
|
||||||
|
// Split night into contiguous runs (pre-dawn vs post-dusk) and evaluate
|
||||||
|
// each independently so a cloudy evening doesn't suppress a clear morning
|
||||||
|
// (or vice versa), and so the icon is pinned to only the clear hours.
|
||||||
const nightRows = rows.filter(r => r.elev < -5);
|
const nightRows = rows.filter(r => r.elev < -5);
|
||||||
if (nightRows.length < 3) return null;
|
if (nightRows.length < 3) return null;
|
||||||
const avgCloud = nightRows.reduce((s, r) => s + r.cc, 0) / nightRows.length;
|
|
||||||
if (avgCloud > 30) return null;
|
// Build contiguous runs — a gap > 2 h means we've crossed daytime.
|
||||||
|
const runs = [];
|
||||||
|
let cur = [nightRows[0]];
|
||||||
|
for (let i = 1; i < nightRows.length; i++) {
|
||||||
|
const dtDiff = (new Date(nightRows[i].iso) - new Date(nightRows[i - 1].iso)) / 3600000;
|
||||||
|
if (dtDiff <= 2) { cur.push(nightRows[i]); }
|
||||||
|
else { runs.push(cur); cur = [nightRows[i]]; }
|
||||||
|
}
|
||||||
|
runs.push(cur);
|
||||||
|
|
||||||
|
// Evaluate each run; among those passing ≤30% avg cloud prefer the evening
|
||||||
|
// run (actionable for tonight) then fall back to whichever is clearest.
|
||||||
|
let bestRun = null, bestAvg = Infinity;
|
||||||
|
for (const run of runs) {
|
||||||
|
if (run.length < 2) continue;
|
||||||
|
const avg = run.reduce((s, r) => s + r.cc, 0) / run.length;
|
||||||
|
if (avg > 30) continue;
|
||||||
|
const isEvening = parseInt(run[0].iso.slice(11, 13), 10) >= 12;
|
||||||
|
if (!bestRun || (isEvening && parseInt(bestRun[0].iso.slice(11, 13), 10) < 12) || avg < bestAvg) {
|
||||||
|
bestRun = run; bestAvg = avg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!bestRun) return null;
|
||||||
|
|
||||||
|
const isPreDawn = parseInt(bestRun[0].iso.slice(11, 13), 10) < 12;
|
||||||
return {
|
return {
|
||||||
id: 'stargazing',
|
id: 'stargazing',
|
||||||
emoji: '⭐',
|
emoji: '⭐',
|
||||||
title: 'Great Stargazing Tonight',
|
title: isPreDawn ? 'Great Stargazing Overnight' : 'Great Stargazing Tonight',
|
||||||
message: `Clear skies expected overnight at ${nightRows.length} hours with average ${Math.round(avgCloud)}% cloud — ideal conditions for stargazing.`,
|
message: `Clear skies expected for ${bestRun.length} hours overnight with average ${Math.round(bestAvg)}% cloud — ideal conditions for stargazing.`,
|
||||||
color: '#080e1a',
|
color: '#080e1a',
|
||||||
textColor: '#c8dcff',
|
textColor: '#c8dcff',
|
||||||
type: 'weather',
|
type: 'weather',
|
||||||
nightOnly: true,
|
nightOnly: true,
|
||||||
|
isoRange: [bestRun[0].iso, bestRun[bestRun.length - 1].iso],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-2
@@ -18,9 +18,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"2026-06-11": {
|
"2026-06-11": {
|
||||||
"visits": 28,
|
"visits": 31,
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"showall": 1
|
"showall": 11,
|
||||||
|
"custom": 9,
|
||||||
|
"alltemps": 1,
|
||||||
|
"vehicle": 3,
|
||||||
|
"home": 4,
|
||||||
|
"basic": 5,
|
||||||
|
"farming": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"2026-06-12": {
|
||||||
|
"visits": 1,
|
||||||
|
"profiles": {
|
||||||
|
"showall": 1,
|
||||||
|
"basic": 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>scope cell</title>
|
|
||||||
<link rel="stylesheet" href="./assets/css/header.css" />
|
|
||||||
<style>
|
|
||||||
html,body { margin:0; height:100%; background: transparent; overflow: hidden; }
|
|
||||||
#cell { display:flex; align-items:center; justify-content:center; height:100%; }
|
|
||||||
.scope-ring { width: 200px; height: 200px; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="cell"></div>
|
|
||||||
<script type="module">
|
|
||||||
import { h, render } from './assets/vendor/preact.js';
|
|
||||||
const { ScopeReticle } = await import('./assets/js/components.js?v=' + Date.now());
|
|
||||||
|
|
||||||
const q = new URLSearchParams(location.search);
|
|
||||||
const n = (k, d) => { const v = q.get(k); return v == null ? d : Number(v); };
|
|
||||||
|
|
||||||
// hour drives the rising/setting palette (UTC hour < 12 => sunrise tints).
|
|
||||||
const dt = new Date(Date.UTC(2026, 5, 6, n('hour', 12), 0, 0));
|
|
||||||
const cat = { label: 'Comfortable', bg: '#90d090', fg: '#157a15' };
|
|
||||||
const storm = q.get('storm') === '1';
|
|
||||||
|
|
||||||
render(h(ScopeReticle, {
|
|
||||||
value: 12.4, cat, loading: false,
|
|
||||||
elev: n('elev', 30), dt, glob: n('glob', 400),
|
|
||||||
activeEvent: storm ? { id: 'storm' } : null,
|
|
||||||
cc: n('cc', 0), ccLow: n('ccLow', 0), ccMid: n('ccMid', 0), ccHigh: n('ccHigh', 0),
|
|
||||||
precip: n('precip', 0), snow: n('snow', 0), visKm: n('vis', 30),
|
|
||||||
env: q.get('env') || 'open',
|
|
||||||
wind: n('wind', 0), gust: n('gust', 0), wd: q.get('wd') != null ? n('wd', 0) : null,
|
|
||||||
}), document.getElementById('cell'));
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<title>SunScope — Ground Lab (dev)</title>
|
|
||||||
<style>
|
|
||||||
body { margin: 0; background: #1a140c; font-family: Manrope, system-ui, sans-serif; color: #f5edd6; }
|
|
||||||
h1 { text-align: center; font-weight: 700; font-size: 18px; padding: 14px 0 2px; opacity: 0.8; }
|
|
||||||
p.note { text-align:center; opacity:0.45; font-size:12px; margin:0 0 10px; }
|
|
||||||
.row-label { text-align:center; font-size:12px; font-weight:700; opacity:0.6; margin:14px 0 2px; letter-spacing:1px; text-transform:uppercase; }
|
|
||||||
.grid { display: grid; grid-template-columns: repeat(8, 150px); gap: 6px 8px; justify-content: center; padding: 0 12px; }
|
|
||||||
.cell { display: flex; flex-direction: column; align-items: center; }
|
|
||||||
.label { font-size: 11px; font-weight: 700; opacity: 0.85; }
|
|
||||||
iframe { width: 150px; height: 150px; border: 0; background: transparent; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>SunScope — Ground Lab</h1>
|
|
||||||
<p class="note">dev preview · per-environment grounds across the cycle</p>
|
|
||||||
<div id="lab"></div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const envs = ['open','forest','alpine','urban','beach','openwater','desert','river'];
|
|
||||||
const envName = { open:'Grass', forest:'Forest', alpine:'Alpine', urban:'City', beach:'Beach', openwater:'Open Water', desert:'Desert', river:'River' };
|
|
||||||
|
|
||||||
// [rowLabel, base query]
|
|
||||||
const rows = [
|
|
||||||
['Midday · clear', 'elev=45&hour=12&glob=800&cc=0&vis=30'],
|
|
||||||
['Sunset · clear', 'elev=2&hour=19&glob=110&cc=10&vis=30'],
|
|
||||||
['Night · clear', 'elev=-15&hour=23&glob=0&cc=10&vis=30'],
|
|
||||||
['Midday · overcast','elev=45&hour=12&glob=300&cc=95&ccLow=90&ccMid=70&vis=18'],
|
|
||||||
['Day · rain', 'elev=40&hour=12&glob=220&cc=98&ccLow=92&ccMid=75&precip=5&vis=6'],
|
|
||||||
['Day · snow', 'elev=30&hour=12&glob=200&cc=95&ccLow=90&snow=1.2&vis=4'],
|
|
||||||
];
|
|
||||||
|
|
||||||
const lab = document.getElementById('lab');
|
|
||||||
for (const [rowLabel, q] of rows) {
|
|
||||||
const rl = document.createElement('div');
|
|
||||||
rl.className = 'row-label';
|
|
||||||
rl.textContent = rowLabel;
|
|
||||||
lab.appendChild(rl);
|
|
||||||
const grid = document.createElement('div');
|
|
||||||
grid.className = 'grid';
|
|
||||||
for (const env of envs) {
|
|
||||||
const cell = document.createElement('div');
|
|
||||||
cell.className = 'cell';
|
|
||||||
const lbl = document.createElement('span');
|
|
||||||
lbl.className = 'label';
|
|
||||||
lbl.textContent = envName[env];
|
|
||||||
const ifr = document.createElement('iframe');
|
|
||||||
ifr.src = `./scope-cell?${q}&env=${env}&_=${Date.now()}`;
|
|
||||||
ifr.loading = 'eager';
|
|
||||||
cell.append(lbl, ifr);
|
|
||||||
grid.append(cell);
|
|
||||||
}
|
|
||||||
lab.appendChild(grid);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Reference in New Issue
Block a user