Public
Edited
Aug 19, 2021
Insert cell
Insert cell
books = {
const bookWidth = 50;
const minHeight = 250;
const maxHeight = 300;
const bookPadding = 3;
const fadeColor = "#43464A";

const svgWidth = NUMBOOKS * bookWidth + bookPadding * 2;
const usableWidth = NUMBOOKS * bookWidth;
const svgHeight = maxHeight + bookPadding * 2;
const usableHeight = maxHeight;

const svg = d3
.create("svg")
.attr("width", svgWidth)
.attr("height", svgHeight)
// .attr("font-family", "serif") // want to play with this
.attr("font-size", "10");

const bookWidthRange = d3.extent(BOOKDATA, (d) => d.NumPages);
const bookWidthScale = d3.scaleLinear().domain(bookWidthRange).range([1, 10]);

const bookHeightScale = d3.randomUniform(minHeight, maxHeight);

const booksData = _.map(BOOKDATA, (d, i) => {
const title = d.Title;
const id = i;
const thickness = bookWidthScale(d.NumPages);
const titleLength = d.Title.length; //bookTitleScale(d.Title.length);
const height = bookHeightScale();
const coverColor = d.CoverColor;
const accentColor = d.AccentColor;
return {
id,
title,
thickness,
titleLength,
height,
coverColor,
accentColor
};
});

var xScale = d3
.scaleLinear()
.domain([0, Math.PI * 2])
.range([0, usableWidth]);

var pie = d3
.pie()
.sortValues(null)
.value((d) => d.thickness);

var adjustedData = pie(booksData);

const books = svg.selectAll("g").data(adjustedData).enter().append("g");

const bookRects = books.data(adjustedData);

var gradients = books
.append("linearGradient")
.attr("id", (d) => `${d.data.id}-gradient`)
.attr("y1", "0%")
.attr("y2", "0%")
.attr("x1", "0%")
.attr("x2", "100%");
//.attr('gradientUnits', 'userSpaceOnUse')

gradients.append("stop").attr("offset", "0%").attr("stop-color", fadeColor);

gradients
.append("stop")
.attr("offset", "7%")
.attr("stop-color", (d) => d.data.coverColor);

gradients
.append("stop")
.attr("offset", "93%")
.attr("stop-color", (d) => d.data.coverColor);

gradients.append("stop").attr("offset", "100%").attr("stop-color", fadeColor);

books
.append("rect")
.style("fill", (d) => `url(#${d.data.id}-gradient)`)
.style("stroke", fadeColor)
.style("stroke-width", bookPadding)
.merge(bookRects)
.attr("x", function (d) {
return xScale(d.startAngle) + bookPadding;
})
.attr("width", function (d) {
return xScale(d.endAngle) - xScale(d.startAngle);
})
.attr("y", (d) => maxHeight - d.data.height)
.attr("height", (d) => d.data.height);

books
.append("text")
.text((d) => d.data.title)
.attr(
"x",
(d) =>
d.data.height / 2 +
(maxHeight - d.data.height) -
d.data.titleLength * 1.7
) // pretty weird approximation of correct
.attr("y", function (d) {
return -(xScale(d.startAngle) + xScale(d.endAngle)) / 2;
})
.attr("fill", (d) => d.data.accentColor)
.attr("transform", (d) => `rotate(90)`)
.attr("font-family", FONT);

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
origData = FileAttachment("goodreads_library_export.csv").csv({typed: true});
Insert cell
Insert cell
Insert cell
Insert cell
cutDownData = Promise.all(smaller.map(function(d) {

const dateYear = d["Date Read"]?.split('/')[0];
const dateMonth = d["Date Read"]?.split('/')[1] - 1; // months are zero-indexed
const dateDay = d["Date Read"]?.split('/')[2];
const isbn = Number(d["ISBN13"].replace(/\D/g,''));
const imageUrl = "http://covers.openlibrary.org/b/isbn/"+ isbn + "-L.jpg";
var colorPalette = {
Vibrant: "asdffa",
DarkMuted: "fdsadsf"
}
var toret = Vibrant.from(imageUrl).getPalette()
.then(
function(palette) {
colorPalette = palette;
console.log(colorPalette);
return {
Title: d["Title"],
Url: imageUrl,
CoverColor: colorPalette.Vibrant.getHex(),
AccentColor: colorPalette.DarkMuted.getHex(),
NumPages: d["Number of Pages"],
DateRead: new Date(dateYear, dateMonth, dateDay),
Status: d["Exclusive Shelf"]
};
}, function(err) {
console.log(err);
return null;
});
return toret;
}));
Insert cell
filteredCutDownData = cutDownData.filter(d => d !== null)
Insert cell
currentlyReading = filteredCutDownData.filter(d => d.Status === "currently-reading").slice(0, NUMBOOKS);
Insert cell
recentlyRead = filteredCutDownData.filter(d => d.Status ==="read").slice(0, NUMBOOKS);
Insert cell
BOOKDATA = recentlyRead;
Insert cell
Table(BOOKDATA);
Insert cell
Insert cell
html`
<svg width="100" height="100">
<style>
.small { font: italic 13px serif; }
</style>
<linearGradient id="gradient" y1="0%" y2="0%" x1="0%" x2="100%" spreadMethod="pad">
<stop offset="0%" stop-color="Yellow"></stop>
<stop offset="10%" stop-color="#ac840c"></stop>
<stop offset="90%" stop-color="#ac840c"></stop>
<stop offset="100%" stop-color="Yellow"></stop>
</linearGradient>
<rect width="30" height="100" style="fill: url(#gradient)";" />
<text transform="translate(15,50) rotate(90)" x="0" y="0" class="small" fill="#784426">My text</text>
</svg>`
Insert cell
html`
<svg width="100" height="200">
<style>
.small { font: italic 13px serif; }
</style>
<linearGradient id="gradient" y1="0%" y2="0%" x1="0%" x2="100%" spreadMethod="pad">
<stop offset="0%" stop-color="Yellow"></stop>
<stop offset="10%" stop-color="#ac840c"></stop>
<stop offset="90%" stop-color="#ac840c"></stop>
<stop offset="100%" stop-color="Yellow"></stop>
</linearGradient>
<rect width="30" height="100" style="fill: url(#gradient)";" transform="translate(0,20)" />
<text transform="translate(15,50) rotate(90)" x="0" y="0" class="small" fill="#784426">My text</text>
<path transform="translate(20,0)" d="${bookPath}"/>
</svg>`
Insert cell
bookPath = 'M -19,20 L 9,20 L 5,1 L -15,1 Z'
Insert cell
Insert cell
Vibrant.from('http://covers.openlibrary.org/b/isbn/9780142000656-L.jpg').getPalette();
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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