Public
Edited
May 4, 2023
Insert cell
Insert cell
newLineChart = {
const data = genaistartups // data is drawn from DealRoom.co's genAi dataset

const margin = { top: 20, right: 20, bottom: 40, left: 60 };
const width = 783;
const height = 233;

// Creating the base of SVG
const svg = d3
.select(DOM.svg(width, height));

// Creating X and Y scales
const xScale = d3
.scalePoint()
.domain(data.map(d => d.year))
.range([margin.left, width - margin.right])
.padding(0.1);

const yScale = d3
.scaleLinear()
.domain([0, d3.max(data, d => d.startups)])
.range([height - margin.bottom, margin.top]);

// Adding lines
const line = d3
.line()
.x(d => xScale(d.year))
.y(d => yScale(d.startups));


const dur = 2500
const radius = 5;

// Append a rectangle to the SVG as the background
svg.append("rect")
.attr("width", "100%") // Set the width to 100% of the SVG container
.attr("height", "100%") // Set the height to 100% of the SVG container
.attr("fill", "transparent"); // Set the fill color of the rectangle to light gray


// segregating values for each line
const groupedData = Array.from(d3.group(data, d => d.country));

const uniqueCategories = groupedData.map(([category, data]) => category);

// Define color scale
const colorScale = d3.scaleOrdinal()
.domain(uniqueCategories)
.range(d3.schemeCategory10);


// Loop through each category and plot a line
groupedData.forEach(([category, data]) => {
const line = d3.line()
.x(d => xScale(d.year))
.y(d => yScale(d.startups));

const path = svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line)
.style("stroke", colorScale(category))
.style("fill", "none");


// Get total length of the path
const totalLength = path.node().getTotalLength();
// Transition to draw the line point by point
path.attr("stroke-dashoffset", totalLength)
.attr("stroke-dasharray", totalLength)
.transition()
.duration(2000)
.ease(d3.easeLinear)
.attr("stroke-dashoffset", 0);
// Add legend for each category
svg.append("circle")
.attr("id", "circle")
.attr("cx", 100)
.attr("cy", 30 + uniqueCategories.indexOf(category) * 20)
.attr("r", 3)
.style("fill", colorScale(category));
svg.append("text")
.attr("x", 110)
.attr("y", 35 + uniqueCategories.indexOf(category) * 20)
.text(category)
.style("fill", colorScale(category))
.attr("font-family", "sans-serif")
.style("font-size", "0.7em");
});


// Adding Axes

const xAxis = d3.axisBottom(xScale);
const yAxis = d3.axisLeft(yScale);

svg.append("g")
.attr("class", "x-axis")
.attr("transform", `translate(0, ${height - margin.bottom})`)
.call(xAxis);

svg.append("g")
.attr("class", "y-axis")
.attr("transform", `translate(${margin.left}, 0)`)
.call(yAxis);

svg.select(".x-axis")
.attr("fill", "transparent")
.attr("class", "x-axis")
.call(xAxis);

svg.select(".y-axis")
.attr("fill", "transparent")
.attr("class", "y-axis")
.call(yAxis);

// Adding circles
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", d => xScale(d.year))
.attr("cy", d => yScale(d.startups))
.attr("r", 5)
.attr("fill", d => colorScale(d.country))
.on('mouseenter', mouseEnter)
.on('mouseleave', mouseLeave);


// The tooltip code is inspired from - https://observablehq.com/@nyuvis/interaction?collection=@nyuvis/guides-and-examples
// create tooltip
const tooltip = svg.append('g')
.attr('visibility', 'hidden');
const tooltipHeight = 20;
// add a rectangle to the tooltip to serve as a background
const tooltipRect = tooltip.append('rect')
.attr('fill', '#a1a1a1')
.attr('rx', 5)
.attr('height', tooltipHeight);
// add a text element to the tooltip to contain the label
const tooltipText = tooltip.append('text')
.attr('fill', '#fcfcfc')
.attr('font-family', 'sans-serif')
.attr('font-size', 12)
.attr('y', 2) // offset it from the edge of the rectangle
.attr('x', 3) // offset it from the edge of the rectangle
.attr('dominant-baseline', 'hanging')
// handle hovering over a circle
function mouseEnter(event, d) {
// make the circle larger
d3.select(this)
.attr('r', radius * 1.5);
// update the label's text and get its width
tooltipText.text("For " + d.country+ ": " + d.startups + " startups");
const labelWidth = tooltipText.node().getComputedTextLength();
// set the width of the tooltip's background rectangle
// to match the width of the label, plus some extra space
tooltipRect.attr('width', labelWidth + 6);
// move the tooltip to the position of the circle (offset by a bit)
// and make the tooltip visible
const xPos = d3.select(this).attr("cx") + radius * 3;
const yPos = d3.select(this).attr("cy") - tooltipHeight / 2;

tooltip.attr('transform', `translate(${xPos},${yPos})`)
.attr('visibility', 'visible');
}
// handle leaving a circle
function mouseLeave(event, d) {
// reset the size of the circle
d3.select(this)
.attr('r', radius)
// make the tooltip invisible
tooltip
.attr('visibility', 'hidden');
}

// Return SVG in the notebook
return svg.node();
}

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