Public
Edited
Feb 25
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
urlSun = `https://api.met.no/weatherapi/sunrise/3.0/sun?lat=${place.lat}&lon=${place.lon}&date=${date}`
Insert cell
sunData = fetch(urlSun).then(response => response.json())
Insert cell
Insert cell
urlMoon = `https://api.met.no/weatherapi/sunrise/3.0/moon?lat=${place.lat}&lon=${place.lon}&date=${date}`
Insert cell
moonData = fetch(urlMoon).then(response => response.json())
Insert cell
Insert cell
{
const width = 400, height = 300;
const toRadians = (degrees) => degrees * (Math.PI / 180);
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);

const g = svg.append("g")
.attr("transform", `translate(${width / 2},${height / 2})`);

// Draw the compass arc (background circle)
g.append("path")
.attr("d", d3.arc()({
innerRadius: 105,
outerRadius: 110,
startAngle: 0,
endAngle: Math.PI * 2
}))
.attr("fill", "black");

// Draw the arc representing the sun's path
g.append("path")
.attr("d", d3.arc()({
innerRadius: 100,
outerRadius: 120,
startAngle: toRadians(sunData.properties.sunrise.azimuth),
endAngle: toRadians(sunData.properties.sunset.azimuth)
}))
.attr("fill", "red");

// Draw the arc representing the moon's path
g.append("path")
.attr("d", d3.arc()({
innerRadius: 130,
outerRadius: 140,
startAngle: toRadians(moonData.properties.moonrise.azimuth),
endAngle: toRadians(moonData.properties.moonset.azimuth)
}))
.attr("fill", "blue");

// Helper: convert polar (r, angle in radians) to Cartesian coordinates.
function polarToCartesian(r, angleInRadians) {
return {
x: r * Math.cos(angleInRadians - Math.PI / 2),
y: r * Math.sin(angleInRadians - Math.PI / 2)
};
}

// --- Add labels for sun and moon events ---
// For sun events, use a radius slightly outside the sun arc.
const sunLabelRadius = 125;
const sunriseAngle = toRadians(sunData.properties.sunrise.azimuth);
const sunsetAngle = toRadians(sunData.properties.sunset.azimuth);
let pos = polarToCartesian(sunLabelRadius, sunriseAngle);
g.append("text")
.attr("x", pos.x)
.attr("y", pos.y)
.attr("text-anchor", "start")
// adjust dy for vertical offset
.attr("dy", "-0.5em")
.text("Sunrise")
.style("fill","red");

pos = polarToCartesian(sunLabelRadius, sunsetAngle);
g.append("text")
.attr("x", pos.x)
.attr("y", pos.y)
.attr("text-anchor", "end")
.attr("dy", "-0.5em")
.text("Sunset")
.style("fill","red");

// For moon events, use a radius outside the moon arc.
const moonLabelRadius = 145;
const moonriseAngle = toRadians(moonData.properties.moonrise.azimuth);
const moonsetAngle = toRadians(moonData.properties.moonset.azimuth);

pos = polarToCartesian(moonLabelRadius, moonriseAngle);
g.append("text")
.attr("x", pos.x)
.attr("y", pos.y)
.attr("text-anchor", "start")
.attr("dy", "-0.5em")
.text("Moonrise")
.style("fill","blue");

pos = polarToCartesian(moonLabelRadius, moonsetAngle);
g.append("text")
.attr("x", pos.x)
.attr("y", pos.y)
.attr("text-anchor", "end")
.attr("dy", "-0.5em")
.text("Moonset")
.style("fill","blue");

// --- Add cardinal direction labels ---
// We'll position these outside the compass ring.
const cardinalLabelRadius = 80;
const cardinals = [
{ label: "N", angle: 0 },
{ label: "E", angle: 90 },
{ label: "S", angle: 180 },
{ label: "W", angle: 270 }
];

cardinals.forEach(d => {
const pos = polarToCartesian(cardinalLabelRadius, toRadians(d.angle));
g.append("text")
.attr("x", pos.x)
.attr("y", pos.y)
.attr("text-anchor", "middle")
.attr("dy", "0.35em")
.style("font-weight", "bold")
.text(d.label);
});
return svg.node();
}

Insert cell
Insert cell
corsProxy = "https://corsproxy.io/?url="
Insert cell
fetch(corsProxy + encodeURIComponent(urlSun))
.then(response => response.json())
Insert cell
fetch(corsProxy + encodeURIComponent(urlMoon))
.then(response => response.json())
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