Public
Edited
Mar 25, 2024
Importers
Insert cell
Insert cell
chart()
Insert cell
selected_curve = d3.curveStep
Insert cell
function createToolTip(container) {
const divObj = d3.create("div").attr("class", "checkit").style("position", "absolute")
divObj.node().innerHTML = "Hello "
if (container) container.node().parentElement.appendChild(divObj.node())
return {
el: divObj.node(),
moveTo: (pos) => {
divObj.style("top", pos[1]+"px").style("left", pos[0] + "px")
},
display: (d, pos) => {
divObj.style("opacity", "1")
divObj.style("top", pos[1]+"px").style("left", pos[0] + "px")
divObj.node().innerHTML = d
},
hide: () => {
divObj.style("opacity", "0")
}
}
}
Insert cell
function chart() {
let btnPanel = undefined;
const svg = d3
.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("style", "border: 1px solid #eee")
.attr("xmlns", "http://www.w3.org/2000/svg");
// gradient(svg);

svg.call(xAxis);
svg.call(yAxis);
const brush = d3
.brush()
.extent([
[0, 0],
[width, height]
])
.on("end", brushedRectBrushStartBrush);
svg.append("g").attr("class", "brush").call(brush);
let tooltip;
const barArea = svg
.selectAll(".g-area")
.data([0])
.join("g")
.selectAll("path")
.data([data["attn_dist"].map((d, i) => [d, i])])
.join("path")
.attr("d", (d) => {
return area(d);
})
.style("stroke-width", 4)
.style("fill", "none")

const bar = svg
.selectAll(".g-bar")
.data([0])
.join("g")
.selectAll("path")
.data([data["attn_dist"].map((d, i) => [d, i])])
.join("path")
.attr("d", (d) => {
return lineBuilder(d);
})
.style("stroke", (d) => color(d[4]))
.style("stroke-width", 2)
.style("fill", "transparent");

const dots = svg
.append("g")
.selectAll("circle")
.data(data["attn_dist"].map((d, i) => [d, i]))
.join("circle")
.attr("class", "dot")
.attr("r", 2)
.attr("fill", "black")
.attr("stroke", "white")
.attr("stroke-width", 1)
.attr("cx", function (d) {
return xScale(d[1]) + xScale.bandwidth() / 2;
})
.attr("cy", function (d) {
return yScale(d[0]);
})
.on("mouseover", function (d) {
if (xDomainsKey[d[1]] == undefined) console.log(d)
if (!tooltip) tooltip = createToolTip(svg);
tooltip.display(`${xDomainsKey[d[1]]}:${d[0]}`, [d3.event.pageX - margin.left, d3.event.pageY - margin.top - margin.bottom - 30]);
})
.on("mouseout", function (d) {
tooltip.hide()
});

function brushedRectBrushStartBrush() {
console.log(d3.event.type, d3.event.sourceEvent.type);

const extent = d3.event.selection;
console.log("check extent", extent);
if (extent) {
let selected_dots = [];
dots.classed("selected", function (d) {
let res = isBrushed(
extent,
xScale(d[1]) + xScale.bandwidth() / 2,
yScale(d[0])
);
if (res) selected_dots.push(d);
console.log(res)
return res;
});
highlightSelected(selected_dots, extent);
} else {
dots.classed("selected", false);
const bar = svg.selectAll(".g-tmp").remove();
if (btnPanel) btnPanel.forEach((btn) => btn.hide());
}
}

function highlightSelected(selectedItems, extent) {
console.log(selectedItems)
const bar = svg
.selectAll(".g-tmp")
.data([0])
.join("g")
.attr("class", "g-tmp")
.selectAll("path")
.data([selectedItems])
.join("path")
.attr("d", (d) => {
return lineBuilder(d);
})
.style("stroke", (d) => "red")
.style("stroke-width", 2)
.style("fill", "transparent");

if (!btnPanel) {
btnPanel = [addIcon1([80, 10], svg), addIcon2([80, 10], svg)];
}
btnPanel.forEach((btn) => btn.display(extent[0]));
}

function isBrushed(brush_coords, cx, cy) {
const x0 = brush_coords[0][0];
const x1 = brush_coords[1][0];
const y0 = brush_coords[0][1];
const y1 = brush_coords[1][1];

return x0 <= cx && cx <= x1 && y0 <= cy && cy <= y1;
}

return svg.node();
}
Insert cell
function createBtnPanel(container, pos, ) {
}
Insert cell
function addIcon1(pos, container, callback) {
const wrapper = d3.create("div").attr("class", "icon-btn-container").style("position", "absolute").style("left", pos[0] + "px").style("top", pos[1] + "px").style("z-index", 100).style("background-color", "white").style("border", "1px solid lightgrey").style("width", "26px").style("height", "26px").style("padding", "2px")
const svg = d3
.create("svg")
.attr("width", 22)
.attr("height", 22)
.attr("viewBox", [0, 0, 14, 14])
const g = svg
.append("g")
.attr("class", "icon-btn");
wrapper.on("mouseover", function(){g.attr("color", "red")})
.on("mouseout", function() {g.attr("color", "black")})
.on("click", function() {})
// g.append("circle").attr("r", 7).attr("fill", "red").attr("cx", 3.5).attr("cy", 3.5)
g.node().innerHTML = `<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M9.5 3.5h4v4"/><path d="M13.5 3.5L7.85 9.15a.5.5 0 0 1-.7 0l-2.3-2.3a.5.5 0 0 0-.7 0L.5 10.5"/></g>`;
wrapper.node().appendChild(svg.node())
if (container) {
container.node().parentElement.appendChild(wrapper.node())
}


function hide() {
wrapper.style("display", "none")
}

function display(pos) {
wrapper.style("display", "block")
if (pos) wrapper.style("left", pos[0] + "px").style("top", pos[1] + "px").style("transform", "translate(0%, -100%)")
}
return {
obj: wrapper.node(),
hide: hide,
display: display,
}
}
Insert cell
function addIcon2(pos, container, callback) {
const wrapper = d3.create("div").attr("class", "icon-btn-container").style("position", "absolute").style("left", pos[0] + "px").style("top", pos[1] + "px").style("z-index", 100).style("background-color", "white").style("border", "1px solid lightgrey").style("width", "26px").style("height", "26px").style("padding", "2px")
const svg = d3
.create("svg")
.attr("width", 22)
.attr("height", 22)
.attr("viewBox", [0, 0, 14, 14])
const g = svg
.append("g")
.attr("class", "icon-btn");
wrapper.on("mouseover", function(){g.attr("color", "red")})
.on("mouseout", function() {g.attr("color", "black")})
.on("click", function() {})
// g.append("circle").attr("r", 7).attr("fill", "red").attr("cx", 3.5).attr("cy", 3.5)
g.node().innerHTML = `<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M9.5 10.5h4v-4"/><path d="M13.5 10.5L7.85 4.85a.5.5 0 0 0-.7 0l-2.3 2.3a.5.5 0 0 1-.7 0L.5 3.5"/></g>`;
wrapper.node().appendChild(svg.node())
if (container) {
console.log(container)
container.node().parentElement.appendChild(wrapper.node())
}


function hide() {
wrapper.style("display", "none")
}

function display(pos) {
wrapper.style("display", "block")
if (pos) wrapper.style("left", pos[0] + "px").style("top", (pos[1]) + "px").style("transform", "translate(100%, -100%)")
}
return {
obj: wrapper.node(),
hide: hide,
display: display,
}
}
Insert cell
lineBuilder = {
return d3
.line()
// .curve(selected_curve)
.x(d => xScale(d[1]) + xScale.bandwidth() / 2)
.y((d) => yScale(d[0]));
}

Insert cell
area = d3.area()
.x((d) => xScale(d[1]) + + xScale.bandwidth() / 2)
.y0(height - margin.bottom).y1(d => yScale(d[0]))
.curve(selected_curve)
Insert cell
lineTypeMap = ({
'd3.curveBasis': d3.curveBasis,
'd3.curveBasisClosed': d3.curveBasisClosed,
'd3.curveBasisOpen': d3.curveBasisOpen,
'd3.curveBumpX': d3.curveBumpX,
'd3.curveBumpY': d3.curveBumpY,
'd3.curveBundle': d3.curveBundle,
'd3.curveCardinal': d3.curveCardinal,
'd3.curveCardinalClosed': d3.curveCardinalClosed,
'd3.curveCardinalOpen': d3.curveCardinalOpen,
'd3.curveCatmullRom': d3.curveCatmullRom,
'd3.curveCatmullRomClosed': d3.curveCatmullRomClosed,
'd3.curveCatmullRomOpen': d3.curveCatmullRomOpen,
'd3.curveLinear': d3.curveLinear,
'd3.curveLinearClosed': d3.curveLinearClosed,
'd3.curveMonotoneX': d3.curveMonotoneX,
'd3.curveMonotoneY': d3.curveMonotoneY,
'd3.curveNatural': d3.curveNatural,
'd3.curveStep': d3.curveStep,
'd3.curveStepAfter': d3.curveStepAfter,
'd3.curveStepBefore': d3.curveStepBefore,

})
Insert cell
Insert cell
xAxis = svg => {
function grid(tick) {
tick.select("line").remove()
tick.append("line")
.attr("class", "grid")
.attr("y1", -(height - margin.bottom - margin.top))
.attr("stroke", "rgba(0,0,0,0.2)")
.attr("stroke-opacity", 0.5)
.attr("stroke-dasharray", [5, 5])
}
return svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(xScale).tickValues(d3.range(0, d3.max(xScale.domain()), 10)))
.call(g => g.selectAll(".tick").call(grid));
}
Insert cell
yAxis = svg => {
// A helper method for generating grid lines on the y-axis.
function grid(tick) {
tick.select("line").remove()
return tick.append("line")
.attr("class", "grid")
.attr("x2", width - margin.left - margin.right)
.attr("stroke", "rgba(0,0,0,0.2)")
// .attr("stroke-opacity", 0.5)
.attr("stroke-dasharray", [5, 5])
}

return svg.append("g")
.attr("transform", `translate(${margin.left}, 0)`)
.call(d3.axisLeft(yScale).tickSizeOuter(0))
// .call(svg => svg.select(".domain").remove())
.call(g => g.selectAll(".tick").call(grid));
}

Insert cell
xScale = {
const domains = xDomains;

const scale = d3.scaleBand()
.range([margin.left, width - margin.right])
.domain(domains)
.paddingInner(0.1)
.paddingOuter(0.1);
return scale;
}
Insert cell
yScale = {
const min = d3.min(data["attn_dist"], (d) => d);
const max = d3.max(data["attn_dist"], (d) => d);

const scale = d3.scaleLinear()
.domain([0, max])
.nice()
.clamp(true)
.range([height - margin.bottom, margin.top]);
return scale;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
data = {
const result = await FileAttachment("population+treatment.json").json();
const data = result["attn_dist"][0];
// data["attn_dist"] = data["attn_dist"].map((d, i) => {if (i < 60) {return 0} else {return 0}})
return data
};
Insert cell
Insert cell
d3tip = require("d3-tip")
Insert cell
style = html`
<style>
.dot {
cursor: move;
fill: steelblue;
}

.selected {
stroke: seagreen;
stroke-width: 3px;
fill: seagreen;
}

.drag-active{
fill: red;
}

.selection {
fill: none;
stroke-width: 2px;
stroke-dasharray: 8, 0, 8, 5;
stroke: grey;
}

.icon-btn-container:hover {
cursor: pointer
}

div.tooltip {
position: fixed;
display: none;
padding: 12px 6px;
background: #fff;
border: 1px solid #333;
pointer-events: none;
}
</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