ฦ’

calculang โœ๏ธ editable and dangerous! ๐Ÿง™โ€โ™‚๏ธโš ๏ธ
javascript โœจ generated from calculang โฌ†๏ธ
dev tools ๐Ÿงฐ

reactive workings (todo):

Path of โ˜€๏ธ and ๐ŸŒ

Replicating sun/moon position calcs from SunCalc; compare results by layering both models below!

location ๐ŸŒŽ (default: Brรบ na Bรณinne, Ireland)
const lat_in = view(Inputs.range([-360,360], {label: 'latitude', value: 53.694712}))
const lng_in = view(Inputs.range([-360,360], {label: 'longitude', value: -6.475492}))

const clip1 = view(Inputs.toggle({value:true}))
const clip2 = view(Inputs.toggle())

date below

๐Ÿšง even though I replicate numbers, I need to do some DST and other time-related thinking

const spec = ({
  // vega-lite
  mark: {type:'point', size:400, filled:true,tooltip:true, clip:false, strokeWidth:0.2},
  //transform: [{calculate: 'datum.obj_in == "moon" ? (datum.eclipse_guestimate < 0.005 ? "๐ŸŒš" : "๐ŸŒ") : "โ˜€๏ธ"', as: 'annotation'},{calculate: 'datum.eclipse_guestimate < 0.0016 ? true : false', as: 'eclipse'}],
  encoding: {
    stroke: {field:'obj_in'},
    strokeWidth: {value: 2},
    strokeOpacity: {value:200},
    opacity: {value: 0.8},
    size: {value: 150},
    y: {grid: false,field: 'altitude_obj', type:'quantitative', axis: { values: [0,1], title: 'altitude (rads)'}, scale: {ticks: [0],zero: false, domain: clip2 ? [0,1] : [-1,1.5]}},
    x: {grid:false,field: 'azimuth_obj', type: 'quantitative', axis: {title: 'azimuth (rads)'}, scale: {zero: false, domain: !clip2 ? [-3,3] : [-0.5,2.5]}},
    shape: {field: 'model_id'},
    order: {field: 'model_id', sort:'descending'},
    color: {field: 'time_in', sort:'ascending', type: 'quantitative', timeUnit: 'hoursminutes', legend:true, scale: {scheme: 'lightmulti'}
    }
  },
  background: '#00000000',
  width:500*.8 * (clip1 ? 1 : 0.5),
  height:400*.8 * (clip1 ? 1 : 0.5),
  data: { name: "data" },
})

// interactivity via vega signals and listeners
const viz = embed('#viz', spec, {actions:true})
const data_source = calcuvegadata({
  models: model_option == 'calculang' ? [model] : model_option.includes('both') ? [model, suncalc_js_wrapped] : [suncalc_js_wrapped],
  spec,
  domains: {
    obj_in: ['sun','moon'],
    date_in: [date_in],
    // all days (for analemma)
    //date_in: [new Date(),new Date(),new Date(),new Date(),new Date(),new Date(),new Date(),new Date(), ..._.range(0,365+1, 5).map(d => addDays(date_in, d))/*, ..._.range(1,121).map(d => addDays(date_in, -d))*/],//[new Date()],//[new Date(),new Date(),new Date(),new Date(),new Date(),new Date(),new Date(),new Date(), ..._.range(0,365+1, 5).map(d => addDays(date_in, d))/*, ..._.range(1,121).map(d => addDays(date_in, -d))*/],
    time_in: _.range(0,25.1,1).map(hrs => addMinutes(date_in, hrs*60))
  },
  input_cursors: [
    { lat_in, lng_in, date_in }
  ]
})
viz.view.data("data", data_source)/*.resize()*/.run();

SunCalc wrapper for comparisons:

// wrapper around SunCalc: the JS model by Vladimir Agafonkin I replicate
// which itself is bassed on Astronomy Answers formulae: https://aa.quae.nl/en/reken/hemelpositie.html

// wrapper allows me to call it consistent with calculang model
// so I can layer onto the same visual
// to check calculang numbers/progress

// the JS model is suncalc_js_wrapped
// the calculang model is simply model, as in other examples

import {default as SunCalc} from 'npm:suncalc'

const suncalc_js_wrapped = {
  time_composed: ({date_in, time_in}) => new Date(date_in.getFullYear(), date_in.getMonth(), date_in.getDate(), time_in.getHours(), time_in.getMinutes(), time_in.getSeconds()),

  altitude_obj: ({obj_in, date_in, time_in, lat_in, lng_in }) => {
    const time_composed = suncalc_js_wrapped.time_composed({date_in,time_in});

    if (obj_in == 'sun') return SunCalc.getPosition(time_composed, lat_in, lng_in).altitude
    else return SunCalc.getMoonPosition(time_composed, lat_in, lng_in).altitude
  },

  azimuth_obj: ({obj_in, time_in, lat_in, lng_in }) => {
    const time_composed = suncalc_js_wrapped.time_composed({date_in,time_in});

    if (obj_in == 'sun') return SunCalc.getPosition(time_composed, lat_in, lng_in).azimuth
    else return SunCalc.getMoonPosition(time_composed, lat_in, lng_in).azimuth
  },
}