Public
Edited
Aug 21, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
country = "brazil"
Insert cell
height = width * 0.5
Insert cell
width
Insert cell
keyCol = "Geocod"
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
pct_select = ["Extrema", "Baixíssima", "Média", "Alta", "Baixa"]
Insert cell
munSelected = SelectedDataMap?.length
Insert cell
munSelectedProduction = d3.sum(SelectedDataMap, (d) => d["Área de pasto degradado (2021)"])
Insert cell
munSelectedProductionPct = munSelectedProduction /
d3.sum(selectedData, (d) => d["Área de pasto degradado (2021) (ha)"])
Insert cell
munSelectedExports = d3.sum(SelectedDataMap, (d) => d["Volume exportado de carne (2020)"])
Insert cell
munSelectedExportsPct = munSelectedExports /
d3.sum(selectedData, (d) => d["Volume exportado de carne (2020) (toneladas)"])
Insert cell
valueCol = valueColSelect?.column
Insert cell
valueTitle = valueColSelect?.name
Insert cell
valueUnits = valueColSelect?.units
Insert cell
columnsToDisplay = {
const selected = possibleColumns.find((d) => d.column === valueCol);
const other = possibleColumns.filter((d) => d.column !== valueCol);
return [selected, ...other];
}
Insert cell
possibleColumns = [
{
column: "Área de pasto degradado (2021)",
name: "Área de pasto degradado (2021)",
units: "(ha)",
selectable: true,
format: ",.0~f"
},
{
column: "Desmatamento associado à produçao de carne (2020)",
name: "Desmatamento associado à produçao de carne (2020)",
units: "(ha)",
selectable: true,
format: ",.0~f"
},
{
column: "Volume exportado de carne (2020)",
name: "Volume exportado de carne (2020)",
units: "(toneladas)",
selectable: true,
format: ",.0~f"
}
]
Insert cell
tableVis
Insert cell
selectedData = data
.sort((a, b) => d3.descending(a[valueCol], b[valueCol]))
.map((d) => {
const obj = {
Municipality: `${d["Município"]}, ${d["Estado"]}`
};
for (const c of possibleColumns) {
obj[`${c.name} ${c.units}`] = d[c.column];
}
return obj;
})
.filter((element) => {
return element !== null;
})
Insert cell
data[0]["Need for intervention"]
Insert cell
SelectedDataMap = data.filter((d) =>
valuePctSelect.includes(d["Need for intervention"])
)
Insert cell
data = await FileAttachment("MT_database_beef_Insight_Nov_2023_pt@1.csv").csv({
typed: true
})
Insert cell
ti_raw = FileAttachment("tis_poligonais_MT_FUNAI_All_-4_simplified.json").json()
Insert cell
ti = ti_raw.features.map((feature) => ({
...feature,
geometry: {
...feature.geometry,
coordinates: reverseCoordinates(feature.geometry.coordinates)
}
}))
Insert cell
dataMap = new Map(
SelectedDataMap.map((d) => {
const regionId = String(d[keyCol]);
return [regionId, d];
})
)
Insert cell
companies = d3.csvParse(await FileAttachment("Companies@1.csv").text(), (d) => ({
type: "Feature",
properties: d,
geometry: {
type: "Point",
coordinates: [+d.Long, +d.Lat]
}
}))
Insert cell
circle = d3.geoPath(projection)
Insert cell
boundaries = {
let boundaries = await d3.json(
`https://unpkg.com/@trase/trase-atlas@1.1.1/files/brazil.json`
);

// Filter geometries at the top-level of the GeoJSON by "id" equal to "51"
boundaries.objects.level3.geometries =
boundaries.objects.level3.geometries.filter(
(d) => d.id.substring(0, 2) == "51"
);

boundaries.objects.level2.geometries =
boundaries.objects.level2.geometries.filter((d) => d.id == "51");

return boundaries;
}
Insert cell
features = topojson.feature(boundaries, boundaries.objects[`level${3}`])
.features
Insert cell
toTitleCase = (str) => {
return str?.toLowerCase()?.replace(/(^|\s)\S/g, (t) => t?.toUpperCase());
}
Insert cell
margin = ({
top: 10,
right: 10,
bottom: 30,
left: 10
})
Insert cell
projection = d3
.geoPolyconic()
.rotate([54, 0])
.fitExtent(
[[margin.left, margin.top], [width - margin.right, height - margin.bottom]],
topojson.feature(boundaries, boundaries.objects.level2)
)
Insert cell
projection_ti = d3
.geoPolyconic()
.rotate([54, 0])
.fitExtent(
[
[margin.left, margin.top],
[width - margin.right, height - margin.bottom]
],
{ type: "FeatureCollection", features: ti_raw.features }
)
Insert cell
colourScheme = traseGreens
Insert cell
ninetiethPercentile = d3.quantile(
data.map((d) => d[valueCol]),
0.99
)
Insert cell
tenthPercentile = d3.quantile(
data.map((d) => d[valueCol]),
0.40
)
Insert cell
extent = [tenthPercentile, ninetiethPercentile];
Insert cell
colour = d3
.scaleSequential(
d3.interpolateRgbBasis([
"#bbffec",
"#a0dfd1",
"#85bfb7",
"#6aa09c",
"#4f8081",
"#346067",
"#19404c"
])
)
.domain(extent)
Insert cell
numberFormat = ",.0~f"
Insert cell
format = (value) =>
Math.abs(value) < 10
? d3.format(".2r")(value)
: d3.format(numberFormat)(value)
Insert cell
annotate = g => {}
Insert cell
function hover(canvas, svg) {
const path = d3.geoPath(projection);
const highlight = svg
.append("path")
.attr("stroke", "#666")
.attr("stroke-width", 1.5)
.attr("fill", "none");
const tooltip = new Tooltip();
svg.append(() => tooltip.node);
canvas
.style("cursor", "pointer")
.on("touchstart", (event) => event.preventDefault())
.on("pointerenter pointermove", (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 && dataMap.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(dataMap.get(f.id)), f)
generateTooltip(f)
);
tooltip.position(cx, cy);
highlight.attr("d", path(f));
} else {
highlight.attr("d", null);
tooltip.hide();
}
})
.on("pointerout", () => {
highlight.attr("d", null);
tooltip.hide();
});
}
Insert cell
generateTooltip = (d) => {
if (!dataMap.has(d.id)) {
return svg``;
}
const spacing = 4.25;
const template = (valueTitle, valueUnits, value, i) => svg`
<text>
<tspan font-size="12px" fill="#839095" x="0" dy="${
spacing * i
}em" font-weight="${
i === 0 ? 700 : 400
}" style="text-transform:uppercase">${valueTitle} ${valueUnits}</tspan>
<tspan font-family="var(--trase-sans-serif)" font-size="14px" fill="#31464e" x="0" dy="1.4em">
<tspan font-weight="${i === 0 ? 700 : 400}">${format(value)}</tspan>
</tspan>
`;
return svg`
<line x1="0" y1="30" x2="250" y2="30" stroke="#839095" />
${columnsToDisplay.map((x, i) =>
template(x.name, x.units, dataMap.get(d.id)[x.column], i)
)}
`;
}
Insert cell
tooltipKey = data.c || `${valueTitle} ${valueUnits ? valueUnits : ""}`
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
columnsOfInterest = ({
Geocod: "Geocod",
Município: "Município",
Estado: "Estado",
"Need for intervention": "Nível requerido de intervenção (LAPIG)",
"Área de pasto degradado (2021)": "Área de pasto degradado (2021)",
"Desmatamento associado à produçao de carne (2020)":
"Desmatamento associado à produçao de carne (2020)",
"Volume exportado de carne (2020)": "Volume exportado de carne (2020)"
})
Insert cell
csv_export_data = SelectedDataMap.map((d) => {
const renamedObj = {};
Object.keys(columnsOfInterest).forEach((oldCol) => {
if (d.hasOwnProperty(oldCol)) {
const newCol = columnsOfInterest[oldCol];
renamedObj[newCol] = d[oldCol];
}
});
return renamedObj;
});
Insert cell
import { traseColours, traseGreens, fonts } from "@trase/visual-id@1366"
Insert cell
import { legend as legendTemplate } from "@trase/legends@376"
Insert cell
import { titleCard } from "@trase/title-card"
Insert cell
import { Tooltip, tooltipKeyValue, tooltipOffset } from "@trase/tooltip@440"
Insert cell
import { tableVis } with { selectedData as data } from "@trase/table-visualization"
Insert cell
d3 = require("d3@6", "d3-geo-projection@2.9")
Insert cell
import { hover as hover1 } from "@mkfreeman/plot-tooltip"
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
import { input } from "@jashkenas/inputs"
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