Published
Edited
Jun 9, 2019
Insert cell
Insert cell
chart = {
// <svg></svg>
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

// <g></g>
// var g = svg.append("g");
// xAxis(g);
svg.append("g").call(xAxis);
//svg.append("g").call(xAxis_median);

svg.append("g").call(yAxis);
//svg.append("g").call(yAxis_median);

svg
.append("g")
.attr("class", "remove")
.style("position", "absolute")
.style("z-index", "19")
.style("width", "1px")
.style("height", height)
.attr("stroke", "black");
//.style("top", "10px")
//.style("bottom", "30px")
//.style("left", "0px")
//.style("background", "#fff");

//scatter plot
// thing = { key: value };
// thing["key"]
// thing.key

//
// function(d) { return d

////scatter plot
svg
.append("g")
.attr("stroke-width", 1.5)
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.selectAll("g")
.data(datafiltered)
.join("g")
.attr("transform", d => `translate(${x(d.date)},${y(d.value)})`)
//.attr("transform", d => `translate(100,100)`)
.call(
g =>
g
.append("circle")
.attr("stroke", "steelblue")
.attr("fill", "none")
.attr("r", 3)
/*.filter(function(d) {
return d.value == null;
})
.remove()*/
);
/*.call(g =>
g
.append("text")
.attr("dy", "0.35em")
.attr("x", 7)
.text(d => d.name)*/

/*svg
.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "green")
.attr("stroke-width", 1.5)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("d", line);*/

//median line
svg
.append("path")
.datum(yearlymedian_filtered)
.attr("fill", "none")
.attr("stroke", "darkblue")
.attr("stroke-width", 1.5)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("d", line_median);

//points on the median line
svg
.append("g")
.attr("stroke-width", 1.5)
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.selectAll("g")
.data(yearlymedian_filtered)
.join("g")
.attr("transform", d => `translate(${x_median(d.date)},${y(d.value)})`)

.call(g =>
g
.append("circle")
.attr("stroke", "darkblue")
.attr("fill", "none")
.attr("r", 4)
.filter(function(d) {
return d.value == null;
})
.remove()
);

var ordinal = d3
.scaleOrdinal()
.domain([
"Median Pixel Intensity for a Single Day",
"Annual Median Pixel Intensity"
])
.range(["steelblue", "darkblue"]);

svg
.append("g")
.attr("class", "legendOrdinal")
.attr("transform", "translate(650,20)");

var legendOrdinal = d3Legend
.legendColor()
//d3 symbol creates a path-string, for example
//"M0,-8.059274488676564L9.306048591020996,
//8.059274488676564 -9.306048591020996,8.059274488676564Z"
.shape(
"path",
d3
.symbol()
.type(d3.symbolCircle)
.size(150)()
)
.shapePadding(30)
//use cellFilter to hide the "e" cell
.cellFilter(function(d) {
return d.label !== "e";
})
.scale(ordinal);

svg.select(".legendOrdinal").call(legendOrdinal);

//title
svg
.append("text")
.attr(
"transform",
`translate(${(width + margin.left + margin.right) / 2}, ${margin.top /
2})`
)
//transform title to middle and down so it's in hte right place
.style("text-anchor", "middle")
//center title
.style("font-weight", 700)
//bold text
.text("Blue Band");

//extra information about median and interquartile range
svg
.append("text")

.attr(
"transform",
`translate(${(width + margin.left + margin.right) / 2 - 350}, 490)`
)
//.attr("transform", "rotate(-90)")
/*.attr("y", 0 - margin.left)
.attr("x", 0 - height / 2)
.attr("dy", "1em")*/
.style("text-anchor", "middle")
.style("font-weight", 500)
.text("Median =" + String(median) + ", Interquartile Range = 0.038");

svg
.append("text")
.attr(
"transform",
`translate(${(width + margin.left + margin.right) / 2}, ${margin.top / 2 +
30})`
)
//transform title to middle and down so it's in the right place
.style("text-anchor", "middle")
//center title
.style("font-weight", 700);
//bold text
//.text("Median = 0.034, Interquartile Range = 0.03025");

//y-axis label
svg
.append("text")

//.attr("transform", `translate(${margin.right * 4}, 500)`)
.attr("transform", "rotate(-90)")
.attr("y", margin.left * 0.3)
.attr("x", 0 - height / 2.5)
.attr("dy", "1em")
.style("text-anchor", "middle")
.style("font-weight", 700)
.text("Median Band Intensity");
//x-axis label
svg
.append("text")

.attr(
"transform",
`translate(${(width + margin.left + margin.right) / 2}, ${0.95*height})`
)
//.attr("transform", "rotate(-90)")
/*.attr("y", 0 - margin.left)
.attr("x", 0 - height / 2)
.attr("dy", "1em")*/
.style("text-anchor", "middle")
.style("font-weight", 700)
.text("Year");

/*const brush = d3.brushX().extent([[0, 0], [width, height]]);
// .on("start brush end", brushmoved);
const gBrush = svg
.append("g")
.attr("class", "brush") // we don't know what this line does - if you comment it out, brush still works
.call(brush);*/

return svg.node();
}
Insert cell
height = width/3.63
Insert cell
margin = ({ top: 30, right: 30, bottom: 75, left: 100 })
Insert cell
x = d3
.scaleTime()
.domain(d3.extent(data, d => d.date))
.range([margin.left, width - margin.right])
Insert cell
x_median = d3
.scaleTime()
.domain(d3.extent(yearlymedian_filtered, d => d.date))
.range([margin.left, width - margin.right])
Insert cell
/*y = d3
.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.nice()
.range([height - margin.bottom, margin.top])*/

y = d3
.scaleLinear()
.domain(d3.extent(data, d => d.value))
.nice()
.range([height - margin.bottom, margin.top])
Insert cell
y_median = d3
.scaleLinear()
.domain([0, d3.max(yearlymedian_filtered, d => d.value)])
.nice()
.range([height - margin.bottom, margin.top + 360])
Insert cell
xAxis = g =>
// x axis takes a node "g" as input, and applies properties to g such that g becomes an X axis.
g.attr("transform", `translate(0,${height - margin.bottom + 10})`).call(
d3
.axisBottom(x)
.ticks(width / 81)
.tickSizeOuter(0)
)
Insert cell
xAxis_median = g =>
// x axis takes a node "g" as input, and applies properties to g such that g becomes an X axis.
g.attr("transform", `translate(0,${height - margin.bottom})`).call(
d3
.axisBottom(x_median)
.ticks(width / 80)
.tickSizeOuter(0)
)
Insert cell
/*yAxis = g =>
g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g =>
g
.select(".tick:last-of-type text")
.clone()
.attr("x", 3)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(data.y)
)*/

yAxis = g =>
g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g =>
g
.select(".tick:last-of-type text")
.clone()
.attr("x", 4)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(data.y)
)
Insert cell
yAxis_median = g =>
g
.attr("transform", `translate(${margin.left + 20},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g =>
g
.select(".tick:last-of-type text")
.clone()
.attr("x", 4)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(yearlymedian_filtered.y)
)
Insert cell
line = d3
.line()
.defined(d => !isNaN(d.value))
.x(d => x(d.date))
.y(d => y(d.value))
Insert cell
line_median = d3
.line()
.defined(d => !isNaN(d.value))
.x(d => x_median(d.date))
.y(d => y(d.value))
Insert cell
data = Object.assign(
await d3.csv(
datalink,
d3.autoType
)
)
Insert cell
datafiltered = data.filter(function(d) {
return d.value != null;
})
Insert cell
//data.push({ date: 2020, value: 0 })
Insert cell
num_datapoints = data.length
Insert cell
// data.forEach(function(arrayItem) {
date_yearlymedian = {
var year = 1984,
date_yearlymedian = [],
year_values = [],
calc_year = [];
for (let ii = 0; ii < data.length; ii++) {
let arrayItem = data[ii];
console.log("current item year is ", arrayItem.date.getFullYear());
console.log("comparing against ", year);
if (arrayItem.date.getFullYear() === year) {
year_values.push(arrayItem.value);
console.log("hi", year_values);
year_values.filter(Boolean);
} else {
console.log("year changed, aggregating median");
date_yearlymedian.push({ date: year, value: d3.median(year_values) });
year_values = [];
year = arrayItem.date.getFullYear();
}
}
return date_yearlymedian;
}
Insert cell
yearlymedian_filtered = date_yearlymedian.filter(function(d) {
return d.value != null;
})
Insert cell
yearlymedian_filtered
Insert cell
yearlymedian_filtered.push({ date: 2019, value: 0.1085 })
Insert cell
yearlymedian_filtered
Insert cell
median = d3.median(data, d => d.value)
Insert cell
tip = d3Tip()
.attr("class", "d3-tip")
.style("color", "white")
.style("background-color", "black")
.style("padding", "6px")
.style("border-radius", "4px")
.style("font-size", "12px")
.offset([-10, 0])
.html(function(d) {
return `<strong>${d3.format(",")(d.value)}</strong> intensity`;
})
Insert cell
d3Legend = require("d3-svg-legend")
Insert cell
d3Tip = require("d3-tip")
Insert cell
d3 = require("d3@^5.9")
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