Public
Edited
Oct 11, 2023
2 forks
Insert cell
Insert cell
Insert cell
Insert cell
data
Insert cell
chart = {
const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.style("border", "1px solid lightgrey");

// Draw axes
//const gx = svg.append("g").call(xAxis, xScale);
//const gy = svg.append("g").call(yAxis, yScale);
const gx = drawAxis(svg, xScale, "bottom", height - margin.bottom)[0];
const gy = drawAxis(svg, yScale, "left", margin.left)[0];

// Draw circles
const gc = svg.append("g");
const circles = gc
.selectAll("circle")
.data(data)
.join("circle")
.style("fill", "steelblue") //d => cScale(d.PopGrowthRate)).style('opacity', 0.8)
//.style("fill", (d) => cScale(d.PopGrowthRate))
.style("opacity", 0.8)
.attr("cx", (d) => xScale(d.Longitude))
.attr("cy", (d) => yScale(d.Latitude))
.attr("r", 3);
//.attr("r", d => aScale(d.Population));
circles.append("title").text((d) => d.FullName);

// Zooming code goes here...

//setupBrush(svg, xScale.copy(), yScale.copy());
//setupZoom(svg, xScale, yScale);
//setupZoomTransf(svg, circles, gc);

// Update with new x&y scales for zooming (without re-running the whole cell)
svg.node().update = function (xScaleNew, yScaleNew) {
circles
.attr("cx", (d) => xScaleNew(d.Longitude))
.attr("cy", (d) => yScaleNew(d.Latitude));
//gx.call(xAxis, xScaleNew);
//gy.call(yAxis, yScaleNew);
gx.update(xScaleNew);
gy.update(yScaleNew);
};

return svg.node();

resetbutton; // re-execute this cell when button value is updated (pressed)
}
Insert cell
Insert cell
xScale = d3
.scaleLinear()
.domain(d3.extent(data, (r) => r.Longitude))
//.domain([-90, -67])
.range([margin.left, width - margin.right])
//.range([-900, 944])
Insert cell
xScale.domain() // view the x domain extent in Longitude coords
Insert cell
xScale.range() // view the x range extent in pixels
Insert cell
yScale = d3
.scaleLinear()
.domain(d3.extent(data, (r) => r.Latitude))
.range([height - margin.bottom, margin.top])
Insert cell
Insert cell
function setupBrush(svg, xscale, yscale) {
// D3 Brush API
const brush = d3.brush().on("end", brushing); // Only zoom on brush 'end' event
const gb = svg.append("g").call(brush);

// Brush event callback
function brushing(event) {
if (event.selection) {
const [[x0, y0], [x1, y1]] = (mutable brushCoords = event.selection); // Get brush pixel coordinates
// Do brush zoom here...

// update the scales
xscale.domain([xscale.invert(x0), xscale.invert(x1)]); // x.invert = pixel --> Longitude
yscale.domain([yscale.invert(y1), yscale.invert(y0)]);
// update the plot, given new x & y scales
svg.node().update(xscale, yscale);
gb.call(brush.clear);
}
}
}
Insert cell
mutable brushCoords = null
Insert cell
viewof resetbutton = Inputs.button("Reset Map")
Insert cell
Insert cell
function setupZoom(svg, xscale, yscale) {
// D3 Zoom API
const extent = [
[margin.left, margin.top],
[width - margin.right, height - margin.top]
];
const zoom = d3
.zoom()
.extent(extent) // Where the interaction occurs
.translateExtent(extent) // Limits panning to the original extent
.scaleExtent([1, 32]) // Sets the maximum zoom factor
.on("zoom", zooming);
svg.call(zoom);

// Zoom event callback
function zooming(event) {
mutable transform = event.transform;
// Do zooming here, event.transform expresses the pan+zoom from original x & y scales

// update the scales
const xzoom = event.transform.rescaleX(xscale); // Apply zoom to xscale
const yzoom = event.transform.rescaleY(yscale); // Apply zoom to yscale
// update the plot
svg.node().update(xzoom, yzoom);
}
}
Insert cell
mutable transform = null
// k = scale; x,y = translate
// zooming is exponential, double click changes scale by factor of 2
Insert cell
transform
Insert cell
Insert cell
// Zoom slider
viewof zoom = html`<input type="range" min=0 max=8 value=0 step=0.1>`
Insert cell
zoom
Insert cell
{
// Compute new transform
var transf = d3.zoomIdentity
//.translate(width / 2, height / 2)
.scale(2 ** zoom); // exponential zoom
//.translate(-width / 2, -height / 2);
// Apply transform to Scales
const xzoom = transf.rescaleX(xScale);
const yzoom = transf.rescaleY(yScale);
// Update plot
chart.update(xzoom, yzoom);
}
Insert cell
Insert cell
function setupZoomTransf(svg, circles, gc) {
// D3 Zoom API
const extent = [
[margin.left, margin.top],
[width - margin.right, height - margin.top]
];
const zoom = d3
.zoom()
.extent(extent) // Where the interaction occurs
.translateExtent(extent) // Limits panning to the original extent
.scaleExtent([1, 32]) // Sets the maximum zoom factor
.on("zoom", zooming);
svg.call(zoom);

// Zoom event callback
function zooming(event) {
mutable transform = event.transform;
// Do zooming here, event.transform expresses the pan+zoom from original x & y scales

// Geometric zoom:
gc.attr("transform", event.transform);

// Spatial zoom:
//circles.attr("cx", d => event.transform.applyX(xScale(d.Longitude)))
// .attr("cy", d => event.transform.applyY(yScale(d.Latitude)));
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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