Public
Edited
Oct 1, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const svg = d3.select(DOM.svg(400, 600));
const margin = { top: 50, right: 50, bottom: 80, left: 50 };
const width = +svg.attr("width") - margin.left - margin.right;
const height = +svg.attr("height") - margin.top - margin.bottom;

const x = d3
.scaleLinear()
.domain([0, d3.max(dataPricing, (d) => Math.max(d.Brightdata, d.Apify))])
.range([0, width]);

const y = d3
.scaleLog()
.domain([
d3.min(dataPricing, (d) => d["Query Count (per Month)"]),
d3.max(dataPricing, (d) => d["Query Count (per Month)"])
])
.range([height, 0]);

//const uniqueXValues = [...new Set(dataPricing.map(d => d.Brightdata))];
//const filteredXValues = uniqueXValues.filter(d => d !== 0)
const maxX = d3.max(dataPricing, (d) => Math.max(d.Brightdata, d.Apify));
//const tickValuesX = x.ticks(3).concat(maxX).filter((v, i, self) => self.indexOf(v) === i)
const tickValuesX = [0, maxX / 2, maxX];

const gX = svg
.append("g")
.attr("transform", `translate(${margin.left},${height + margin.top})`)
//.call(d3.axisBottom(x).ticks(3).tickValues(filteredXValues).tickFormat(d3.format(".0s")))
.call(d3.axisBottom(x).tickValues(tickValuesX).tickFormat(d3.format(".0s")))
.attr("font-size", "16px")
.attr("font-family", fontFamily)
.append("text")
.attr("class", "label")
.attr("x", width + 20)
.attr("y", 50)
.attr("fill", "#000")
.attr("text-anchor", "end")
.attr("font-size", "16px")
.attr("font-family", fontFamily)
.attr("font-weight", "bold")
.text("Cost ($)");

const gY = svg
.append("g")
.attr("class", "y axis")
.attr("transform", `translate(${margin.left},${margin.top})`)
.call(d3.axisLeft(y))
.attr("font-size", "16px")
.attr("font-family", fontFamily)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(0)")
.attr("y", -20)
.attr("x", -margin.left)
.attr("fill", "#000")
.attr("text-anchor", "start")
.attr("font-size", "16px")
.attr("font-weight", "bold")
.attr("font-family", fontFamily)
.text("Monthly Query Count");

svg
.selectAll(".brightdata-circle")
.data(dataPricing)
.enter()
.append("circle")
.attr("class", "brightdata-circle")
.attr("cx", (d) => x(d.Brightdata) + margin.left)
.attr("cy", (d) => y(d["Query Count (per Month)"]) + margin.top)
.attr("r", 5)
.style("fill", "blue")
.on("mouseover", function () {
svg
.selectAll(".hover-label")
.data(dataPricing)
.enter()
.append("text")
.attr("class", "hover-label")
.attr("x", (d) => x(d.Brightdata) + margin.left + 10)
.attr("y", (d) => y(d["Query Count (per Month)"]) + margin.top - 10)
.attr("font-family", fontFamily)
.attr("font-size", "12px")
.attr("fill", "blue")
.text((d) => d.Brightdata);
})
.on("mouseout", function () {
svg.selectAll(".hover-label").remove();
});

const lineGeneratorBrightdata = d3
.line()
.x((d) => x(d.Brightdata) + margin.left)
.y((d) => y(d["Query Count (per Month)"]) + margin.top)
.curve(d3.curveLinear);

svg
.append("path")
.data([dataPricing])
.attr("d", lineGeneratorBrightdata)
.attr("fill", "none")
.attr("stroke", "blue")
.attr("stroke-width", 2)
.on("mouseover", showLabelsBrighdata)
.on("mouseout", hideLabels);

svg
.selectAll(".apify-circle")
.data(dataPricing)
.enter()
.append("circle")
.attr("class", "apify-circle")
.attr("cx", (d) => x(d.Apify) + margin.left)
.attr("cy", (d) => y(d["Query Count (per Month)"]) + margin.top)
.attr("r", 5)
.style("fill", "green");

const lineGeneratorApify = d3
.line()
.x((d) => x(d.Apify) + margin.left)
.y((d) => y(d["Query Count (per Month)"]) + margin.top)
.curve(d3.curveLinear);

svg
.append("path")
.data([dataPricing])
.attr("d", lineGeneratorApify)
.attr("fill", "none")
.attr("stroke", "green")
.attr("stroke-width", 2)
.on("mouseover", showLabelsApify)
.on("mouseout", hideLabels);

function showLabelsBrighdata() {
svg
.selectAll(".hover-label")
.data(dataPricing)
.enter()
.append("text")
.attr("class", "hover-label")
.attr("x", (d) => x(d.Brightdata) + margin.left + 10)
.attr("y", (d) => y(d["Query Count (per Month)"]) + margin.top - 10)
.attr("font-family", fontFamily)
.attr("font-size", "12px")
.attr("fill", "black")
.text((d) => d.Brightdata);
}

function showLabelsApify() {
svg
.selectAll(".hover-label")
.data(dataPricing)
.enter()
.append("text")
.attr("class", "hover-label")
.attr("x", (d) => x(d.Apify) + margin.left + 10)
.attr("y", (d) => y(d["Query Count (per Month)"]) + margin.top - 10)
.attr("font-family", fontFamily)
.attr("font-size", "12px")
.attr("fill", "green")
.text((d) => d.Apify);
}

function hideLabels() {
svg.selectAll(".hover-label").remove();
}

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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