Public
Edited
Jan 10, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
update_colorscale = {
d3.selectAll(".heatmap_rects")
.style("fill", d => color_torque(d.torque));
d3.selectAll(".first_threshold")
.attr("x", (d, i) => x_hist(first_threshold * (i == 1 ? 1 : -1)));
d3.selectAll(".second_threshold")
.attr("x", (d, i) => x_hist(second_threshold * (i == 1 ? 1 : -1)));
}
Insert cell
Insert cell
Insert cell
Insert cell
heatmap_generator = type_i => {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, total_width, height.top]);

var brush = d3.brush()
.extent([[margin.left, margin.top], [width.right - margin.right, height.top - margin.bottom]])
.on("brush end", brushend);
const plot1 = svg.append("g");
const plot2 = svg.append("g")
.attr("transform", `translate(${width.left}, 0)`);

plot_hist(type_i, plot1, x_hist);
plot_heatmap(type_i, plot2 , x_year_torque, y_city);
plot2.append("g")
.attr("id", "brush")
.call(brush);

function brushend({selection}) {
d3.selectAll(".signal_plot")
.attr("d", d => line(x_year_signal, y_signal[type_i])(d[1]))
.style("opacity", 1)
.style("stroke", "steelblue");
d3.selectAll(".map_plot")
.style("opacity", 0.15);
if(selection){
var x_range = [selection[0][0], selection[1][0]].map(x_year_torque.invert).sort((a, b) => a - b);
if (x_range[1] > 34) x_range[1] = 34;
var y_invert = [selection[0][1], selection[1][1]].map(y_city.invert).sort((a, b) => a - b);
x_year_signal.domain(x_range);
y_signal[type_i].domain(
d3.extent(
torque
.filter(d => d.type == type[type_i])
.filter(d => (y_invert[0] - 50 <= d.city) && (y_invert[1] + 50 >= d.city))
.filter(d => (x_range[0] - 4 <= d.year) && (x_range[1] + 4 >= d.year))
.map(d => d.signal)
)
);
d3.select(".signal_x_axis").call(d3.axisBottom(x_year_signal).tickFormat(d => d + 1986));
d3.select(".signal_y_axis").call(d3.axisLeft(y_signal[type_i]));
d3.selectAll(".signal_plot")
.attr("d", d => {
console.log(d);
return line(x_year_signal, y_signal[type_i])(d[1]);
});
d3.selectAll(".signal_plot")
.style("opacity", d => {
var is_between = (d[0] >= y_invert[0]) && (d[0] <= y_invert[1]);
return is_between ? 1 : 0.1;
})
.style("stroke", d => {
var is_between = (d[0] >= y_invert[0]) && (d[0] <= y_invert[1]);
return is_between ?"steelblue" : "#dddddd"
});

var cities_selected = cities_cd_sorted.slice(parseInt(y_invert[0]), parseInt(y_invert[1])).forEach(d => {
d3.select("#city_" + d.cd_geocmu)
.style("opacity", 1);
});
} else {
x_year_signal.domain(d3.extent(torque, d => d.year));
y_signal[type_i].domain(d3.extent(torque.filter(d => d.type == type[type_i]).map(d => d.signal)));
d3.select(".signal_x_axis").call(d3.axisBottom(x_year_signal).tickFormat(d => d + 1986));
d3.select(".signal_y_axis").call(d3.axisLeft(y_signal[type_i]));
}
}
return svg.node();
}
Insert cell
chat_generator = type_i => {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, total_width, height.bottom]);
const plot1 = svg.append("g");
const plot2 = svg.append("g")
.attr("transform", `translate(${width.left}, 0)`);
plot2.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("x", margin.left)
.attr("y", margin.top)
.attr("height", height.bottom - margin.top - margin.bottom)
.attr("width", width.right - margin.left - margin.right);

plot2.append("g")
.attr("class", "signal_x_axis")
.attr("transform", `translate(0, ${height.bottom - margin.bottom})`)
.call(d3.axisBottom(x_year_signal).tickFormat(d => d + 1986));
plot2.append("g")
.attr("class", "signal_y_axis")
.attr("transform", `translate(${margin.left}, 0)`)
.call(d3.axisLeft(y_signal[type_i]));

plot_map(plot1);
plot_signal(type_i, plot2, x_year_signal, y_signal[type_i]);
return svg.node();
}
Insert cell
plot_signal = (type_i, g, x, y) => {
g
.selectAll(".signal_plot_" + type_i)
.data(d3.group(torque.filter(d => d.type == type[type_i]), d => d.city))
.join("path")
.attr("clip-path", "url(#clip)")
.attr("d", d => line(x, y)(d[1]))
.attr("class", "signal_plot")
.attr("id", d => "city_" + d[0])
.style("fill", "none")
.style("stroke", "steelblue")
.style("stroke-width", 0.75)
.style("opacity", 1);

}
Insert cell
plot_heatmap = (type_i, g, x, y) => {
g.append("g")
.attr("transform", `translate(0, ${height.top - margin.bottom})`)
.call(d3.axisBottom(x).tickFormat(d => d + 1986));
g.append("g")
.attr("transform", `translate(${margin.left}, 0)`)
.call(d3.axisLeft(y));
g.selectAll(".heatmap_rects")
.data(torque)
.join("rect")
.filter(d => d.type == type[type_i])
.attr("class", "heatmap_rects")
.attr("x", d => x(d.year))
.attr("y", d => y(d.city))
.attr("width", x(1) - x(0))
.attr("height", Math.abs(y(0) - y(1)))
.style("fill", d => color_torque_first(d.torque))
}
Insert cell
plot_hist = (type_i, g, x) => {
var bins = histogram(torque.filter(d => d.type == type[type_i]));

g.append("g")
.attr("transform", `translate(0, ${height.top - margin.bottom})`)
.call(d3.axisBottom(x));
var y = d3.scaleLinear()
.range([height.top - margin.bottom, margin.top]);
y.domain([0, d3.max(bins, d => d.length)]);
g.append("g")
.attr("transform", `translate(${margin.left}, 0)`)
.call(d3.axisLeft(y));
g.selectAll(".hist_" + type_i)
.data(bins)
.enter()
.append("rect")
.attr("x", 1)
.attr("transform", d => "translate(" + x(d.x0) + "," + y(d.length) + ")")
.attr("width", d => x(d.x1) - x(d.x0) -1)
.attr("height", d => height.top - margin.bottom - y(d.length))
.style("fill", "steelblue");
g.selectAll(".threshold_" + type_i)
.data([-0.3, -0.05, 0.05, 0.3])
.enter()
.append("rect")
.attr("x", d => x(d))
.attr("y", margin.top)
.attr("width", 2)
.attr("class", (d, i) => ((i == 0) | (i == 3)) ? "second_threshold" : "first_threshold")
.attr("height", height.top - margin.bottom - margin.top)
.style("fill", (d, i) => ((i == 0) | (i == 3)) ? "red" : "orange");
}
Insert cell
plot_map = (g) => {
var projection = d3.geoMercator()
.translate([width.left/2, height.bottom/2])
.center([-50, -7])
.scale(450);
var path = d3.geoPath(projection);

var zoom = d3.zoom()
.on("zoom", zoomed);

function zoomed(e){
d3.select(".g_map_plot")
.attr("transform", e.transform);
}
g.append("g")
.attr("class", "g_map_plot")
.selectAll(".map_plot")
.data(cities.features)
.enter()
.append("path")
.attr("d", (d, i) => {
// little fix to make the path be only the city
if(d.geometry){
var s = path(d);
return s.slice(0, s.indexOf("Z"));
} else {
return "";
}
})
.attr("fill", "steelblue")
.attr("stroke", "#303030")
.attr("stroke-width", 0.5)
.style("opacity", 0.15)
.attr("id", d => "city_" + d.properties.cd_geocmu)
.attr("class", "map_plot");

g.call(zoom);
}
Insert cell
cities.features[593]
Insert cell
Insert cell
x_year_signal = d3.scaleLinear()
.domain(d3.extent(torque, d => d.year))
.range([margin.left, width.right - margin.right])
Insert cell
x_year_torque = d3.scaleLinear()
.domain([d3.min(torque.map(d => d.year)), 1 + d3.max(torque.map(d => d.year))])
.range([margin.left, width.right - margin.right])

Insert cell
x_hist = d3.scaleLinear()
.domain(d3.extent(torque, d => d.torque))
.range([margin.left, width.left - margin.right])
Insert cell
y_city = d3.scaleLinear()
.domain(d3.extent(torque, d => d.city))
.range([margin.top, height.top - margin.bottom])
Insert cell
y_signal = Array(3).fill(0).map((d, i) => d3.scaleLinear()
.domain(d3.extent(torque.filter(e => e.type == type[i]), e => e.signal))
.range([height.bottom - margin.bottom, margin.top]))
Insert cell
color_torque_first =
d3.scaleThreshold()
.domain([-0.3, -0.05, 0.05, 0.3])
.range(["#053061", "#6aacd0", "#ffffbf", "#e58267", "#67001f"])
Insert cell
color_torque =
d3.scaleThreshold()
.domain([-second_threshold, -first_threshold, first_threshold, second_threshold])
.range(["#053061", "#6aacd0", "#ffffbf", "#e58267", "#67001f"])

// d3.scaleQuantile()
// .domain(torque.map(d => Math.abs(d.torque)).concat(torque.map(d => -Math.abs(d.torque))))
// .range(Array(40).fill(0).map((d, i) => d3.interpolateRdBu(i/40)).reverse())
Insert cell
line = (x, y) => d3.line()
.x(d => x(d.year))
.y(d => y(d.signal))
Insert cell
histogram = d3.histogram()
.value(d => d.torque)
.domain(d3.extent(torque.map(d => d.torque)))
.thresholds(x_hist.ticks(50))
Insert cell
//xaxis_year_signal = (g, x) => {
// g.attr("transform", `translate(0, ${height.bottom - margin.bottom})`)
// .call(d3.axisBottom(x).tickFormat(d => Number.isInteger(d) ? 1986 + d : ""));
//}
Insert cell
//xaxis_year_signal = (g, x) => {
// g.attr("transform", `translate(0, ${height.bottom - margin.bottom})`)
// .call(d3.axisBottom(x).tickFormat(d => Number.isInteger(d) ? 1986 + d : ""));
//}
Insert cell
//yAxis_signal = (g, y) => {
// g.attr("transform", `translate(${margin.left},0)`)
// .call(d3.axisLeft(y));
//}
Insert cell
//yAxis_city = (g, y) => {
// g.attr("transform", `translate(${margin.left},0)`)
// .call(d3.axisLeft(y).tickFormat(d => "").tickSize(0));
//}
Insert cell
Insert cell
total_height = 600
Insert cell
total_width = 928
Insert cell
height= ({top: total_height * 0.5, bottom: total_height * 0.5})
Insert cell
width = ({left: 0.3 * total_width, right : 0.7 * total_width})
Insert cell
margin = ({top: 25, right: 20, bottom: 35, left: 40})
Insert cell
Insert cell
torque = FileAttachment("torque_joint.csv").csv({typed : true})
Insert cell
type = ["pastagem", "agricultura", "area_nao_vegetada"]
Insert cell
cities = FileAttachment("cities_amazonia@1.json").json()
Insert cell
cities_cd = FileAttachment("cities.csv").csv({typed : true})
Insert cell
cities_cd_sorted = cities_cd.sort((a, b) => a.cd_geocmu - b.cd_geocmu)
Insert cell
Insert cell
import {Legend, Swatches} from "@d3/color-legend"
Insert cell
import {slider} from '@jashkenas/inputs'
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