Public
Edited
Feb 24, 2023
Fork of Moon Grid
2 forks
47 stars
Insert cell
Insert cell
Insert cell
Insert cell
viewof title = html`<input type="text" value="PHASES OF THE MOON IN 2023, AS SEEN FROM HELSINKI" />`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof cut = html`<input type=checkbox />`
Insert cell
Insert cell
blackCuts = days.map((d, i) => {
// Full circles
const x = dayScale(d, i);
const y = rowScale(d, i);
return pathCircle(x, y, MOON_R);
})
Insert cell
pathCircle = (x, y, r) =>
`M ${x - r} ${y} A ${r} ${r} 180 1 0 ${x + r} ${y} A ${r} ${r} 180 1 0 ${x -
r} ${y}`
Insert cell
whiteCuts = days.map((d, i) => {
// Dark side of moon
const x = dayScale(d, i);
const y = rowScale(d, i);
const time = d3.timeHour.offset(d, 12); // 12 for noon

const { phase, angle } = suncalc.getMoonIllumination(time);
const lightAngle = 180 - phase * 360;
const darkAngle = 180 + lightAngle;

const [latitude, longitude] = coords;
// const { rise } = suncalc.getMoonTimes(d, latitude, longitude, true);
// return rise;

const { parallacticAngle } = suncalc.getMoonPosition(
time,
latitude,
longitude
);
const rotationZ = ((angle - parallacticAngle) / TAU) * 360; // * -1;

// Close enough to full moon; too small to cut
if (Math.abs(lightAngle) < 8) return '';

return (
projection.rotate([darkAngle, 0, rotationZ]).translate([x, y]),
path(hemisphere)
);
})
Insert cell
registrationCuts = {
const REG_RADIUS = inch(1 / 64);
const MARGIN = inch(1 / 4) + REG_RADIUS;
return [
pathCircle(MARGIN, MARGIN, REG_RADIUS), // TL
pathCircle(width - MARGIN, MARGIN, REG_RADIUS), // TR
pathCircle(width - MARGIN, height - MARGIN, REG_RADIUS), // BR
pathCircle(MARGIN, height - MARGIN, REG_RADIUS) // BL
];
}
Insert cell
year = startDate.getUTCFullYear()
Insert cell
inch = n => pRound(n * 72)
Insert cell
Insert cell
Insert cell
MOON_R = 14
Insert cell
//moonGridCutCompile = compileSVG(moonGrid)
Insert cell
Insert cell
TAU = Math.PI*2
Insert cell
radians = n => n/360 * TAU
Insert cell
pointOnArc = (r, a) =>
pRound(Math.sin(radians(a)) * r) + " " + pRound(Math.cos(radians(a)) * r)
Insert cell
pRound = n => Math.round(n * 100000) / 100000
Insert cell
isoDate = d => d.toISOString().split('T')[0]
Insert cell
projection = d3
.geoOrthographic()
.translate([0, 0])
.scale(MOON_R)
Insert cell
path = d3.geoPath(projection)
Insert cell
hemisphere = d3.geoCircle()()
Insert cell
ROWS = 13
Insert cell
COLS = 28
Insert cell
dayScale = {
const scale = d3
.scalePoint()
.domain(d3.range(COLS))
.range([margin.left, width - margin.right])
.padding(1);
return (d, i) => {
return pRound(scale(i % COLS));
};
}
Insert cell
rowScale = {
const scale = d3
.scalePoint()
.domain(d3.range(ROWS))
.range([margin.top, height - margin.bottom])
.padding(1);
return (d, i) => {
const offset = i % 2 !== 0 ? (MOON_R * 4) / 3 : 0;
return pRound(scale(Math.floor(i / COLS)) + offset);
};
}
Insert cell
days = {
const oneDay = 24 * 60 * 60 * 1000;
return d3.timeDay.range(startDate - oneDay, endDate);
}
Insert cell
months = {
const now = new Date(year, 0, 1);
const start = d3.timeYear(now);
return d3.timeMonths(start, d3.timeYear.offset(start, 1));
}
Insert cell
width = inch(WIDTH)
Insert cell
height = inch(HEIGHT)
Insert cell
margin = ({
top: inch(3 / 4),
right: inch(1 / 2),
bottom: inch(1 / 2),
left: inch(1 / 2)
})
Insert cell
Insert cell
suncalc = require("suncalc@1")
Insert cell
d3 = require("d3@5")
Insert cell
import { renderTextArray, renderTextPaths } from '@meetamit/hershey-fonts'
Insert cell
import { compileSVG } from '@forresto/svg-to-cnc'
Insert cell
import { worldMapCoordinates } from "@jashkenas/inputs"
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more