Public
Edited
Jan 4, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
canvas = {
const fix = (x, p = 2) => +x.toFixed(p);
const deg = (n) => +fix(360 * n);
const rad = (r) => 2 * Math.PI * r;
const scale = d3
.scaleLinear()
.domain([-0.5 * Math.PI, 1.5 * Math.PI])
.range([0, 2 * Math.PI]);
const context = DOM.context2d(edge, edge);
// sun path
context.beginPath();
context.moveTo(edge / 2, edge / 2);
context.strokeStyle = "rgba(255,255,0,.5)";
context.lineWidth = 16;
context.lineCap = "butt";
context.lineTo(
edge / 2 + (edge / 2) * 0.8 * Math.cos(scale(currentAzimuth)),
edge / 2 + (edge / 2) * 0.8 * Math.sin(scale(currentAzimuth))
);
context.stroke();
// end sun path
// reverse
context.beginPath();
context.moveTo(edge / 2, edge / 2);
context.strokeStyle = "rgba(200,200,200,.35)";
context.lineWidth = 16;
context.lineTo(
edge / 2 + (edge / 2) * 0.8 * Math.cos(scale(reverseAzimuth)),
edge / 2 + (edge / 2) * 0.8 * Math.sin(scale(reverseAzimuth))
);
context.stroke();
// reverse end
// arc path
context.beginPath();
context.lineWidth = 5;
context.lineCap = "round";
context.shadowBlur = 5;
context.shadowColor = "#aaa";
context.strokeStyle = "#fff";
context.arc(
edge / 2,
edge / 2,
(edge / 2) * 0.8,
scale(SunriseAzimuth),
scale(SunsetAzimuth)
);
context.stroke();
// end arc path
// "sun orb"
context.shadowBlur = 0;
context.beginPath();
context.arc(
edge / 2 + (edge / 2) * 0.8 * Math.cos(scale(currentAzimuth)),
edge / 2 + (edge / 2) * 0.8 * Math.sin(scale(currentAzimuth)),
10,
0,
2 * Math.PI
);
context.strokeStyle = "#fff";
context.lineWidth = 10;
context.shadowBlur = 10;
context.fillStyle = "#ff0";
context.stroke();
context.shadowBlur = 0;
context.fill();
// end "sun orb"

return context.canvas;
}
Insert cell
{
const sunMarker = new maplibre.Marker(canvas).setLngLat(loc).addTo(map);
const marker = new maplibre.Marker().setLngLat(loc).addTo(map);
invalidation.then(() => {
sunMarker.remove();
marker.remove();
});
}
Insert cell
currentAzimuth = {
return SunCalc.getPosition(
new Date(`2023-${month.n}-21`).setHours(
Math.floor(time / 60),
Math.round((time / 60 - Math.floor(time / 60)) * 60),
0
),
loc.lat,
loc.lng
).azimuth;
}
Insert cell
Insert cell
edge = 300
Insert cell
SunriseAzimuth = SunCalc.getPosition(times.sunrise, loc.lat, loc.lng).azimuth
Insert cell
SunsetAzimuth = SunCalc.getPosition(times.sunset, loc.lat, loc.lng).azimuth
Insert cell
times = SunCalc.getTimes(date, loc.lat, loc.lng)
Insert cell
displayTime = {
const t = new Date(`2023-${month.n}-21`).setHours(
Math.floor(time / 60),
Math.round((time / 60 - Math.floor(time / 60)) * 60),
0
);
return formatter.format(t);
}
Insert cell
formatter = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
hour12: false,
minute: "numeric",
// weekday: "long",
timeZone: loc.tz
});
Insert cell
date = new Date(`2023-${month.n}-21`)
Insert cell
dayStraddle = times.sunset.getDate() > times.sunrise.getDate()
Insert cell
months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
].map((d, i) => ({ m: d, n: i + 1 < 10 ? "0" + (i + 1) : i + 1 }))
Insert cell
Insert cell
Insert cell
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