Public
Edited
Oct 24, 2023
Insert cell
Insert cell
data = [{
agentId: "John Smith",
apn: "123 Cherry Ln.",
status: "listed",
listDate: "2023-05-01",
loDate: "2022-04-10",
loListAgent: "Candi Carlisle",
loSoldAgent: "Jake Simms"
},
// {
// agentId: "John Smith",
// apn: "456 Harwood Ave.",
// status: "listed",
// listDate: "2022-07-01",
// loDate: "2023-02-10",
// loListAgent: "Candi Carlisle",
// loSoldAgent: "Ken Jennings"
// },
// ... More data
];
Insert cell
imgAddress = 'https://www.svgrepo.com/show/111256/house.svg'
Insert cell
function makeChart() {
const margin = { top: 10, right: 10, bottom: 20, left: 40 };
const width = 800 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;

const svg = d3.create("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);

const xDomainStart = new Date('2022-04-10');
xDomainStart.setDate(xDomainStart.getDate() - 10);
const xDomainEnd = new Date('2023-05-01');
xDomainEnd.setDate(xDomainEnd.getDate() + 10);

const xScale = d3.scaleTime()
.domain([xDomainStart, xDomainEnd])
.range([0, width]);

// svg.append("g")
// .attr("transform", `translate(0,${height})`)
// .call(d3.axisBottom(xScale));

const lineYEnd = height - 25;
const lineYStart = height - 60;

data.forEach(d => {
// Dashed line for loDate
svg.append("line")
.attr("x1", xScale(new Date(d.loDate)))
.attr("y1", lineYStart - 100)
.attr("x2", xScale(new Date(d.loDate)))
.attr("y2", lineYEnd)
.style("stroke", "black")
.style("stroke-dasharray", ("3, 3"));

// House image for loDate
const housePath = "M17 21H7a4 4 0 0 1-4-4v-6.292a4 4 0 0 1 1.927-3.421l5-3.03a4 4 0 0 1 4.146 0l5 3.03A4 4 0 0 1 21 10.707V17a4 4 0 0 1-4 4ZM9 17h6";

svg.append("path")
.attr("d", housePath)
.attr("stroke", "#000000")
.attr("stroke-width", 1.5)
.attr("fill", "none")
.attr("transform", `translate(${xScale(new Date(d.loDate)) - 10},${lineYEnd + 10})`);


// Text for loDate
svg.append("text")
.attr("x", xScale(new Date(d.loDate)) + 10)
.attr("y", lineYStart - 125)
.text(`You Mortgaged ${d.apn} on ${d.loDate} with ${d.loSoldAgent}.`)
.style("font-size", "14px")
.attr('text-anchor', 'start').attr(
'font-family',
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
)

// Dashed line for listDate or soldDate based on status
const eventDate = d.status === "listed" ? d.listDate : d.soldDate;
svg.append("line")
.attr("x1", xScale(new Date(eventDate)))
.attr("y1", lineYStart)
.attr("x2", xScale(new Date(eventDate)))
.attr("y2", lineYEnd)
.style("stroke", "black")
.style("stroke-dasharray", ("3, 3"));


svg.append("path")
.attr("d", housePath)
.attr("stroke", "#000000")
.attr("stroke-width", 1.5)
.attr("fill", "none")
.attr("transform", `translate(${xScale(new Date(eventDate)) - 10},${lineYEnd + 10})`);
// Text for listDate or soldDate
svg.append("text")
.attr("x", xScale(new Date(eventDate)) - 10)
.attr("y", lineYStart - 25)
.text(d.status === "listed" ? `${d.agentId} listed ${d.apn} on ${d.listDate}.` : `Sold ${d.apn}`)
.style("font-size", "14px")
.attr('text-anchor', 'end').attr(
'font-family',
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
)

});

svg.selectAll("text")
.each(function(d, i) { wrap_text(d3.select(this), 80) });

return svg.node().parentNode;
}

Insert cell
measure_width = {
const context = document.createElement("canvas").getContext("2d");
return text => context.measureText(text).width;
}
Insert cell
wrap_text = (text_element, max_width, line_height, unit = "em") => {
// word parameters
let words = text_element.text().split(/\s+/).reverse(),
word,
line = [],
line_number = 0;
// styling parameters
const x = text_element.attr("x"),
y = text_element.attr("y");
if (!line_height) line_height = 1.1;
// clear text_elements text
text_element.text(null);
// append first tspan element (to fill as we build the lines)
let tspan = text_element.append("tspan")
.attr("x", x)
.attr("y", y)
.attr("dy", 0);
// loop through all words and make new lines when we exceed our max_width
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (measure_width(tspan.text()) > max_width) {
line.pop()
tspan.text(line.join(" "));
line = [word];
tspan = text_element.append("tspan")
.attr("x", x)
.attr("y", y)
.attr("dy", `${++line_number * line_height}${unit}`)
.text(word);
}
}

// Get the bounding box of the text element
let bbox = text_element.node().getBBox();

return {
width: bbox.width,
height: bbox.height,
x: bbox.x,
y: bbox.y
};
}
Insert cell
makeChart()
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