Public
Edited
Nov 6, 2022
Importers
3 stars
Insert cell
Insert cell
Insert cell
viewof chart = spikeMapColombia()
Insert cell
chart(data, false)
Insert cell
spikeMapColombia = (dataAsMap, options) => {
const svg = d3
.create("svg")
.style("overflow", "visible")
.attr("viewBox", [0, 0, width, height]);

const gGeo = svg.append("g");
gGeo
.selectAll(".mcpio")
.data(features.features)
.join("path")
.attr("class", "mcpio")
.style("stroke", "#ddd")
.style("stroke-width", 0.5)
.style("fill", "none")
.attr("d", d => path(d));

gGeo
.selectAll(".depto")
.data(topojson.feature(mapData, mapData.objects.MGN_AMN_DPTOS).features)
.join("path")
.attr("class", "depto")
.style("fill", "none")
.style("stroke", "#aaa")
.style("stroke-width", 0.5)
.attr("d", d => path(d));

const gMountains = svg.append("g");

const gLegend = svg
.append("g")
.attr("class", "gLegend")
.attr(
"transform",
`translate(${width - margin.right - margin.left - 150},${margin.top +
150})`
);

gLegend
.append("text")
.attr("id", "legendTitle")
.attr("y", 30)
.attr("x", -6)
.style("font-family", "sans-serif")
.style("font-size", "10pt")
.style("font-style", "italic");

svg
.append("text")
.attr("class", "tooltip")
.style("font-family", "sans-serif")
.style("font-size", "12pt")
.attr("transform", "translate(50, 50)")
.call(text =>
text
.append("tspan")
.attr("class", "tooltipName")
.style("font-weight", "bolder")
.attr("y", 0)
.attr("x", 0)
.text()
)
.call(text =>
text
.append("tspan")
.attr("class", "tooltipValue")
.style("font-size", "10pt")
.style("font-style", "italic")
.attr("y", 15)
.attr("x", 0)
.text()
);

const update = (dataAsMap, options) => {
const {
normalize = false,
dynamicScale = true,
duration = 250,
sort = false,
spikeHeight = 250,
normalizeFactor = 1000000,
legendTitle = "Título"
} = options;

// const svg = d3.select(svg);
const before = new Date();
console.log("dataAsMap", (new Date() - before) / 1000, dataAsMap);

let notFound = [];
features.features.forEach(d => {
const value = dataAsMap.get(d.properties.MPIO_CDPMP);
if (value === undefined) {
notFound.push(d.properties.MPIO_CDPMP);
d.value = undefined;
return;
}
d.value =
value / (normalize ? d.properties.STP27_PERS / normalizeFactor : 1);
});
if (notFound.length) {
console.log("Municipios no encontrados", notFound);
}
console.log("features.value", (new Date() - before) / 1000);

const dataExtent = d3.extent(features.features, d => d.value);
const rangeLegend = d3.range(
dataExtent[0],
dataExtent[1] + Number.MIN_VALUE,
(dataExtent[1] + Number.MIN_VALUE - dataExtent[0]) / 6
);

h.range([0, spikeHeight]);
if (dynamicScale) {
updateScale(dataExtent);
}

if (sort) {
features.features = features.features.sort((a, b) =>
sort === "descending" ? b.value - a.value : a.value - b.value
);
console.log("sorted ", (new Date() - before) / 1000);
}

const onMouseOver = function(evt, d) {
const [x, y] = path.centroid(d);

gMountains.selectAll(".mountain").style("stroke-opacity", 0.3);
d3.select(this).style("stroke-opacity", 1);
svg
.select(".tooltip")
.attr("transform", `translate(${x + 10}, ${y})`)
.call(text =>
text
.select(".tooltipName")
.text(`${d.properties.MPIO_CNMBR} ${d.properties.DPTO_CNMBR}`)
)
.call(text => text.select(".tooltipValue").text(fmt(d.value)));
};

const onMouseOut = function() {
gMountains.selectAll(".mountain").style("stroke-opacity", 1);
svg.selectAll(".tooltip tspan").text("");
};

gMountains
.selectAll(".mountain")
.data(
features.features.filter(d => d.value !== undefined),
d => d.properties.MPIO_CDPMP
)
.join("path")
.attr("class", "mountain")
.attr("transform", d => `translate(${projection(d3.geoCentroid(d))})`)
.style("fill", "#eee")
.style("fill-opacity", 0.9)
.style("stroke", d => color(d.value))
.style("stroke-width", 1)
.on("mouseover", onMouseOver)
.on("mouseout", onMouseOut)
.transition()
.duration(duration)
.attr("d", d => mountain(d.value));

gLegend.select("#legendTitle").text(legendTitle);
gLegend
.selectAll("g.legend")
.data(rangeLegend)
.join(enter =>
enter
.append("g")
.attr("class", "legend")
.call(g => {
g.append("path");
g.append("text");
})
)
.attr("transform", (d, i) => `translate(${i * 25},0)`)
.call(legend =>
legend
.select("path")
.attr("class", "legendMountain")
.style("fill", "#eee")
.style("fill-opacity", 0.9)
.style("stroke", d => color(d))
.style("stroke-width", 0.7)
.transition()
.duration(duration)
.attr("d", d => mountain(d))
)
.call(legend =>
legend
.select("text")
.style("font-size", "6pt")
.style("text-anchor", "middle")
.style("font-family", "sans-serif")
.attr("y", 10)
.text(d => fmt(d))
);
}; // update

svg.node().value = update;

if (dataAsMap) update(dataAsMap, options);

return svg.node();
}
Insert cell
updateScale = dataExtent => {
const max = Math.abs(Math.max(...dataExtent));
h.domain([0, Math.abs(dataExtent[1])]);
color.domain([-max, max]);
}
Insert cell
data = {
const data = new Map(
features.features.map(d => [d.properties.MPIO_CDPMP, d.properties[spikeBy]])
);
features.features = features.features.sort((a, b) =>
d3.ascending(a.properties.STP27_PERS, b.properties.STP27_PERS)
);
return data;
}
Insert cell
features.features.filter(d => d.properties.MPIO_CDPMP == "05059")
Insert cell
fmt = d3.format(".2s")
Insert cell
h = d3
.scaleLinear()
// .domain([0, dataExtent[1]])
.range([0, 250])
.nice()
Insert cell
color =
d3.scaleSequential(d3.interpolateYlOrRd)
// .domain([-max, max]);

Insert cell
mountain = (d, mx = 5) => {
return `M${-mx / 2},0 Q0,${-h(d)} ${mx / 2}, 0`;
}
Insert cell
path = d3.geoPath().projection(projection)
Insert cell
projection = d3
.geoAzimuthalEqualArea()
.rotate([74 + 30 / 60, -38 - 50 / 60])
.fitExtent(
[[margin.left, margin.top], [width - margin.right, height - margin.bottom]],
features
)
Insert cell
margin = ({ left: 20, right: 20, top: 20, bottom: 20 })
Insert cell
height = 900
Insert cell
line = d3.line()
Insert cell
features = {
const features = topojson.feature(mapData, mapData.objects.MGN_AMN_MPIOS);
features.features = features.features.sort(
(a, b) => a.properties.STP27_PERS - b.properties.STP27_PERS
);
return features;
}
Insert cell
mapData = FileAttachment(
"Colombia_departamentos_municipios_poblacion_deptos-topo.json"
).json()
Insert cell
// dDeptos = new Map(
// mapData.objects.MGN_AMN_DPTOS.geometries.map(d => [
// d.properties.DPTO_CCDGO,
// d.properties.DPTO_CNMBR
// ])
// )
Insert cell
// mapData2 = {
// const mapData2 = mapData;
// mapData2.objects.MGN_AMN_MPIOS.geometries.forEach(
// d => (d.properties["DPTO_CNMBR"] = dDeptos.get(d.properties.DPTO_CCDGO))
// );
// return mapData2;
// }
Insert cell
d3 = require("d3@6")
Insert cell
topojson = require("topojson-client@3")
Insert cell
import { select } from "@jashkenas/inputs"
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