Published
Edited
Aug 6, 2022
Importers
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
country = "brazil"
Insert cell
height = width * 0.625
Insert cell
data = new Map(
d3
.csvParse(await FileAttachment("data.csv").text())
.map(({ id, value }) => [id, +value])
)
Insert cell
boundaries = d3.json(
`https://unpkg.com/@bayre/trase-atlas@1.1.0/files/${country.toLowerCase()}.json`
)
Insert cell
features = topojson.feature(boundaries, boundaries.objects[`level${level}`])
.features
Insert cell
// levels of data resolution - will need updating over time
levels = new Map([
["argentina", 3],
["brazil", 3],
["colombia", 2],
["ecuador", 3],
["paraguay", 2],
["indonesia", 3]
])
Insert cell
level = levels.get(country.toLowerCase())
Insert cell
projections = new Map([
[
"argentina",
d3
.geoTransverseMercator()
.rotate([69, 0])
.fitSize(
[width, height],
topojson.mesh(boundaries, boundaries.objects.level1)
)
],
[
"brazil",
d3
.geoPolyconic()
.rotate([54, 0])
.fitSize(
[width, height],
topojson.mesh(boundaries, boundaries.objects.level1)
)
],
[
"colombia",
d3
.geoTransverseMercator()
.rotate([68, 4.6])
.fitSize(
[width, height],
topojson.mesh(boundaries, boundaries.objects.level1)
)
],
[
"ecuador",
d3
.geoTransverseMercator()
.rotate([50, 55])
.fitSize(
[width, height],
topojson.mesh(boundaries, boundaries.objects.level1)
)
],
[
"paraguay",
d3
.geoTransverseMercator()
.rotate([63, 0])
.fitSize(
[width, height],
topojson.mesh(boundaries, boundaries.objects.level1)
)
],
[
"indonesia",
d3
.geoTransverseMercator()
.rotate([-139.5, 0])
.fitSize(
[width, height],
topojson.mesh(boundaries, boundaries.objects.level1)
)
]
])
Insert cell
projection = projections.get(country.toLowerCase())
Insert cell
colourScheme = traseBlues
Insert cell
colour = {
const values = [...data.values()].sort((a, b) => a - b);
const length = values.length >= 5 ? 5 : values.length
const breaks = simple.ckmeans(values, length).map(d => d.pop());
return d3
.scaleThreshold()
.domain(breaks)
.range(colourScheme[length]);
}
Insert cell
format = value => Math.abs(value) < 1 ? d3.format(".2r")(value) : d3.format(".2s")(value)
Insert cell
annotate = g => {}
Insert cell
function hover(canvas, svg) {
const path = d3.geoPath(projection);
const highlight = svg
.append("path")
.attr("stroke", "white")
.attr("stroke-width", 2)
.attr("fill", "none");
const tooltip = new Tooltip();
svg.append(() => tooltip.node);
canvas
.on("mousemove", event => {
// get feature
let [cx, cy] = d3.pointer(event);
const [x, y] = projection.invert([cx, cy]);
const candidates = index.search(x, y, x, y).map(i => features[i]);
const f = candidates.find(f => turf.booleanPointInPolygon([x, y], f));
// define interaction
if (f && data.has(f.id)) {
(cx += 10), (cy += 10);
if (cx + tooltip.width > width) cx -= tooltip.width + 20;
if (cy + tooltip.height > height) cy -= tooltip.height;
tooltip.show(f.properties.name, tooltipKeyValue(tooltipKey, format(data.get(f.id)), f));
tooltip.position(cx, cy);
highlight.attr("d", path(f));
} else {
highlight.attr("d", null);
tooltip.hide();
}
})
.on("mouseout", () => {
highlight.attr("d", null);
tooltip.hide();
});
}
Insert cell
tooltipKey = "VALUE"
Insert cell
// https://observablehq.com/@rdmurphy/combining-html-canvas-svg-flatbush-for-super-efficient-hov
script = {
const blob = new Blob([`
importScripts("${await require.resolve("flatbush@3.2")}");

onmessage = (event) => {
const { data } = event.data;
const index = new Flatbush(data.length);
data.forEach(bbox => { index.add(...bbox) });
index.finish();
postMessage(index.data);
close();
};
`], {type: "text/javascript"});
const script = URL.createObjectURL(blob);
invalidation.then(() => URL.revokeObjectURL(script));
return script;
}
Insert cell
index = Generators.queue(notify => {
const worker = new Worker(script);
invalidation.then(() => worker.terminate());
worker.addEventListener("message", ({data}) => notify(Flatbush.from(data)))
worker.postMessage({ data: features.map(d => turf.bbox(d)) });
});
Insert cell
import { traseBlues, traseGreys } from "4be0b7b0578fccff@1315"
Insert cell
import { legend } from "657be07736bb79d2@353"
Insert cell
import { Tooltip, tooltipKeyValue } from "3689ef3fedaa3591@429"
Insert cell
d3 = require("d3@6", "d3-geo-projection@2.9")
Insert cell
topojson = require("topojson-client@3")
Insert cell
Flatbush = require("flatbush@3.2")
Insert cell
turf = require("@turf/turf@5")
Insert cell
simple = require("simple-statistics@7")
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