D3: Mappa coropletica - Legenda
<style>
.country:hover {
stroke: #b64;
stroke-width: 1px;
}
</style>
<script>
let svg = d3.select("body").append("svg")
.attr("width",670).attr("height",350)
let margin = { top: 0, right: 0, bottom: 0, left: 40 }
let graphWidth = 670 - margin.left - margin.right
let graphHeight = 350 - margin.top - margin.bottom
let graph = svg.append("g")
.attr("transform", "translate("+margin.left+","+margin.top+")")
let projection = d3.geoNaturalEarth1()
projection.translate([graphWidth/2, graphHeight/2])
.scale(projection.scale() * (graphHeight/500))
let pathGenerator = d3.geoPath().projection(projection)
Promise.all([
d3.json("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson"),
d3.tsv("https://unpkg.com/world-atlas@1.1.4/world/50m.tsv")
]).then(function([geoData, tsvData]) {
let countryIncomes = {}
tsvData.forEach(d => {
countryIncomes[d.iso_a3] = d.income_grp
})
let incomeGroups = []
tsvData.forEach(d => {
if (!incomeGroups.includes(d.income_grp)) {
incomeGroups.push(d.income_grp)
}
})
let colorScale = d3.scaleOrdinal()
.domain(incomeGroups.sort())
.range(d3.schemeBlues[incomeGroups.length+1].slice().reverse())
graph.selectAll(".country")
.data(geoData.features)
.join("path")
.attr("class", "country")
.attr("d", pathGenerator)
.attr("fill", d => {
let incomeGroup = countryIncomes[d.id]
return incomeGroup ? colorScale(incomeGroup) : "#bbb"
})
.append("title")
.text(d => {
let countryName = d.properties.name
let incomeGroup = countryIncomes[d.id] || "Unclassified"
return countryName + ":\n" + incomeGroup
})
let legendY = svg.attr("height") - incomeGroups.length*18 - 20
let legend = svg.append("g")
.attr("class", "legend")
.attr("transform", "translate(10, " + legendY + ")")
incomeGroups.forEach((group, i) => {
let legendRow = legend.append("g")
.attr("transform", "translate(0, " + (i*18) + ")")
legendRow.append("rect")
.attr("width", 15)
.attr("height", 15)
.attr("fill", colorScale(group))
legendRow.append("text")
.attr("x", 22)
.attr("y", 12)
.style("font-family", "sans-serif")
.style("font-size", "12px")
.text(group)
})
})
</script>
La legenda viene creata all’interno di un gruppo SVG (legend ) distinto da quello della mappa. Nel gruppo vengono creati altri gruppi, uno per ogni voce (legendRow ), che contengono un quadrato colorato e il testo corrispondente. Il testo viene letto direttamente dall’elemento dell’array incomeGroups passato da forEach() (group ). Il colore viene ricavato dalla scala cromatica definita in precedenza (colorScale(group) ).
La posizione della legenda è a 10 pixel dal bordo sinistro e a 20 dal bordo inferiore. La distanza in verticale fra le righe della legenda è di 18 pixel.