Public
Edited
Apr 23
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
projection = choice.value().rotate([-148, 0])
Insert cell
coor = [[[-18, -90], [-18, 90], [18, 90], [18, -90], [-18, -90], ]]
Insert cell
viewof s = Inputs.range([0, 1000], {label: "satur", value: 1000, step: 1})
Insert cell
viewof l = Inputs.range([0, 1000], {label: "light", value: 800, step: 1})
Insert cell
viewof a = Inputs.range([0, 1000], {label: "alpha", value: 1000, step: 1})
Insert cell
hsla10 = [
`hsla(0, ${s / 10}%, ${l / 10}%, ${a / 10}%)`, // red
// `hsla(340, ${s / 10}%, ${l / 10}%, ${a / 10}%)`, // magentared
`hsla(300, ${s / 10}%, ${l / 10}%, ${a / 10}%)`, // magenta
`hsla(280, ${s / 10}%, ${l / 10}%, ${a / 10}%)`, // violet
`hsla(240, ${s / 10}%, ${l / 10}%, ${a / 10}%)`, // blue
`hsla(200, ${s / 10}%, ${l / 10}%, ${a / 10}%)`, // azure
`hsla(180, ${s / 10}%, ${l / 10}%, ${a / 10}%)`, // cyan
// `hsla(160, ${s / 10}%, ${l / 10}%, ${a / 10}%)`, // cyangreen
`hsla(120, ${s / 10}%, ${l / 10}%, ${a / 10}%)`, // green
`hsla(80, ${s / 10}%, ${l / 10}%, ${a / 10}%)`, // lime
`hsla(60, ${s / 10}%, ${l / 10}%, ${a / 10}%)`, // yellow
`hsla(40, ${s / 10}%, ${l / 10}%, ${a / 10}%)`, // orange
`hsla(0, ${s / 10}%, ${l / 10}%, ${a / 10}%)`, // red
]
Insert cell
deczones = [...Array(10).keys()].map(
i => ({
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [coor[0].map(t => [t[0]+36*i, t[1]])]},
"properties": []
})
)
Insert cell
// adapted from https://observablehq.com/@mbostock/autosize-svg
getLabelLength = (label, labelFont = "12px sans-serif") => {
const id = DOM.uid("label").id;
const svg = html`<svg>
<style> .${id} { font: ${labelFont} } </style>
<g id=${id}>
<text class="${id}">${DOM.text(label)}</text>
</g>
</svg>`;

// Add the SVG element to the DOM so we can determine its size.
document.body.appendChild(svg);

// Compute the bounding box of the content.
const width = svg.getElementById(id).getBBox().width;

// Remove the SVG element from the DOM.
document.body.removeChild(svg);

return width;
}
Insert cell
// https://observablehq.com/@bayre/svg-swatches
function swatches({
colour,
swatchRadius = 7.25,
swatchPadding = swatchRadius * (2/3),
labelFont = "12px sans-serif",
labelFormat = x => x,
labelPadding = swatchRadius * 1.5,
marginLeft = 0
} = {}) {
const spacing = colour
.domain()
.map(d => labelFormat(d))
.map(d => getLabelLength(d, labelFont) + (swatchRadius * 2) + swatchPadding + labelPadding)
.map((_, i, g) => d3.cumsum(g)[i] + marginLeft)
const width = d3.max(spacing)
const height = swatchRadius * 2 + swatchPadding * 2
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.style("overflow", "visible")
.style("display", "block");
const g = svg
.append("g")
.attr("transform", `translate(0, ${height / 2})`)
.selectAll("g")
.data(colour.domain())
.join("g")
.attr("transform", (d, i) => `translate(${spacing[i - 1] || marginLeft}, 0)`);
g.append("circle")
.attr("fill", colour)
.attr("stroke", "black")
.attr("r", swatchRadius)
.attr("cx", swatchRadius)
.attr("cy", 0);
g.append("text")
.attr("x", swatchRadius * 2 + swatchPadding)
.attr("y", 0)
.attr("dominant-baseline", "central")
.style("font", labelFont)
.text(d => labelFormat(d));
return svg.node()
}
Insert cell
sun = {
const now = new Date;
const day = new Date(+now).setUTCHours(0, 0, 0, 0);
const t = solar.century(now);
const longitude = (day - now) / 864e5 * 360 - 180;
return [longitude - solar.equationOfTime(t) / 4, solar.declination(t)];
}
Insert cell
night = d3.geoCircle()
.radius(90)
.center(antipode(sun))
()
Insert cell
antipode = ([longitude, latitude]) => [longitude + 180, -latitude]
Insert cell
solar = require("solar-calculator@0.3/dist/solar-calculator.min.js")
Insert cell
countries = fetch("https://cdn.jsdelivr.net/npm/world-atlas@2/countries-50m.json").then(response => response.json())
Insert cell
borders = topojson.mesh(countries, countries.objects.countries, (a, b) => a !== b)
Insert cell
formatLatitude = y => `${y / 36}`
Insert cell
// formatLongitude = x => `${Math.abs(x)}°${x < 0 ? "W" : "E"}`
formatLongitude = x => `${(x+18) / 36}`
Insert cell
path = d3.geoPath(projection)
Insert cell
margin = ({top: 10, right: 10, bottom: 10, left: 10})
Insert cell
height = {
const [[x0, y0], [x1, y1]] = d3.geoPath(projection.fitWidth(width, outline)).bounds(outline);
const dx = x1 - x0;
const k = (dx - margin.left - margin.right) / dx;
const dy = (y1 - y0) * k + margin.bottom + margin.top;
projection.scale(projection.scale() * k);
projection.translate([(dx + margin.left - margin.right) / 2, (dy + margin.top - margin.bottom) / 2]);
projection.precision(0.2);
return Math.round(dy);
}
Insert cell
outline = ({type: "Sphere"})
Insert cell
Insert cell
graticule = d3.geoGraticule().stepMinor([36,36]).stepMajor([36,36])()
Insert cell
graticule.coordinates = graticule.coordinates.map(
i => i.map(j => j.map((k, index, arr) => i.length === 3 && index === 0 ? k - 18 : k))
)
Insert cell
graticule.coordinates
Insert cell
land = topojson.feature(world, world.objects.land)
Insert cell
world = FileAttachment("land-50m.json").json()
Insert cell
topojson = require("topojson-client@3")
Insert cell
d3 = require("d3@6", "d3-array@2", "d3-geo@2", "d3-geo-projection@3")
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