Public
Edited
Nov 18, 2023
Insert cell
Insert cell
pie = {
// Dimensions
const width = 500;
const height = 500;
const radius = Math.min(width, height) / 2;


// Create SVG container
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height)
.attr('viewBox', [ -width / 2, -height / 2, width, height])
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');

// Create pie generator
const pie = d3.pie()
.value(d => d.value)
.sort(null);

// Create arc generator with rounded corners
const arc = d3.arc()
.outerRadius(radius - 10)
.innerRadius(radius / 2)
.cornerRadius(15);

// Create arcs
const arcs = svg.selectAll('.arc')
.data(pie(data))
.enter().append('g')
.attr('class', 'arc');

// Draw arc paths
arcs.append('path')
.attr('d', arc)
.style('fill', function(d, i) { return color(i); }) // Assign colors
.attr('stroke', 'white')
.style('stroke-width', '4px');

return svg.node()
}
Insert cell
veroni = {
// Dimensions for the SVG
const width = 500;
const height = 500;
// Create the SVG container
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);

var simulation = d3.voronoiMapSimulation(data)
.weight(function(d){ return d.value; }) // set the weight accessor
.clip([[0,0], [0,height], [width, height], [width,0]]) // set the clipping polygon
.stop(); // immediately stops the simulation

var state = simulation.state(); // retrieve the simulation's state, i.e. {ended, polygons, iterationCount, convergenceRatio}

while (!state.ended) { // manually launch each iteration until the simulation ends
simulation.tick();
state = simulation.state();
}

var polygons = state.polygons;


// Run the simulation
simulation.tick(500); // Run for 500 iterations, or use a loop to run until stability

// Get the polygons from the simulation

console.log(polygons)
// Function to generate path "d" attribute from a polygon
function polygonPath(polygon) {
return polygon ? "M" + polygon.join("L") + "Z" : null;
}

const clipPathId = 'rounded-clip';

// Define a clip path with a rounded rectangle
svg.append('clipPath')
.attr('id', clipPathId)
.append('rect')
.attr('width', width)
.attr('height', height)
.attr('rx', 130) // Adjust the radius for rounded corners here
.attr('ry', 130);
// Draw Voronoi cells
svg.selectAll(".voronoi")
.data(polygons)
.enter().append("path")
.attr("class", "voronoi")
.attr("d", polygon => polygonPath(polygon))
.attr("stroke", "white")
.attr("stroke-width", 4)
.attr("fill", d => {
return color(d.site.originalObject.data.originalData.name)
})
.attr("clip-path", `url(#${clipPathId})`); // Apply the clip path here


return svg.node();
}
Insert cell
veroniMap = {
// Dimensions for the SVG

const width = 500;
const height = 500;

const californiaGeoJSON = topojson.feature(us, us.objects.states).features.filter(d => d.properties.name === 'New York');

const projection = d3.geoAlbers().fitSize([width, height], californiaGeoJSON[0])

function multiPolygonToPoints(multiPolygonFeature, projection) {
let points = [];

// Check if the feature is a MultiPolygon
if (multiPolygonFeature.type === 'Feature' && multiPolygonFeature.geometry.type === 'MultiPolygon') {
let multiPolygon = multiPolygonFeature.geometry.coordinates;

// Iterate through each polygon in the MultiPolygon
for (let polygon of multiPolygon) {
// Iterate through each linear ring in the polygon
for (let linearRing of polygon) {
// Iterate through each point in the linear ring
for (let point of linearRing) {
points.push(point);
}
}
}
}

return points.map(p => projection(p));
}
// Create the clipping polygon from the California GeoJSON
const clippingPolygon = multiPolygonToPoints(californiaGeoJSON[0], projection);


// Create the SVG container
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);

// Create a generic Albers projection

// Define a geoPath generator
const geoPath = d3.geoPath().projection(projection);
// Assuming californiaGeoJSON[0] is your GeoJSON object and projection is your D3 projection
var simulation = d3.voronoiMapSimulation(data)
.weight(function(d){ return d.value; })
.clip(clippingPolygon)
.stop();

var state = simulation.state();
while (!state.ended) {
simulation.tick();
state = simulation.state();
}

var polygons = state.polygons;

console.log(polygons)
// Function to generate path "d" attribute from a polygon
function polygonPath(polygon) {
return polygon ? "M" + polygon.join("L") + "Z" : null;
}

svg.append("clipPath")
.attr("id", "california-clip")
.append("path")
.datum(californiaGeoJSON[0]) // Make sure this is the correct feature for California
.attr("d", geoPath);

// Draw Voronoi cells
svg.selectAll(".voronoi")
.data(polygons)
.enter().append("path")
.attr("class", "voronoi")
.attr("d", d => polygonPath(d))
.attr("stroke", "white")
.attr("clip-path", `url(#${"california-clip"})`) // Apply the clipping path here
.attr("stroke-width", 4)
.attr("fill", d => {
return color(d.site.originalObject.data.originalData.name)
});

// // Draw the California path
// svg.append("path")
// .data(californiaGeoJSON)
// .attr("d", geoPath)
// .attr("fill", "lightgray")
// .attr("stroke", "white");


return svg.node()
}
Insert cell
data = [
{ name: 'Turkey', value: 64 },
{ name: 'Chicken', value: 24 },
{ name: 'Beef', value: 8 },
{ name: 'Lamb', value: 4 }
];
Insert cell
color = d3.scaleOrdinal(['#f87171', '#38bdf8', '#4ade80', '#fdba74']).domain(data.map(({ name }) => name))
Insert cell
d3 = require("d3@6", "d3-weighted-voronoi", "d3-voronoi-map@2", 'seedrandom@2.4.3/seedrandom.min.js', 'd3-exploder')
Insert cell
california = topojson.feature(us, us.objects.states).features.filter(d => d.properties.name === 'California')
Insert cell
us = d3.json("https://unpkg.com/us-atlas@3/counties-10m.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