function make_time_series_graphs(opts = {}) {
let {
max_size = 1200,
margin = { left: 70, right: 30, bottom: 25, top: 20 },
opacity = 0.2,
highlight = "102001",
interactive = true
} = opts;
let graph_width = width < max_size ? width : max_size;
let graph_height = 0.625 * graph_width;
let svg = d3
.create("svg")
.attr("width", graph_width)
.attr("height", graph_height)
.attr("viewBox", [0, 0, graph_width, graph_height]);
let x_scale = d3
.scaleTime()
.domain([zillow_data.start_date, zillow_data.end_date])
.range([margin.left, graph_width - margin.right]);
let y_scale = d3
.scaleLinear()
.domain([0, max_current_house_price])
.range([graph_height - margin.bottom, margin.top]);
let pts_to_path = d3
.line()
.x((d) => x_scale(d[0]))
.y((d) => y_scale(d[1]));
let axes = svg.append("g");
let x_axis = d3.axisBottom(x_scale);
axes
.append("g")
.attr("transform", `translate(0,${graph_height - margin.bottom})`)
.call(x_axis);
let y_axis = d3.axisLeft(y_scale);
axes.append("g").attr("transform", `translate(${margin.left})`).call(y_axis);
let time_series_plots = svg.append("g");
let text_group = svg
.append("text")
.attr("x", 1.3 * margin.left)
.attr("y", 1.5 * margin.top)
.attr("font-size", 16)
.attr("text-anchor", "start")
.attr("fill", "steelblue");
let text_group2 = svg
.append("text")
.attr("x", 1.3 * margin.left)
.attr("y", 1.5 * margin.top + 20)
.attr("font-size", 16)
.attr("text-anchor", "start")
.attr("fill", "steelblue");
zillow_data.forEach(function (d) {
time_series_plots
.append("path")
.attr("d", pts_to_path(d.time_series))
.attr("class", "time_series")
.attr("id", `city${d.RegionID}`)
.attr("stroke", d.RegionID == highlight ? "steelblue" : "black")
.attr("stroke-opacity", d.RegionID == highlight ? 1 : opacity)
.attr("stroke-width", d.RegionID == highlight ? 4 : 0.7)
.attr("stroke-linejoin", "round")
.attr("fill", "none");
if (d.RegionID == highlight) {
text_group.text(d.RegionName);
text_group2.text(
`Current median price: $${d.time_series.slice(-1)[0][1]}`
);
}
});
if (highlight) {
time_series_plots.select(`#city${highlight}`).raise();
}
if (interactive) {
svg
.on("touchmove", (e) => e.preventDefault()) // prevent scrolling
.on("pointermove", function (evt) {
let p = d3.pointer(evt);
let x = p[0];
let y = p[1];
let day = x_scale.invert(x);
let price = y_scale.invert(y);
let closest = get_closest(day, price);
if (closest) {
time_series_plots
.selectAll("path.time_series")
.attr("stroke-opacity", opacity)
.attr("stroke-width", 0.7)
.attr("stroke", "black");
time_series_plots
.select(`#city${closest.cityID}`)
.attr("stroke-opacity", 1)
.attr("stroke-width", 4)
.attr("stroke", "steelblue")
.raise();
let zd = zillow_data.filter((o) => o.RegionID == closest.cityID)[0];
text_group.text(zd.RegionName);
text_group2.text(
`Current median price: $${zd.time_series.slice(-1)[0][1]}`
);
}
})
.on("pointerleave mouseleave", function () {
text_group.text("");
time_series_plots
.selectAll("path.time_series")
.attr("stroke-opacity", opacity)
.attr("stroke-width", 0.7)
.attr("stroke", "black");
if (highlight) {
time_series_plots
.select(`#city${highlight}`)
.attr("stroke-opacity", 1)
.attr("stroke-width", 4)
.attr("stroke", "steelblue")
.raise();
let zd = zillow_data.filter((o) => o.RegionID == highlight)[0];
text_group.text(zd.RegionName);
text_group2.text(
`Current median price: $${zd.time_series.slice(-1)[0][1]}`
);
}
});
}
return svg.node();
}