Published
Edited
Oct 9, 2022
1 fork
2 stars
Insert cell
Insert cell
Insert cell
viewof interfaceToggle = {
mutable reactDebug = [];
return Inputs.toggle({label:"Mostrar Interface", value: true})
}
Insert cell
mutable biglayoutToggle = false
Insert cell
mutable filtrarPorRolldownToggle = false
Insert cell
mutable reactDebug = []
Insert cell
clearMainWindow = {
mutable reactDebug = ["clear"];
interfaceToggle;
width;
for (let className of [
"menuPrincipal",
"indicadorChave",
"indicadorChaveSmall",
"mapa",
"scatterplot",
"scatterplotBig",
"filtrarPor",
"filtrarPorSmall",
]) {
const elem = mainWindow.querySelector(`div.${className}`);
elem.innerHTML = className;
}
if (biglayoutToggle) {
mainWindow.querySelector(`div.scatterplot`).style.display = "none";
mainWindow.querySelector(`div.scatterplotBig`).style.display = "block";
mainWindow.querySelector(`div.indicadorChave`).style.display = "none";
mainWindow.querySelector(`div.indicadorChaveSmall`).style.display = "block";
} else {
mainWindow.querySelector(`div.scatterplotBig`).style.display = "none";
mainWindow.querySelector(`div.scatterplot`).style.display = "block";
mainWindow.querySelector(`div.indicadorChaveSmall`).style.display = "none";
mainWindow.querySelector(`div.indicadorChave`).style.display = "block";
}
if (filtrarPorRolldownToggle) {
mainWindow.querySelector(`div.filtrarPorSmall`).style.display = "none";
mainWindow.querySelector(`div.filtrarPor`).style.display = "block";
} else {
mainWindow.querySelector(`div.filtrarPorSmall`).style.display = "block";
mainWindow.querySelector(`div.filtrarPor`).style.display = "none";
}
}
Insert cell
panel = {
mutable reactDebug.push ("panel")
clearMainWindow;
let panel = {};
for (let className of ["menuPrincipal", "indicadorChave", "indicadorChaveSmall",
"mapa", "scatterplot", "scatterplotBig", "filtrarPor", "filtrarPorSmall"]) {
let elem = mainWindow.querySelector (`div.${className}`);
panel[className] = {elem, w:elem.clientWidth, h:elem.clientHeight}
};
return panel;
}
Insert cell
//
// Insert elements into grid (other than the map)
//
populate = {
mutable reactDebug.push ("populate")
if (interfaceToggle) {
panel.menuPrincipal.elem.innerHTML = "";
//panel.menuPrincipal.elem.append (viewof munSelecionados);
panel.menuPrincipal.elem.append (
htl.html`<div>${viewof munSelecionados}<br><div style="margin-top:10px">Selecione até 4 municípios com duplo clique nos gráficos</div></div>`);
viewof munSelecionados.style.display = "inline";
viewof munSelecionados.style.paddingTop = "10px";
panel.menuPrincipal.elem.append (viewof ano);
viewof ano.style.display = "inline";

const indicadorChave = biglayoutToggle ? panel.indicadorChaveSmall : panel.indicadorChave;

const indicadorDiv = htl.html`<div style = "display:flex; justify-content: space-between;margin-bottom:10px">`;
const indicadorTitle = htl.html`<span style="display:inline">INDICADOR CHAVE</span>`;
const indicadorExpandReduce = biglayoutToggle ? makeRollDownIcon () : makeRollUpIcon ();
indicadorExpandReduce.style.cursor = "pointer";
indicadorDiv.onclick = () => mutable biglayoutToggle = !mutable biglayoutToggle;
indicadorDiv.append(indicadorTitle, indicadorExpandReduce);
indicadorChave.elem.innerHTML = "";
indicadorChave.elem.append(indicadorDiv,
viewof colorSelect,
htl.html`<br><br>`,
legendaEscalaCores
);
rankingBox.style.marginTop = "10px";
if (!biglayoutToggle) {
indicadorChave.elem.append(rankingBox);
rankingBox.querySelector("div.rankBox").dispatchEvent(new CustomEvent("scroll", {scrollTop:0}));
}

const scatter = (biglayoutToggle) ? panel.scatterplotBig : panel.scatterplot;
scatter.elem.innerHTML = "";
viewof yAxisSelect.style.display = "inline-block";
viewof yAxisSelect.style.marginBottom = "10px";
viewof yAxisSelect.style.lineHeight = "15px";
viewof xAxisSelect.style.display = "inline-block";
viewof xAxisSelect.style.float = "right";
viewof xAxisSelect.style.marginTop = "5px";
viewof xAxisSelect.style.lineHeight = "15px";
const relacionarExpandReduce = biglayoutToggle ? makeReduceIcon () : makeEnlargeIcon ();
relacionarExpandReduce.style.marginLeft = "5px";
relacionarExpandReduce.style.cursor = "pointer";
relacionarExpandReduce.style.position = "absolute";
relacionarExpandReduce.style.right = "10px";
relacionarExpandReduce.style.top = "10px";
relacionarExpandReduce.style.zIndex = 20;
relacionarExpandReduce.onclick = () => mutable biglayoutToggle = !mutable biglayoutToggle;
//yAxisAndButton.append(viewof yAxisSelect, relacionarExpandReduce);
//relacionarDiv.append(relacionarTitle, yAxisAndButton);
scatter.elem.append(relacionarExpandReduce, viewof yAxisSelect, scatterplotComponent,viewof xAxisSelect,);

const filtrarPor = (filtrarPorRolldownToggle) ? panel.filtrarPor : panel.filtrarPorSmall;
const filtrarPorDiv = htl.html`<div style = "display:flex; justify-content: space-between;margin-bottom:10px">`;
const filtrarPorTitle = htl.html`<span style="display:inline">FILTRAR POR</span>`;
const filtrarPorExpandReduce = filtrarPorRolldownToggle ? makeRollUpIcon () : makeRollDownIcon ();
filtrarPorDiv.onclick = () => mutable filtrarPorRolldownToggle = !mutable filtrarPorRolldownToggle;
filtrarPorDiv.append (filtrarPorTitle, filtrarPorExpandReduce)
filtrarPor.elem.innerHTML = "";
filtrarPor.elem.append(filtrarPorDiv);
if (filtrarPorRolldownToggle) filtrarPor.elem.append(painelFiltros);
}
}
Insert cell
//
// We must compute the size of the map div after the initial layout
// was established with the modifications introduced by populate
//
afterInitialLayout = {
mutable reactDebug.push ("afterInitial")
populate;
panel.mapa.oldW = panel.mapa.w;
panel.mapa.oldH = panel.mapa.h;
// Avoid recomputing the map if the element is the same size and has already been
// placed in the interface
if (panel.mapa.elem.clientWidth != panel.mapa.w ||
panel.mapa.elem.clientHeight != panel.mapa.h ||
panel.mapa.elem.children.length == 0) {
panel.mapa.w = panel.mapa.elem.clientWidth;
panel.mapa.h = panel.mapa.elem.clientHeight;
mutable remakeMapa = true;
}
}
Insert cell
//
// At this point, the dorling map must have been computed with the right size.
//
populateMapa = {
mutable reactDebug.push ("populateMapa");
if (interfaceToggle && panel.mapa.oldElem != viewof dorling) {
panel.mapa.elem.innerHTML = "";
panel.mapa.elem.append (viewof dorling);
}
}
Insert cell
mutable remakeMapa = false
Insert cell
Insert cell
//
// Maintain the session storage variable for synchronizing with other visualizations
//
storageUpdate = {
sessionStorage.setItem("municipios", JSON.stringify(munSelecionados));
}
Insert cell
initialLoad = {
let defaultSelection = sessionStorage.getItem("municipios");
if (!defaultSelection) defaultSelection = ["SÃO PAULO"];
else defaultSelection = JSON.parse(defaultSelection);
viewof munSelecionados.value = defaultSelection;
}
Insert cell
Insert cell
Insert cell
viewof ano = {
mutable reactDebug.push ("ano")
return sliderbase({range:[2012,2019],value:2015,ticks:1,width:400, height:56})
}
Insert cell
Insert cell
viewof filtroNascimentos = {
mutable reactDebug.push ("filtroNascimentos")
interfaceToggle;
return sliderArray({range:[1,10,1e2,1e3,1e4,1e5,2e5],
value:[1,6],
ticksLabelFormatter:(i)=>["1","10","100","1K","10K","100K","200K"][i],
width:300, height:56
},sliderRangeBase)
}

Insert cell
Insert cell
Insert cell
viewof filtroRegiao = {
mutable reactDebug.push ("filtroRegiao")
interfaceToggle;
return aSelect({
options: ["(todas)", "Centro-oeste", "Nordeste", "Norte", "Sudeste", "Sul"],
width: 250,
});
}
Insert cell
filtroRegiaoDrawer =
aDrawer(
html`<div style="padding:4px 0px 20px 10px;">${viewof filtroRegiao}`,
{shown:true, title:"Região", subtitle: "Mostrar apenas uma região"})
Insert cell
Insert cell
painelFiltros = {
mutable reactDebug.push ("painelFiltros")
interfaceToggle;
const painel = htl.html`<div>`;
painel.append(filtroNascimentosDrawer, filtroRegiaoDrawer);
return painel;
}
Insert cell
Insert cell
allFilters = {
mutable reactDebug.push ("allFilters")
const regionIndex = ["Norte", "Nordeste", "Sudeste", "Sul", "Centro-oeste"].indexOf(filtroRegiao);
const [nascMin, nascMax] = filtroNascimentos.map(i => [0,10,1e2,1e3,1e4,1e5,2e5][i]);
//return {regionIndex, nascMin, nascMax}
const filter = d =>
(regionIndex == -1 || regionIndex == Math.floor(d.CODMUNRES / 100000) - 1) &&
d.ANO == ano &&
+d.TOTAL >= nascMin &&
+d.TOTAL <= nascMax

// Apply to the scatterplot
mutable dataFilter = filter
return filter
}
Insert cell
Insert cell
viewof colorSelect = {
interfaceToggle;
mutable reactDebug.push ("colorSelect")
return aSelect({
options: variables.map(d=>d.name),
width: 280,
});
}
Insert cell
color = {
mutable colorVar = variables.find (d=>d.name == colorSelect);
return mutable colorVar
}
Insert cell
viewof xAxisSelect = {
interfaceToggle;
mutable reactDebug.push ("xAxisSelect")
return aSelect({
options: variables.map(d=>d.name),
value: "MORT. PERINATAL",
width: 110,
});
}
Insert cell
// Reactively change the xAxisVar mutable
xaxis = {
mutable xAxisVar = variables.find (d=>d.name == xAxisSelect);
return mutable xAxisVar
}
Insert cell
viewof yAxisSelect = {
interfaceToggle;
mutable reactDebug.push ("yAxisSelect")
return aSelect({
options: variables.map(d=>d.name),
value: "BAIXO PESO",
width: 110,
});
}
Insert cell
// Reactively change the yAxisVar mutable
yaxis = {
mutable yAxisVar = variables.find (d=>d.name == yAxisSelect);
return mutable yAxisVar
}
Insert cell
Insert cell
normalizeString = s => s.toUpperCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "")
Insert cell
mutable unknownData = null
Insert cell
filteredData = {
mutable unknownData = [];
let filtered = [];
dadosMunicipio.filter(allFilters).forEach(d => {
const mun = codigoToMunicipio.get(d.CODMUNRES);
if (!mun) {
mutable unknownData.push (d);
return;
};
//const mun2 = municipioPorCodigo.get(codToCodarea("110001"));
const name = mun.nome;
const uf = mun.uf;
const val = +colorVar.field(d);
filtered.push( {name,uf,mun,normName:normalizeString(name),val} )
})
filtered.sort((a,b) => b.val - a.val);
return filtered
}
Insert cell
rankStyles = htl.html`<style>
div.rankBox {
max-height:200px;
max-width:280px;
line-height: 20px;
overflow-y: scroll;
}
div.rankItem {
display:block;
}
div.rankItem:hover {
border: 1px solid black;
cursor: pointer;
}
div.rankTitle {
font-family: Roboto Condensed;
font-size: 11px;
margin-top:20px;
margin-bottom:10px;
}
div.rankItem div {
display:inline-block;
font-family: Roboto Condensed;
font-size: 10px;
font-weight: 400;
line-height: 11px;
letter-spacing: 0em;
text-align: left;
padding: 3px 8px 3px 8px;
}
div.rankItem div.rank {
min-width: 20px;
max-width: 20px;
text-align: right;
}
div.rankItem div.name {
min-width: 175px;
max-width: 175px;
}
div.rankItem div.uf {
min-width: 20px;
max-width: 20px;
}
</style>`
Insert cell
rankingBox = {
interfaceToggle;
const listContainer = htl.html`<div class=rankBox >`;
const colorRange = colorVar.range;
const listTitle = htl.html`<div class=rankTitle style="color:${colorRange[colorRange.length-1]}">MUNICÍPIOS RANQUEADOS POR ${colorVar.name}`;
const listSearch = aText({width:280,placeholder:"🔍 BUSCAR"});
const createBoxes = (d,i) => {
const entry = htl.html`<div class=rankItem>`;
const cor = escalaCores(d.val);
const textColor = (d3.hsl(cor).l > 0.5) ? "black" : "white";
const rank = htl.html`<div class=rank style="color:${textColor};background:${cor};" >${i+1}`;
const name = htl.html`<div class=name>${d.name}`;
const uf = htl.html`<div class=uf>${d.uf}`;
const mun = d.mun;
entry.append (rank,name,uf);
entry.ondblclick = () => alteraMenuLocalidade (mun);
return entry;
}
listSearch.style.marginBottom = "10px";
const alteraCallback = 0;
const fakeList = htl.html`<div style="position:relative;min-width:265px;min-height:${20*filteredData.length}px;" >`;
const fakeContents = htl.html`<div style="position:absolute;min-width:265px;min-height:${20*8}px;" >`;
fakeList.append(fakeContents);
listContainer.append(fakeList);
const showBoxes = (posY) => {
fakeContents.style.top = posY+"px";
fakeContents.innerHTML = "";
const first = ~~(posY/20);
const last = Math.min(first+10, filteredData.length);
for (let i = first; i < last; i++) {
const entry = createBoxes (filteredData[i], i);
fakeContents.append(entry);
}
}
listContainer.onscroll = (e) => {
showBoxes(listContainer.scrollTop);
}
showBoxes(0)
listSearch.oninput = (event) => {
let s = normalizeString(listSearch.value);
for (let i = 0; i < filteredData.length; i++) {
if (filteredData[i].normName.includes(s)) {
listContainer.scrollTo(0, 20*i);
break;
}
}
}
return html`<div>${listTitle}${listSearch}${listContainer}`
}
Insert cell
Insert cell
dorlingStyles = html`
<style>
.circle:not(.disabled):hover {
stroke:black;
stroke-width:1px;
}
</style>
`
Insert cell
viewof dorling = {
interfaceToggle;
remakeMapa;
mutable reactDebug.push("dorling");
let [[xmin, ymin], [xmax, ymax]] = limites;
let width = panel.mapa.w;
let height = Math.max(panel.mapa.h);
let mapScale = Math.max(0.2, Math.min (3,
(width - 318 * (mutable filtrarPorRolldownToggle ? 2 : 1)) / (xmax - xmin)));

const container = html`<div style="position:relative;width:${width}px;height:${height}px;">`;

//
// Botões de Zoom
//
const zoomIn = zoomInIcon;
Object.assign(zoomIn.style, {
position: "absolute",
zIndex: 10,
top: "10px",
left: interfaceToggle ? "328px" : "10px"
});
const zoomOut = zoomOutIcon;
Object.assign(zoomOut.style, {
position: "absolute",
zIndex: 10,
top: "38px",
left: interfaceToggle ? "328px" : "10px"
});
container.append(zoomIn, zoomOut);
zoomIn.onclick = () => doZoom(12 / 10);
zoomOut.onclick = () => doZoom(10 / 12);

//
// O svg principal
//
const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("id", "dorling");

container.append(svg.node());
container.value = [];
container.hovered;

//
// Um grupo para conter o mapa propriamente dito.
// Precisamos dele para poder aplicar translações mais facilmente
//
const mainGroup = svg.append("g").attr("class", "mainGroup");

//
// Tratamento de eventos para translação e escala
//
let translation = [interfaceToggle ? 328 : 0, 0],
mousedown = null;
svg
.on("mousedown", function (e) {
mousedown = [e.offsetX, e.offsetY];
svg.style("cursor", "grab");
})
.on("mousemove", function (e) {
if (!mousedown) return;
let mouse = [e.offsetX, e.offsetY];
let displacement = [mouse[0] - mousedown[0], mouse[1] - mousedown[1]];
translation[0] += displacement[0];
translation[1] += displacement[1];
mainGroup.attr("transform", `translate(${translation})`);
mousedown = mouse;
})
.on("mouseup", function (e) {
if (!mousedown) return;
svg.style("cursor", "auto");
mousedown = null;
})
.on("wheel", function (e) {
e.preventDefault();
e.stopPropagation();
const scaleFactor = e.deltaY > 0 ? 11 / 10 : 10 / 11;
const center = [e.offsetX, e.offsetY];
doZoom(scaleFactor, center);
// mutable debug = {x : e.offsetX, y:e.offsetY, delta:[e.deltaX, e.deltaY, e.deltaZ, e.deltaMode]};
});

mainGroup.attr("transform", `translate(${translation})`);

//
// Grupo contendo os círculos. Só este é escalado
//
let circlesGroup = mainGroup
.append("g")
.classed("circles", true)
.attr("transform", `scale(${mapScale},${mapScale})`);

//
// Círculos para os municípios
// Apenas as posições são configuradas aqui. As demais (raio, cor, etc)
// são configuradas externamente pela célula dorlingCircleConf, evitando
// assim o overhead de recriar os círculos quando os filtros mudam
//
let circles = circlesGroup
.selectAll("circle")
.data(posicoesFinais)
.join("circle") // joins the data to the empty circles selected
.on("dblclick", dbclicked)
.classed("circle", true)
.attr("cx", (d) => d.x)
.attr("cy", (d) => d.y);

//
// Tooltips para os círculos

var tip = d3tip().html(
(event, d) => `
${tooltip({
pol: dadosPorCodigo.get(d.properties.codarea.slice(0, -1))["AVG_PM25"],
bp:
(1000 *
dadosPorCodigo.get(d.properties.codarea.slice(0, -1))[
"BAIXO_PESO"
]) /
dadosPorCodigo.get(d.properties.codarea.slice(0, -1))["TOTAL"],
municipio: municipioPorCodigo.get(d.properties.codarea)[
"Nome_Município"
],
uf: municipioPorCodigo.get(d.properties.codarea)["Nome_UF"],
populacao: populacaoPorCodigo.get(d.properties.codarea)
})}`
);

svg.call(tip);
circles.on("mouseover", tip.show).on("mouseout", tip.hide);

//
// As bandeirinhas para os municípios selecionados
//
let flags = mainGroup.append("g").classed("flags", true);
const updateFlags = () => {
flags.selectAll("path").remove();
flags
.selectAll("path")
.data(viewof munSelecionados.value)
.join("path")
.attr("transform", (d) => {
let pos = posicoesPorCod.get(d.cod);
return `translate(${pos.x * mapScale},${pos.y * mapScale})`;
})
.attr("d", flagPath(12))
.attr("fill", (d) => d.cor)
.attr("stroke", "black");
};

//
// Realiza o zoom dos círculos mantendo o ponto center fixo
// Se center não for passado, assume-se o centro do svg
//
function doZoom(scaleFactor, center) {
const [cx, cy] = center || [width / 2, height / 2];
const [tx, ty] = translation;
const newScale = Math.max(0.2, Math.min (3, mapScale * scaleFactor));
const [x0, y0] = [(cx - tx) / mapScale, (cy - ty) / mapScale];
const [newTx, newTy] = [cx - x0 * newScale, cy - y0 * newScale];
translation = [newTx, newTy];
mapScale = newScale;
mainGroup.attr("transform", `translate(${translation})`);
circlesGroup.attr("transform", `scale(${mapScale},${mapScale})`);
updateFlags();
}

function dbclicked() {
const oldValue = viewof munSelecionados.value;
const circle = d3.select(this);
const findMun = (circle) => {
const codigo = circle.data()[0].properties.codarea.slice(0, -1);
const filtrados = municipios.filter((obj) => obj.cod == codigo);
return filtrados[0];
};

let datum = findMun(circle);
alteraMenuLocalidade(datum);
updateFlags();
}

function updateSelected() {
// mutable TESTE = {}
const oldValue = viewof munSelecionados.value;
const codigos = oldValue.map((d) => d.cod);
circles.each(function (d) {
const circle = d3.select(this);
const selected = codigos.includes(d.properties.codarea.slice(0, -1));
circle.classed("clicked", selected);
});
updateFlags();
}

updateSelected();
container.updateSelected = updateSelected;

// Export the circle selection so that we can configure them externally
container.circleSelection = circles;

return container;
}
Insert cell
mutable debug = 0
Insert cell
//
// Reactively configure the dorling circles
//
dorlingCircleConf = {
mutable reactDebug.push("dorlingCircleConf");
viewof dorling.circleSelection.each(function (d) {
const codigo = d.properties.codarea;
const dado = dadosPorCodigo.get(codigo.slice(0, -1));
const circle = d3.select(this);
const disabled = !allFilters(dado);
circle
.classed("disabled", disabled)
.attr("r", raio(populacaoPorCodigo.get(codigo)))
.attr("fill", disabled ? "lightgray" : escalaCores(colorVar.field(dado)));
});
}
Insert cell
Insert cell
zoomInIcon = html`<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 20C15.5228 20 20 15.5228 20 10C20 4.47715 15.5228 0 10 0C4.47715 0 0 4.47715 0 10C0 15.5228 4.47715 20 10 20Z" fill="#151472"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.5 6C7.567 6 6 7.567 6 9.5C6 11.433 7.567 13 9.5 13C10.443 13 11.2988 12.6271 11.9282 12.0207C11.9414 12.0036 11.9558 11.9871 11.9714 11.9714C11.9871 11.9558 12.0036 11.9414 12.0207 11.9282C12.6271 11.2988 13 10.443 13 9.5C13 7.567 11.433 6 9.5 6ZM13.0159 12.3088C13.6318 11.539 14 10.5625 14 9.5C14 7.01472 11.9853 5 9.5 5C7.01472 5 5 7.01472 5 9.5C5 11.9853 7.01472 14 9.5 14C10.5625 14 11.539 13.6318 12.3088 13.0159L14.1464 14.8536C14.3417 15.0488 14.6583 15.0488 14.8536 14.8536C15.0488 14.6583 15.0488 14.3417 14.8536 14.1464L13.0159 12.3088ZM9.5 7.5C9.77614 7.5 10 7.72386 10 8V9H11C11.2761 9 11.5 9.22386 11.5 9.5C11.5 9.77614 11.2761 10 11 10H10V11C10 11.2761 9.77614 11.5 9.5 11.5C9.22386 11.5 9 11.2761 9 11V10H8C7.72386 10 7.5 9.77614 7.5 9.5C7.5 9.22386 7.72386 9 8 9H9V8C9 7.72386 9.22386 7.5 9.5 7.5Z" fill="#E5E5E5"/>
</svg>`
Insert cell
zoomOutIcon = html`<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 20C15.5228 20 20 15.5228 20 10C20 4.47715 15.5228 0 10 0C4.47715 0 0 4.47715 0 10C0 15.5228 4.47715 20 10 20Z" fill="#151472"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.5 6C7.567 6 6 7.567 6 9.5C6 11.433 7.567 13 9.5 13C10.443 13 11.2988 12.6271 11.9282 12.0207C11.9414 12.0036 11.9558 11.9871 11.9714 11.9714C11.9871 11.9558 12.0036 11.9414 12.0207 11.9282C12.6271 11.2988 13 10.443 13 9.5C13 7.567 11.433 6 9.5 6ZM13.0159 12.3088C13.6318 11.539 14 10.5625 14 9.5C14 7.01472 11.9853 5 9.5 5C7.01472 5 5 7.01472 5 9.5C5 11.9853 7.01472 14 9.5 14C10.5625 14 11.539 13.6318 12.3088 13.0159L14.1464 14.8536C14.3417 15.0488 14.6583 15.0488 14.8536 14.8536C15.0488 14.6583 15.0488 14.3417 14.8536 14.1464L13.0159 12.3088ZM7.5 9.5C7.5 9.22386 7.72386 9 8 9H11C11.2761 9 11.5 9.22386 11.5 9.5C11.5 9.77614 11.2761 10 11 10H8C7.72386 10 7.5 9.77614 7.5 9.5Z" fill="#E5E5E5"/>
</svg>`
Insert cell
makeEnlargeIcon = () => html`<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.5 2C7.22386 2 7 1.77614 7 1.5C7 1.22386 7.22386 1 7.5 1H10.5C10.7761 1 11 1.22386 11 1.5V4.5C11 4.77614 10.7761 5 10.5 5C10.2239 5 10 4.77614 10 4.5V2.70711L7.35355 5.35355C7.15829 5.54882 6.84171 5.54882 6.64645 5.35355C6.45118 5.15829 6.45118 4.84171 6.64645 4.64645L9.29289 2H7.5ZM4.64645 6.64645C4.84171 6.45118 5.15829 6.45118 5.35355 6.64645C5.54882 6.84171 5.54882 7.15829 5.35355 7.35355L2.70711 10H4.5C4.77614 10 5 10.2239 5 10.5C5 10.7761 4.77614 11 4.5 11H1.5C1.22386 11 1 10.7761 1 10.5V7.5C1 7.22386 1.22386 7 1.5 7C1.77614 7 2 7.22386 2 7.5V9.29289L4.64645 6.64645Z" fill="black"/>
</svg>`
Insert cell
makeReduceIcon = () => html`<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.1464 1.14645C10.3417 0.951184 10.6583 0.951184 10.8536 1.14645C11.0488 1.34171 11.0488 1.65829 10.8536 1.85355L8.20711 4.5H10C10.2761 4.5 10.5 4.72386 10.5 5C10.5 5.27614 10.2761 5.5 10 5.5H7C6.72386 5.5 6.5 5.27614 6.5 5V2C6.5 1.72386 6.72386 1.5 7 1.5C7.27614 1.5 7.5 1.72386 7.5 2V3.79289L10.1464 1.14645ZM1.5 7C1.5 6.72386 1.72386 6.5 2 6.5H5C5.27614 6.5 5.5 6.72386 5.5 7V10C5.5 10.2761 5.27614 10.5 5 10.5C4.72386 10.5 4.5 10.2761 4.5 10V8.20711L1.85355 10.8536C1.65829 11.0488 1.34171 11.0488 1.14645 10.8536C0.951184 10.6583 0.951184 10.3417 1.14645 10.1464L3.79289 7.5H2C1.72386 7.5 1.5 7.27614 1.5 7Z" fill="black"/>
</svg>`
Insert cell
makeRollDownIcon = () => htl.html`<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.20544 6.45505C5.36816 6.29233 5.63198 6.29233 5.7947 6.45505L8.00007 8.66042L10.2054 6.45505C10.3682 6.29233 10.632 6.29233 10.7947 6.45505C10.9574 6.61777 10.9574 6.88158 10.7947 7.0443L8.2947 9.5443C8.13198 9.70702 7.86816 9.70702 7.70544 9.5443L5.20544 7.0443C5.04272 6.88158 5.04272 6.61777 5.20544 6.45505Z" fill="#151472"/>
</svg>`
Insert cell
makeRollUpIcon = () => htl.html`<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.70541 6.45505C7.86813 6.29233 8.13195 6.29233 8.29467 6.45505L10.7947 8.95505C10.9574 9.11776 10.9574 9.38158 10.7947 9.5443C10.632 9.70702 10.3681 9.70702 10.2054 9.5443L8.00004 7.33893L5.79467 9.5443C5.63195 9.70702 5.36813 9.70702 5.20541 9.5443C5.04269 9.38158 5.04269 9.11776 5.20541 8.95505L7.70541 6.45505Z" fill="#151472"/>
</svg>
`
Insert cell
Insert cell
Insert cell
updateScatterConfig = {
let w = biglayoutToggle ? 496 : 286;
mutable scatterConfig.width = w;
mutable scatterConfig.height = w*3/4;
mutable scatterConfig = mutable scatterConfig;
}
Insert cell
scatterplotComponent = {
mutable reactDebug.push ("scatterPlot");
interfaceToggle;
return scatterplot;
}
Insert cell
scatterConfig
Insert cell
Insert cell
Insert cell
Insert cell
ttipScale = (value, colorScale) => {
const scale = d3.scaleThreshold(colorScale.thresholds, colorScale.range);
const color = scale(value);
const nthChild = (scale.range().indexOf(color) + 1).toString();
var table = html`
<p>${colorScale.label} <span class="value">${d3
.format(".3r")(value)
.replace(/\./g, ",")}</span></p>
<table>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>`;
d3.select(table)
.selectAll("td:nth-child(" + nthChild + ")")
.style("background", color);
return table.outerHTML;
}
Insert cell
tooltip = (d) => {
let num = d3.format(",");
return `
<div class="tooltip">
<h1>${d.municipio} <span>${d.uf}</span></h1>
<p>${num(d.populacao).replace(/,/g, ".")} habitantes</p>
${ttipScale(d.pol, variables[0])}
${ttipScale(d.bp, variables[1])}
</div>
`;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
malhaBrasil = FileAttachment("malhabrasil.json").json()
Insert cell
// Observe o uso de rewind para mudar as circulações dos polígonos para horária
feicoesMunicipio = {
let feicoes = rewind(
topojson.feature(malhaBrasil, malhaBrasil.objects.foo),
true
);
// Consertando alguns centroides que foram computados erroneamente
for (let d of feicoes.features) {
let c = d.properties.centroide;
if (c[0]==0 && c[1] == 0) {
let a = d.geometry.coordinates.flat(4)
c[0] = a[0];
c[1] = a[1];
}
}
return feicoes
}
Insert cell
Insert cell
Insert cell
Insert cell
// Usando só os 6 primeiros digitos para ser compativel com a tabela 'municipio' importada do menu de localidade
posicoesPorCod = new Map(posicoesFinais.map((p) => [p.properties.codarea.slice(0,6), p]))
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
valueAnalysis(obper)
Insert cell
valueAnalysis(obneo)
Insert cell
valueAnalysis(obinf)
Insert cell
valueAnalysis(bp)
Insert cell
valueAnalysis(AVGPM25)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
limites = funcaoPath.bounds(feicoesMunicipio)
Insert cell
Insert cell
Insert cell
Insert cell
legendaEscalaCores = {
interfaceToggle;
const leg = Legend(escalaCores, {
width: 280,
title: colorVar.label,
tickSize: 0
});
d3.select(leg).selectAll("text").style("font-family", "Roboto Condensed");
d3.select(leg).selectAll(".tick").selectAll("line").style("stroke", "white");
d3.select(leg).selectAll(".tick").selectAll("text").style("color", "#C4C4C4");
return leg;
}
Insert cell
Insert cell
raioMax = 24
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3tip = require("d3-tip")
Insert cell
rewind = require('https://bundle.run/@mapbox/geojson-rewind@0.5.0')
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
import { Legend } from "@d3/color-legend"
Insert cell
Insert cell
Insert cell
import {aDrawer, aDrawerStyles} from "@esperanc/adrawer"
Insert cell
import {aSlider, aSliderStyles} from "@esperanc/aslider"
Insert cell
import {aSelect, aText, aSelectStyles} from "@esperanc/aselect"
Insert cell
import {sliderbase, sliderArray, sliderRangeBase, style_slider } from "f493b3d6ad410bbc"
Insert cell
${aDrawerStyles}
${aSliderStyles}
${aSelectStyles}
${style_slider}
${rankStyles}
${styles}
${dorlingStyles}
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more