d3 = require("d3@7")
tabua = await FileAttachment("dados/base_dados/tabua_concatenada.csv").csv()
regioes = ["Brasil", "Centro-Oeste", "Nordeste", "Norte", "Sudeste", "Sul", "Acre", "Alagoas", "Amapá", "Amazonas", "Bahia", "Ceará", "Distrito Federal", "Espírito Santo", "Goiás", "Maranhão", "Mato Grosso", "Mato Grosso do Sul", "Minas Gerais", "Pará", "Paraíba", "Paraná", "Pernambuco", "Piauí", "Rio de Janeiro", "Rio Grande do Norte", "Rio Grande do Sul", "Rondônia", "Roraima", "Santa Catarina", "São Paulo", "Sergipe", "Tocantins"]
faixas_etaria = [...new Set(tabua.map(d => d.faixa_etaria))].sort();
expectativa = tabua.filter(d => d.faixa_etaria === faixa && d.Local === regiao && d.sexo ==='Ambos').map(d => ({
ano: +d.Ano,
ex: +d.ex,
faixa: d.faixa_etaria
})
)
viewof seletores = {
const container = htl.html`
<div style="
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: flex-start;
gap: 30px;
justify-content: center;
">
<div style="
min-width: 200px;
width: 100%;
max-width: 300px;
margin-bottom: 10px;
">
<div style="text-align: center;">Local:</div>
<div style="display: flex; justify-content: center; width: 100%;">
<select id="regiao-select" class="compact-select" style="
padding: 6px 12px;
border: 1px solid #ccc;
border-radius: 20px;
font-size: 16px;
text-align: center;
width: fit-content;
min-width: 180px;
background-color: #f8f9fa;
cursor: pointer;
appearance: none;
background-image: url('data:image/svg+xml;charset=UTF-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="%23666" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>');
background-repeat: no-repeat;
background-position: right 8px center;
background-size: 16px;
margin-top: 10px;">
${regioes.map(regiao =>
htl.html`<option value=${regiao}>${regiao}</option>`
)}
</select>
</div>
</div>
<div style="
min-width: 200px;
width: 100%;
max-width: 300px;
margin-bottom: 10px;
">
<div style="text-align: center;">Faixa etária:</div>
<div style="display: flex; justify-content: center; width: 100%;">
<select id="faixa-select" class="compact-select" style="
padding: 6px 12px;
border: 1px solid #ccc;
border-radius: 20px;
font-size: 16px;
text-align: center;
width: fit-content;
min-width: 150px;
background-color: #f8f9fa;
cursor: pointer;
appearance: none;
background-image: url('data:image/svg+xml;charset=UTF-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="%23666" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>');
background-repeat: no-repeat;
background-position: right 8px center;
background-size: 16px;
margin-top: 10px;">
${faixas_etaria.map(faixa =>
htl.html`<option value=${faixa}>${faixa}</option>`
)}
</select>
</div>
</div>
</div>
`;
// Adiciona código para detectar a largura da tela e ajustar o layout
const mediaQuery = window.matchMedia('(max-width: 650px)');
// Função para ajustar o layout baseado na largura da tela
const handleScreenChange = (e) => {
if (e.matches) {
// Tela pequena - coloca os elementos em coluna
container.style.flexDirection = 'column';
container.style.alignItems = 'center';
} else {
// Tela grande - mantém em linha
container.style.flexDirection = 'row';
container.style.alignItems = 'flex-start';
}
};
// Verifica inicialmente
handleScreenChange(mediaQuery);
// Adiciona o listener para mudanças de tamanho
mediaQuery.addEventListener('change', handleScreenChange);
// Captura os elementos select para manipulação
const regiaoSelect = container.querySelector('#regiao-select');
const faixaSelect = container.querySelector('#faixa-select');
// Define o valor do objeto retornado
container.value = {
regiao: regiaoSelect.value,
faixa: faixaSelect.value
};
// Atualiza os valores quando houver mudança
regiaoSelect.addEventListener('change', () => {
container.value.regiao = regiaoSelect.value;
container.dispatchEvent(new CustomEvent('input'));
});
faixaSelect.addEventListener('change', () => {
container.value.faixa = faixaSelect.value;
container.dispatchEvent(new CustomEvent('input'));
});
return container;
}
// Você pode acessar os valores selecionados assim:
regiao = seletores.regiao
faixa = seletores.faixa
Expectativa de vida para 2000-2023
{
const ptBRLocaleDefinition = {
decimal: ",",
thousands: "",
grouping: [3],
currency: ["R$", ""]
};
d3.formatDefaultLocale(ptBRLocaleDefinition);
// Declare the chart dimensions and margins.
const width = 760;
const height = 500;
const marginTop = 20;
const marginRight = 30;
const marginBottom = 30;
const marginLeft = 40;
// Declare the x (horizontal position) scale.
const x = d3.scaleLinear(d3.extent(expectativa, d => d.ano), [marginLeft, width - marginRight]);
// Declare the y (vertical position) scale.
const y = d3.scaleLinear([d3.min(expectativa, d => d.ex)-1, d3.max(expectativa, d => d.ex)+1], [height - marginBottom, marginTop]);
// Declare the line generator.
const line = d3.line()
.x(d => x(d.ano))
.y(d => y(d.ex));
// Create the SVG container.
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("width", width)
.attr("height", height)
.attr("style", "max-width: 100%; height: auto; height: intrinsic; font: 10px sans-serif;")
.style("-webkit-tap-highlight-color", "transparent")
.style("overflow", "visible")
.on("pointerenter pointermove", pointermoved)
.on("pointerleave", pointerleft)
.on("touchstart", event => event.preventDefault());
// Add the x-axis.
svg.append("g")
.attr("transform", `translate(0,${height - marginBottom})`)
.call(d3.axisBottom(x).ticks(width / 80).tickSizeOuter(0));
svg.append("text")
.attr("text-anchor", "middle")
.attr("x", width / 2)
.attr("y", height) // Posição abaixo do eixo
.style("font-size", "14px")
.style("fill", "currentColor")
.text("Ano");
// Add the y-axis, remove the domain line, add grid lines and a label.
svg.append("g")
.attr("transform", `translate(${marginLeft},0)`)
.call(d3.axisLeft(y).ticks(height / 40))
.call(g => g.select(".domain").remove())
.style("font-size", "12px")
.call(g => g.selectAll(".tick line").clone()
.attr("x2", width - marginLeft - marginRight)
.attr("stroke-opacity", 0.1))
.call(g => g.append("text")
.attr("x", -marginLeft)
.attr("y", 10)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.style("font-size", "18px")
.text("ex"));
// Append a path for the line.
svg.append("path")
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("d", line(expectativa));
// Create the tooltip container.
const tooltip = svg.append("g");
function formatValue(value) {
return value.toLocaleString("en", {
style: "decimal", // Altera para estilo numérico
minimumFractionDigits: 2, // Mantém 2 casas decimais
maximumFractionDigits: 2
});
}
function formatDate(date) {
return date.toLocaleString("pt-BR", {
month: "short",
day: "numeric",
year: "numeric",
timeZone: "UTC"
});
}
// Add the event listeners that show or hide the tooltip.
const bisect = d3.bisector(d => d.ano).center;
function pointermoved(event) {
const i = bisect(expectativa, x.invert(d3.pointer(event)[0]));
tooltip.style("display", null);
tooltip.attr("transform", `translate(${x(expectativa[i].ano)},${y(expectativa[i].ex)})`);
const path = tooltip.selectAll("path")
.data([,])
.join("path")
.attr("fill", "white")
.attr("stroke", "black");
const text = tooltip.selectAll("text")
.data([,])
.join("text")
.call(text => text
.selectAll("tspan")
.data([formatDate(expectativa[i].ano), formatValue(expectativa[i].ex)])
.join("tspan")
.attr("x", 0)
.attr("y", (_, i) => `${i * 1.1}em`)
.attr("font-weight", (_, i) => i ? null : "bold")
.text(d => d));
size(text, path);
}
function pointerleft() {
tooltip.style("display", "none");
}
// Wraps the text with a callout path of the correct size, as measured in the page.
function size(text, path) {
const {x, y, width: w, height: h} = text.node().getBBox();
text.attr("transform", `translate(${-w / 2},${15 - y})`);
path.attr("d", `M${-w / 2 - 10},5H-5l5,-5l5,5H${w / 2 + 10}v${h + 20}h-${w + 20}z`);
}
return svg.node();
}
html`<p>Para acessar a metodologia, <a href="metodologia.html#proje%C3%A7%C3%B5es-de-mortalidade-do-ibge">clique aqui</a>.</p>`