Public
Edited
Apr 23
Fork of Time Zones
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// https://observablehq.com/@bayre/svg-swatches
function swatches({
colour,
swatchRadius = 7.5,
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 * 6 + swatchPadding * 6
const svg = d3.create("svg")
.attr("id", "swatches")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.style("margin-top", "-28px")
.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
formatLongitude = x => `${Math.round((x+18) / 36) - 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
coor = [[[-18, -90], [-18, 90], [18, 90], [18, -90], [-18, -90], ]]
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
projection = d3.geoMiller().rotate([-148])
Insert cell
path = d3.geoPath(projection)
Insert cell
width = 975
Insert cell
height = {
const [[x0, y0], [x1, y1]] = d3.geoPath(projection.fitWidth(width, outline)).bounds(outline);
const dy = Math.ceil(y1 - y0), l = Math.min(Math.ceil(x1 - x0), dy);
projection.scale(projection.scale() * (l - 1) / l).precision(0.2);
return dy;
}
Insert cell
outline = ({type: "Sphere"})
Insert cell
graticule = d3.geoGraticule().stepMinor([15, 10])()
Insert cell
timezones = FileAttachment("timezones.json").json()
Insert cell
zones = topojson.feature(timezones, timezones.objects.timezones).features
Insert cell
mesh = topojson.mesh(timezones, timezones.objects.timezones)
Insert cell
land = FileAttachment("land-50m.json").json().then(topology => topojson.feature(topology, topology.objects.land))
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
d3 = require("d3@6", "d3-geo-projection@2")
Insert cell
topojson = require("topojson-client@3")
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
<style>
div:has(> svg#swatches) {
display: flex;
justify-content: center;
}
</style>
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