worldmap-viewer

git clone https://git.godosa.eu/worldmap-viewer

master

raw · 3067 bytes

// skypanel.js — Sky & seasons bar: day/moment sliders, play, sun marker, tropics and polar circles
import { $ } from './ui.js';
import { sunVec, subsolar, dayFraction, seasonName, TILT_DEG, YEAR_DAYS, DAY_MOMENTS } from './sky.js';
import { PLANET } from './planet.js';

const parallel = lat => Array.from({ length: 181 }, (_, i) => ({ lat, lon: -180 + 2 * i }));
export const SKY_LINES = [TILT_DEG, -TILT_DEG, 90 - TILT_DEG, TILT_DEG - 90].map(parallel);
const int = v => Math.round(v).toLocaleString('en-US');

export function dayLabel(day) {
  const s = seasonName(day);
  return `day ${day} of ${YEAR_DAYS}${s ? ` · ${s}` : ''}`;
}
const pad = v => String(v).padStart(2, '0');
export const momentLabel = mo => (PLANET.moment_s ? `moment ${int(mo)} of ${int(DAY_MOMENTS)}`
  : `${pad(Math.floor(mo / 60))}:${pad(Math.round(mo) % 60)}`);
export function daylightLabel(lat, day) {
  const f = dayFraction(lat, day);
  return `daylight ${Math.round(f * 100)}% (${(f * PLANET.day_hours).toFixed(1)} h)`;
}

export class SkyPanel {
  constructor(app) {
    this.app = app;
    this.timer = null;
    this.day = $('#sky-day');
    this.mo = $('#sky-mo');
    this.day.max = YEAR_DAYS - 1;
    this.mo.max = DAY_MOMENTS - 1;
    $('#sky-toggle').addEventListener('click', () => this.set(!app.state.sky));
    this.day.addEventListener('input', () => { app.state.day = Number(this.day.value); this.apply(); });
    this.mo.addEventListener('input', () => { app.state.mo = Number(this.mo.value); this.apply(); });
    $('#sky-play').addEventListener('click', () => this.play(!this.timer));
    this.set(app.state.sky);
  }

  set(on) {
    this.app.state.sky = on;
    $('#skybar').hidden = !on;
    $('#sky-toggle').classList.toggle('on', on);
    if (!on) this.play(false);
    this.apply();
  }

  play(on) {   // one day per 10 s
    clearInterval(this.timer);
    this.timer = null;
    $('#sky-play').textContent = on ? '⏸' : '▶';
    if (!on) return;
    this.timer = setInterval(() => {
      const s = this.app.state;
      s.mo += Math.round(DAY_MOMENTS / 200);
      if (s.mo >= DAY_MOMENTS) {
        s.mo -= DAY_MOMENTS;
        s.day = (s.day + 1) % YEAR_DAYS;
      }
      this.apply();
    }, 50);
  }

  apply() {
    const s = this.app.state, sun = subsolar(s.day, s.mo);
    this.day.value = String(s.day);
    this.mo.value = String(s.mo);
    $('#sky-day-label').textContent = dayLabel(s.day);
    $('#sky-mo-label').textContent = momentLabel(s.mo);
    this.app.setSun(s.sky ? sunVec(s.day, s.mo) : null);
    this.app.overlays.setMarkers('sun', s.sky ? [{ lat: sun.lat, lon: sun.lon, label: '☉' }] : []);
    if (this.linesOn !== s.sky) {   // the parallels only change when the sky is switched
      this.app.overlays.setExtra('sky', s.sky ? SKY_LINES : [], 0xffd166);
      this.linesOn = s.sky;
    }
    this.refresh();
  }

  refresh() {   // day length at the view centre
    if (!this.app.state.sky || !this.app.view) return;
    $('#sky-daylen').textContent = daylightLabel(this.app.view.getState().lat, this.app.state.day);
  }
}