Published
Edited
Apr 24, 2021
1 fork
13 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// Extract colours from the image using the ColorThief package
colorThief_palette = {
const colorThief = new ColorThief();
let colorThief_palette = colorThief.getPalette(image, 20);
// convert array items to 'rgb(...)' strings
colorThief_palette.forEach(function(d,i){
colorThief_palette[i] = "rgb(" + d[0] + "," + d[1] + "," + d[2] + ")";
});
return colorThief_palette;
}
Insert cell
colour_data_hsl = {
// convert colour_data rgbs to HSL
let object_colours_full = [];

colorThief_palette.forEach(function (d) {
var colour_hsl = d3.hsl(d);
if (isNaN(colour_hsl.h)) {
colour_hsl.h = 0;
};
if (isNaN(colour_hsl.s)) {
colour_hsl.s = 0;
};
if (isNaN(colour_hsl.l)) {
colour_hsl.l = 0;
};
object_colours_full.push({
"rgb": d,
"h": colour_hsl.h,
"s": colour_hsl.s,
"l": colour_hsl.l
});
});
// remove low saturation / low lightness colours
const saturationLimit = 0.2,
lightnessLowerLimit = 0.2,
lightnessHigherLimit = 1;
let object_colours_full_lowSaturationRemoved = [];

object_colours_full.forEach(function (d, i) {
if (d.s > saturationLimit && d.l < lightnessHigherLimit && d.l > lightnessLowerLimit) {
object_colours_full_lowSaturationRemoved.push(d);
}
});
return object_colours_full_lowSaturationRemoved;

}
Insert cell
// construct data structure from which to create colour wheel graphic

pie_chart_matrix = {

let pie_chart_matrix = [];
// array representing 12 sectors of circle, from 30-360 at 30degree intervals
const pie_chart_range = d3.range(30, 390, 30);
// fill pie_chart_matrix array with colours and matching circle sector angles (from pie_chart_range) according to each hues' angle on an artist's colour wheel
for (let i = 0; i < colour_data_hsl.length; i++) {
// Use the ColourWheel object (credit Ben Knight https://github.com/benknight/kuler-d3) to shift the angle for the hue to bring it closer to an artist's colour wheel (Red-Yellow-Blue rather than Red-Green-Blue)
const converted_h = ColourWheel.scientificToArtisticSmooth(colour_data_hsl[i].h);
for (let j = 0; j < pie_chart_range.length; j++) {
if (converted_h < pie_chart_range[j]) {
pie_chart_matrix.push([pie_chart_range[j], colour_data_hsl[i]]);
break;
};
};
};
function compare(a, b) {
if (a[1].l > b[1].l) return -1;
if (a[1].l < b[1].l) return 1;
return 0;
}
// order segments from darkest at the outside to lightest in the middle
pie_chart_matrix.sort(compare);

// calculate the total count and order of colors in each sector
// add this information to pie_chart_matrix
let counts = {};
let pie_chart_matrix_counts = [];
pie_chart_matrix.forEach(function (x) {
let match_count = (counts[x[0]] || 0) + 1;
counts[x[0]] = match_count;
let array_entry = x.push(match_count);
pie_chart_matrix_counts.push(array_entry);
});

pie_chart_matrix.forEach(function (d, i) {
let match_total = counts[d[0]];
pie_chart_matrix[i].push(match_total);
});
return pie_chart_matrix;
}
Insert cell
Insert cell
display = {
// create div element where image, colour wheel graphic, and colour palette grid are displayed together
const display = d3.create("div").attr("width", 800);

// create image element
display
.append("img")
.attr("id", "display_image")
.attr("src", image_urls[+selected_image])
.attr("width", 500)
.style("padding-right", "10px");

// Create an svg element for the colour wheel graphic
const vis = display
.append("svg")
.attr("width", 150)
.style("vertical-align", "top")
.style("background", "rgba(212, 211, 211, 1)");

// Create an svg for the colour palette in grid form
const palette_grid = display
.append("svg")
.attr("width", 150)
.style("vertical-align", "top")
.style("padding-left", "10px");

// generate the colour palette as a grid
colour_data_hsl.forEach(function (d, i) {
palette_grid
.append("rect")
.attr("fill", d.rgb)
.attr("width", 20)
.attr("height", 10)
.attr("transform", "translate(0," + i * 10 + ")");
});

//
// GENERATE COLOUR WHEEL GRAPHIC
//
const vis_colour_wheel = vis
.append("g")
.attr("transform", "translate(25,25)");

// white circle background
vis_colour_wheel
.append("g")
.attr("class", "r axis")
.attr("transform", "translate(50,50)")
.append("circle")
.attr("r", outerRadius)
.attr("fill", "white")
.attr("stroke", "#d4d4dd")
.attr("stroke-dasharray", 2);

// for each element of pie_chart_matrix, draw a circle sector of that colour
vis_colour_wheel
.selectAll("path")
.data(pie_chart_matrix)
.enter()
.append("path")
// The arc generator produces a circular or annular sector, as in a pie or donut chart https://github.com/d3/d3-shape#arcs
.attr("d", arc)
.style("fill", function (d) {
return d[1].rgb;
})
.attr("transform", "translate(50,50) rotate(-15)");

// draw lines between sectors
let ga_vis = vis_colour_wheel
.append("g")
.attr("class", "a axis")
.attr("transform", "translate(50,50)")
.selectAll("g")
.data(d3.range(-15, 345, 30))
.enter()
.append("g")
.attr("transform", function (d) {
return "rotate (" + d + ")";
});

ga_vis
.append("line")
.attr("x2", outerRadius)
.attr("stroke", "#d4d4dd")
.attr("stroke-dasharray", 2);

return display;
}
Insert cell
Insert cell
Insert cell
Insert cell
// ColourWheel functions convert between 'artistic' RYB (Red - Yellow - Blue) and 'scientific' RGB (Red - Green - Blue) colour wheels
// from Ben Knight https://github.com/benknight/kuler-d3

ColourWheel = ({
mapRange: function (value, fromLower, fromUpper, toLower, toUpper) {
return (toLower + (value - fromLower) * ((toUpper - toLower) / (fromUpper - fromLower)));
},
scientificToArtisticSmooth: function (hue) {
return (
hue < 35 ? hue * (60 / 35) :
hue < 60 ? this.mapRange(hue, 35, 60, 60, 122) :
hue < 120 ? this.mapRange(hue, 60, 120, 122, 165) :
hue < 180 ? this.mapRange(hue, 120, 180, 165, 218) :
hue < 240 ? this.mapRange(hue, 180, 240, 218, 275) :
hue < 300 ? this.mapRange(hue, 240, 300, 275, 330) :
this.mapRange(hue, 300, 360, 330, 360));
}
})
Insert cell
angle = d3.scaleLinear().domain([0, 360]).range([0, 2 * Math.PI]);
Insert cell
arc = d3.arc()
.innerRadius(function (d) {
if (d[3] > 1) {
return outerRadius * ((d[2] - 1) / d[3]);
} else {
return innerRadius;
}
})
.outerRadius(function (d) {
return (outerRadius * (d[2] / d[3]));
})
.startAngle(function (d) {
return angle(d[0] - 30);
})
.endAngle(function (d) {
return angle(d[0]);
});
Insert cell
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