Public
Edited
Dec 11, 2022
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
l = L(θs, φs, θp, φp)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
import { julianDate, solar } from "@mbostock/star-map/2"
Insert cell
Insert cell
Insert cell
todaySunPosition = dt.map(d => sun(d, lat, lon))
Insert cell
Insert cell
Insert cell
function L(θp, φp, θs, φs) {
const { acos, cos, sin, transpose, pow } = math;
const [AY, BY, CY, DY, EY] = _.flatten(G_Y.toArray());
const [Ax, Bx, Cx, Dx, Ex] = _.flatten(G_x.toArray());
const [Ay, By, Cy, Dy, Ey] = _.flatten(G_y.toArray());

const 𝛾p = acos(sin(θs) * sin(θp) * cos(φp - φs) + cos(θs) * cos(θp));

const l = (A, B, C, D, E) =>
(1 + A * pow(Math.E, 1 / cos(θp))) *
(1 + C * pow(Math.E, D * 𝛾p) + E * pow(cos(𝛾p), 2));

const LY = l(AY, BY, CY, DY, EY);
const Lx = l(Ax, Bx, Cx, Dx, Ex);
const Ly = l(Ay, By, Cy, Dy, Ey);

return [LY, Lx, Ly];
}
Insert cell
c = L(Math.PI / 0.9, 0.92, 0.91, 0.9)
Insert cell
rgb_color = lrgb_rgb(xyzd50_lrgb(c))
Insert cell
viewof cl = Inputs.color({ value: rgb_color })
Insert cell
import {
tristimulus_d50,
rgb_lrgb,
lrgb_rgb,
lrgb_xyzd50,
xyzd50_lrgb,
xyzd50_lab,
lab_xyzd50
} from "@mbostock/lab-and-rgb"
Insert cell
Insert cell
function sunPosition(date, lat, lon) {
const positionRadians = suncalc.getPosition(date, lat, lon);
const azimuth = rad2deg(positionRadians.azimuth) + 180;
const altitude = rad2deg(positionRadians.altitude);
return {
azimuth,
altitude,
zenith: 90 - altitude
};
}
Insert cell
rad2deg = (rad) => (180 * rad) / Math.PI
Insert cell
beta = 0.1
Insert cell
Insert cell
globe(attitude().angles([0, 0, 0]))
Insert cell
function globe(attitude, options = {}) {
// Adapted from https://observablehq.com/@fil/attitude
const margin = 10;
const width = 400;
const height = 400;
const context = DOM.context2d(width, height);

const extent = [
[margin, margin],
[height - margin, width - margin]
];
const projection = d3
.geoOrthographic()
.fitExtent(extent, { type: "Sphere" })
.rotate(attitude.angles());

const path = d3.geoPath(projection, context);

const horizon = d3.geoCircle().center([lon, lat]).radius(90);
const sunPos = sun(new Date(), lat, lon);

function drawGlobe() {
context.strokeStyle = "#000";
context.fillStyle = "#ddd";

// World
context.beginPath();
context.fillStyle = "#eee";
path(world);
context.fill();

// Graticule
context.beginPath();
context.strokeStyle = "#000";
context.lineWidth = 0.25;
path(d3.geoGraticule10());
context.stroke();

drawLocation();
drawSunPath();

// Outline
context.beginPath();
context.strokeStyle = "#000";
path({ type: "Sphere" });
context.lineWidth = 1;
context.stroke();
}

function drawLocation() {
context.fillStyle = "red";
context.strokeStyle = "red";
context.lineWidth = 1;

// Location Point
context.beginPath();
path({ type: "Point", coordinates: [lon, lat] });
context.fill();

// Horizon
context.beginPath();
path(horizon());
context.stroke();
}

function drawSunPath() {
context.beginPath();
context.strokeStyle = "orange";
path({
type: "Polygon",
coordinates: [
todaySunPosition.map((d) => [
d.azimuth_deg + 180,
-1 * (90 - lat - d.altitude_deg)
])
]
});
context.stroke();

context.beginPath();
context.fillStyle = "orange";
path({
type: "Point",
coordinates: [
(sunPos.azimuth * 180) / Math.PI + 180,
-1 * (90 - lat - sunPos.altitude_deg)
]
});
context.fill();
}

function render() {
context.save();
context.clearRect(0, 0, width, height);

drawGlobe();
}

d3.select(context.canvas)
.style("cursor", "grab")
.call(
drag(projection, options)
.on("drag.render", render)
.on("end.render", render)
);

return d3.select(context.canvas).call(render).node();
}
Insert cell
startToday = luxon.DateTime.now().startOf('day')
Insert cell
endToday = luxon.DateTime.now().endOf('day')
Insert cell
Insert cell
dt = d3.range(startToday, endToday, 60*1000)
Insert cell
dt.map(d => new Date(d))
Insert cell
function sun(date, lat, lon) {
const _sun = suncalc.getPosition(date, lat, lon)
return {
date: new Date(date),
altitude: _sun.altitude,
azimuth: _sun.azimuth,
altitude_deg: rad2deg(_sun.altitude),
azimuth_deg: rad2deg(_sun.azimuth)
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
attitude = require("attitude")
Insert cell
import { drag} from '@fil/attitude'
Insert cell
todaySunPosition.map((d) => [d.azimuth_deg, d.altitude_deg])
Insert cell
sunShape = ({
type: "Point",
coordinates: [
rad2deg(sun(new Date(), lat, lon).azimuth) + 180,
sun(new Date(), lat, lon).altitude_deg
]
})
Insert cell
function formatDecimal(value, places = 2) {
return Intl.NumberFormat(undefined, {
minimumFractionDigits: places,
maximumFractionDigits: places
}).format(value);
}
Insert cell
function printMatrix(name, matrix) {
return tex.block`
${name} =
\begin{pmatrix}
${matrix.map((cells) => cells.join(" & ")).join("\\\\")}
\end{pmatrix}
`;
}
Insert cell
printMatrix("M_Y", M_Y.toArray())
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more