Join geometries that come with topoJSON with the CSV data with the following code:
data = Object.assign(new Map(d3.csvParse(await FileAttachment("<csv_file_name>.csv").text(), ({<ID_attribute>, <attribute2>, <attribute3>}) => [<ID_attribute>, [+<attribute2>, <attribute3>]])), {title: ["<attribute_2_name", " attribute_3_name"]})
Make sure attribute titles are taken from csv headings and that the values for the ID attributes match the property name in the JSON file. You can always edit the attribute name in the CSV file before uploading to make this easier.
Two three-class sequential color schemes that are blended together are used to make a bivariate map. You will want to use color schemes that are complimentary, but that are slightly adjusted so that they are color blind safe. To find color schemes, you can go to colorbrewer.org and select 3 classes. You can then choose two of the one-hue sequential color schemes.
Once you have these two color schemes, you can use an illustration tool to blend the colors together. Figma is an increasingly popular tool (that is also friend and very user friendly). In Figma, create three squares that are lined up next to each other. Adjust the color for each square according to one of the schemes from colorbrewer.org. Select the three squares making up the rectangle you've created and duplicate the object two times. Adjust the three rectangles so that they're next to each other, forming a square. You can group this into one object if you like using the shortcut cmd + g while all of the squares are selected. Repeat this process to create a second square with the other color scheme sequence from colorbrewer.org. Now with one of the 9x9 squares selected go to the panel on the right and under Layer, change from pass through to darken. You can now drag one of the 9x9 squares on top of the other 9x9 square and the colors will blend together. You can then use the color identifier tool ("i" key shortcut) to determine the color codes for each of the squares in the new blended 9x9 square.
Now in your schemes variable, you can add in a new color sequence by replacing the color codes and name seen here
{
name: "PuOr",
colors: [
"#e8e8e8", "#e4d9ac", "#c8b35a",
"#cbb8d7", "#c8ada0", "#af8e53",
"#9972af", "#976b82", "#804d36"
]
}
with an orginal descriptive name and the 9 color codes from Figma.
Create a legend by adding a variable to the notebook with the following code:
legend = () => {
const k = 24;
const arrow = DOM.uid();
return svg`<g font-family=sans-serif font-size=10>
<g transform="translate(-${k * n / 2},-${k * n / 2}) rotate(-45 ${k * n / 2},${k * n / 2})">
<marker id="${arrow.id}" markerHeight=10 markerWidth=10 refX=6 refY=3 orient=auto>
<path d="M0,0L9,3L0,6Z" />
</marker>
${d3.cross(d3.range(n), d3.range(n)).map(([i, j]) => svg`<rect width=${k} height=${k} x=${i * k} y=${(n - 1 - j) * k} fill=${colors[j * n + i]}>
<title>${data.title[0]}${labels[j] && ` (${labels[j]})`}
${data.title[1]}${labels[i] && ` (${labels[i]})`}</title>
</rect>`)}
<line marker-end="${arrow}" x1=0 x2=${n * k} y1=${n * k} y2=${n * k} stroke=black stroke-width=1.5 />
<line marker-end="${arrow}" y2=0 y1=${n * k} stroke=black stroke-width=1.5 />
<text font-weight="bold" dy="0.71em" transform="rotate(90) translate(${n / 2 * k},6)" text-anchor="middle">${data.title[0]}</text>
<text font-weight="bold" dy="0.71em" transform="translate(${n / 2 * k},${n * k + 6})" text-anchor="middle">${data.title[1]}</text>
</g>
</g>`;
}
In the code for the map itself, add the following lines of code to add the legend:
svg.append(legend)
.attr("transform", "translate(<x coordinate>,<y coordinate>)");
Adjust the position of the legend so that it is not hidden behind the map by changing the x and y coordinates in the above code. If your map has an area that naturally has an area of white space carved out, this might be an appropriate space to place the legend.
To create a hover-over effect that displays the values for each polygon, use the following lines of code:
.append("title")
.text(d => `${d.properties[idAttribute]}, ${format(data.get(d.properties[idAttribute]))}`);
In this example, a generic variable name was create titled idAttribute elsewhere in the notebook and was set to the common idAttribute from the topoJSON file. While you could put the topoJSON feature directly into the map code, this method is helpful for making the map code generic and easier to replicate in future maps.