defaults = await FileAttachment("../data/previsao_pop_ben_semob_defaults.json").json()
default_inputs = defaults.default_inputs
lineares = defaults.lineares
frota = defaults.frota
lineares_by_key = Object.fromEntries(lineares.map((item) => [item.key, item]))
frota_by_key = Object.fromEntries(frota.map((item) => [item.key, item]))
fc_options = [
"Baixo — FC = 0,85",
"Médio — FC = 1,00",
"Alto — FC = 1,25"
]
fc_lookup = new Map([
["Baixo — FC = 0,85", 0.85],
["Médio — FC = 1,00", 1.0],
["Alto — FC = 1,25", 1.25]
])
fmt = (n, dec = 0) =>
n.toLocaleString("pt-BR", {
minimumFractionDigits: dec,
maximumFractionDigits: dec
})
fmt1 = (n) => fmt(n, 1)
fmt2 = (n) => fmt(n, 2)
sumValues = (values) => values.reduce((acc, value) => acc + value, 0)
safePct = (num, den) => den > 0 ? (num / den) * 100 : 0
safeRatio = (num, den) => den > 0 ? (num / den) : 0Calculadora de População Beneficiada — Mobilidade Urbana (FGTS)
Estimativa interativa da população potencialmente beneficiada por investimentos FGTS em mobilidade urbana
Autor
CGDI/DEREG/SEMOB
Data de Publicação
abril de 2026
O que esta calculadora faz
htl.html`
<div style="background:#EAF3FF;border-left:5px solid #2C5AA0;padding:1.2em 1.5em;
border-radius:6px;font-size:0.95em;line-height:1.7;margin-bottom:1.5em;">
Esta calculadora estima a <strong>população potencialmente beneficiada</strong> por
investimentos do FGTS em mobilidade urbana. Ela aplica a nova metodologia da
<a href="novo_calculo_fgts_semob.html">Nota Metodológica CGDI/SEMOB</a>, que substitui
a fórmula anterior <code>(INV / 2.500) × 2,96</code> por um cálculo que considera a
<strong>extensão física</strong> das obras, o <strong>tipo de intervenção</strong>
e a <strong>densidade populacional</strong> do território atendido.
</div>
`Dois cenários de uso
htl.html`
<div class="cenario-grid">
<div class="cenario-card cenario-card--agregado">
<div class="cenario-card__header">A — Orçamento previsto</div>
<div class="cenario-card__body">
<p><strong>Quando usar:</strong> você tem apenas o valor total do orçamento
(item 18) e não conhece a composição da carteira por contrato.</p>
<p>O orçamento é distribuído por <strong>pesos históricos</strong> e a
extensão é estimada pela <strong>regra subsidiária</strong>
(KM = INV / C<sub>med</sub>).</p>
</div>
</div>
<div class="cenario-card cenario-card--definida">
<div class="cenario-card__header">B — Carteira definida</div>
<div class="cenario-card__body">
<p><strong>Quando usar:</strong> você conhece as obras selecionadas ou
contratadas, com tipologia e extensão (km) de cada uma.</p>
<p>Informe diretamente a <strong>extensão por tipologia</strong> e a
<strong>quantidade de veículos</strong>. A fórmula é aplicada sem
pesos nem regra subsidiária.</p>
</div>
</div>
</div>
`Parâmetros compartilhados
Os parâmetros abaixo são usados em ambas as abas. Edite diretamente nas tabelas.
Densidade e fator de complexidade
viewof dens_ref = {
const inp = Inputs.number({value: default_inputs.dens_ref, min: 1, step: 0.01});
const el = htl.html`<div class="ojs-input-row">
<div class="ojs-label">DM_ref — Densidade de referência (hab/km²)</div>
${inp}
</div>`;
Object.defineProperty(el, 'value', {get: () => inp.value});
inp.addEventListener("input", () => el.dispatchEvent(new Event("input")));
return el;
}viewof fc_sens_enabled = {
const inp = Inputs.toggle({value: false});
const el = htl.html`<div class="ojs-input-row">
<div class="ojs-label ojs-label--wide">Ativar modo de sensibilidade para FC nas obras lineares</div>
${inp}
</div>`;
Object.defineProperty(el, 'value', {get: () => inp.value});
inp.addEventListener("input", () => el.dispatchEvent(new Event("input")));
return el;
}viewof fc_sel = {
const inp = Inputs.select(fc_options, {value: default_inputs.fc_label});
const el = htl.html`<div class="ojs-input-row">
<div class="ojs-label">FC — Cenário de sensibilidade</div>
${inp}
</div>`;
Object.defineProperty(el, 'value', {get: () => inp.value});
inp.addEventListener("input", () => el.dispatchEvent(new Event("input")));
return el;
}htl.html`
<div style="background:${fc_sens_enabled ? "#FFF3CD" : "#EAF7EA"};
border-left:5px solid ${fc_sens_enabled ? "#B85C00" : "#2E7D32"};
padding:0.9em 1.2em;border-radius:6px;font-size:0.9em;">
<strong>Modo ativo:</strong> ${fc_mode_label}.<br>
${fc_sens_enabled
? htl.html`O cálculo das obras lineares está usando <strong>FC = ${fmt2(fc_val)}</strong> para simulação de cenário.`
: htl.html`O cálculo das obras lineares está usando <strong>FC = 1,00</strong>, conforme a regra subsidiária da Seção 18.`}
</div>
`Parâmetros técnicos — obras lineares
viewof params_lin = {
const state = {};
lineares.forEach(d => {
state[`${d.key}_fi`] = d.fi;
state[`${d.key}_ft`] = d.ft;
state[`${d.key}_cmed`] = d.cmed;
});
const inputStyle = "width:90px;text-align:center;border:1px solid #ccd5e0;border-radius:4px;padding:0.3em 0.4em;font-size:0.88em;";
const el = htl.html`<div class="calc-table-wrap">
<table class="calc-table calc-table--meta">
<colgroup>
<col style="width:35%">
<col style="width:20%">
<col style="width:20%">
<col style="width:25%">
</colgroup>
<thead>
<tr>
<th class="calc-table__head calc-table__head--left">Tipologia</th>
<th class="calc-table__head calc-table__head--center">FI<sub>i</sub> (km)</th>
<th class="calc-table__head calc-table__head--center">FT<sub>i</sub> (adim.)</th>
<th class="calc-table__head calc-table__head--center">C<sub>med,i</sub> (R$ mi/km) <span style="font-weight:normal;font-size:0.85em;color:#666;">*cenário A</span></th>
</tr>
</thead>
<tbody>
${lineares.map((item, i) => htl.html`<tr class="${i % 2 === 0 ? 'calc-table__row calc-table__row--alt' : 'calc-table__row'}">
<td class="calc-table__cell calc-table__cell--left">${item.tipologia}</td>
<td class="calc-table__cell calc-table__cell--center">
<input type="number" value="${item.fi}" min="0" step="0.1" style="${inputStyle}" data-key="${item.key}_fi">
</td>
<td class="calc-table__cell calc-table__cell--center">
<input type="number" value="${item.ft}" min="0" step="0.01" style="${inputStyle}" data-key="${item.key}_ft">
</td>
<td class="calc-table__cell calc-table__cell--center">
<input type="number" value="${item.cmed}" min="0.01" step="0.1" style="${inputStyle}" data-key="${item.key}_cmed">
</td>
</tr>`)}
</tbody>
</table>
</div>
<p style="font-size:0.8em;color:#666;margin-top:0.4em;">Edite diretamente na tabela. C<sub>med</sub> é usado apenas no cenário A (regra subsidiária).</p>`;
el.value = {...state};
el.querySelectorAll("input").forEach(inp => {
inp.addEventListener("input", () => {
state[inp.dataset.key] = +inp.value;
el.value = {...state};
el.dispatchEvent(new Event("input", {bubbles: true}));
});
});
return el;
}Parâmetros técnicos — aquisição de frota
viewof params_fro = {
const state = {};
frota.forEach(d => {
state[`${d.key}_cunit`] = d.cunit;
state[`${d.key}_bunit`] = d.bunit;
});
const inputStyle = "width:90px;text-align:center;border:1px solid #ccd5e0;border-radius:4px;padding:0.3em 0.4em;font-size:0.88em;";
const el = htl.html`<div class="calc-table-wrap">
<table class="calc-table calc-table--meta">
<colgroup>
<col style="width:38%">
<col style="width:18%">
<col style="width:22%">
<col style="width:22%">
</colgroup>
<thead>
<tr>
<th class="calc-table__head calc-table__head--left">Tipo de veículo</th>
<th class="calc-table__head calc-table__head--center">C<sub>unit,j</sub> (R$ mi/un.) <span style="font-weight:normal;font-size:0.85em;color:#666;">*cenário A</span></th>
<th class="calc-table__head calc-table__head--center">Intervalo de referência (R$ mi)</th>
<th class="calc-table__head calc-table__head--center">B<sub>unit,j</sub> (pessoas/un.)</th>
</tr>
</thead>
<tbody>
${frota.map((item, i) => htl.html`<tr class="${i % 2 === 0 ? 'calc-table__row calc-table__row--alt' : 'calc-table__row'}">
<td class="calc-table__cell calc-table__cell--left">${item.tipo}</td>
<td class="calc-table__cell calc-table__cell--center">
<input type="number" value="${item.cunit}" min="0.01" step="0.1" style="${inputStyle}" data-key="${item.key}_cunit">
</td>
<td class="calc-table__cell calc-table__cell--center" style="font-size:0.82em;color:#666;">
${item.cunit_min != null ? `${item.cunit_min.toFixed(2)} – ${item.cunit_max.toFixed(2)}` : "—"}
</td>
<td class="calc-table__cell calc-table__cell--center">
<input type="number" value="${item.bunit}" min="1" step="10" style="${inputStyle}" data-key="${item.key}_bunit">
</td>
</tr>`)}
</tbody>
</table>
</div>
<p style="font-size:0.8em;color:#666;margin-top:0.4em;">C<sub>unit</sub> é usado apenas no cenário A. B<sub>unit</sub> é usado em ambas. O intervalo de referência (mín–máx) é proveniente da <em>Nota Indicadores FGTS — Aquisição Frota</em> (CGDI/SEMOB, 2026).</p>`;
el.value = {...state};
el.querySelectorAll("input").forEach(inp => {
inp.addEventListener("input", () => {
state[inp.dataset.key] = +inp.value;
el.value = {...state};
el.dispatchEvent(new Event("input", {bubbles: true}));
});
});
return el;
}Cenários
Entradas — cenário A
Orçamento total previsto
viewof inv_orc_bi = {
const inp = Inputs.number({value: default_inputs.inv_orc_bi, min: 0.01, step: 0.1});
const el = htl.html`<div class="ojs-input-row">
<div class="ojs-label">INV_orç — Orçamento total previsto (R$ bilhões)</div>
${inp}
</div>`;
Object.defineProperty(el, 'value', {get: () => inp.value});
inp.addEventListener("input", () => el.dispatchEvent(new Event("input")));
return el;
}Divisão do orçamento (α e β)
O parâmetro \(\alpha\) representa a fração histórica de obras lineares; \(\beta = 1 - \alpha\) é a fração histórica de aquisição de frota.
viewof alpha_pct = {
const inp = Inputs.range([0, 100], {value: default_inputs.alpha_pct, step: 0.1});
const el = htl.html`<div class="ojs-input-row">
<div class="ojs-label">α — Participação de obras lineares (%)</div>
${inp}
</div>`;
Object.defineProperty(el, 'value', {get: () => inp.value});
inp.addEventListener("input", () => el.dispatchEvent(new Event("input")));
return el;
}Pesos históricos por tipologia — obras lineares
Os pesos \(w_i\) representam a participação percentual histórica de cada tipologia no valor total de obras lineares contratadas.
viewof w_lin = {
const controls = Object.fromEntries(
lineares.map((item) => [
item.key,
Inputs.range([0, 100], {value: item.default_weight_pct, step: 1})
])
);
const watcher = Inputs.form(controls);
const el = htl.html`<div>
${lineares.map((item) => htl.html`<div class="ojs-input-row">
<div class="ojs-label">${item.tipologia} — w_${item.key} (%)</div>
<div style="flex:1;">${controls[item.key]}</div>
</div>`)}
</div>`;
Object.defineProperty(el, 'value', { get: () => watcher.value });
watcher.addEventListener("input", () => el.dispatchEvent(new Event("input")));
return el;
}w_lin_input = ({
...w_lin
})
w_lin_sum = sumValues(Object.values(w_lin_input))
w_lin_norm = Object.fromEntries(
Object.entries(w_lin_input).map(([key, value]) => [
key,
w_lin_sum > 0 ? value / w_lin_sum : 1 / lineares.length
])
)
htl.html`<p style="font-size:0.88em;
${w_lin_sum === 100 ? "color:#2e7d32" : "color:#c00"};
margin-top:-0.3em;">
Σ w<sub>i</sub> = <strong>${w_lin_sum}%</strong>
${w_lin_sum === 100 ? " ✓" : " — a soma deve ser exatamente 100% para calcular"}
</p>`Pesos históricos por tipo de veículo — aquisição de frota
viewof w_fro = {
const controls = Object.fromEntries(
frota.map((item) => [
item.key,
Inputs.range([0, 100], {value: item.default_weight_pct, step: 1})
])
);
const watcher = Inputs.form(controls);
const el = htl.html`<div>
${frota.map((item) => htl.html`<div class="ojs-input-row">
<div class="ojs-label">${item.tipo} — w_${item.key} (%)</div>
<div style="flex:1;">${controls[item.key]}</div>
</div>`)}
</div>`;
Object.defineProperty(el, 'value', { get: () => watcher.value });
watcher.addEventListener("input", () => el.dispatchEvent(new Event("input")));
return el;
}w_fro_input = Object.fromEntries(frota.map(item => [item.key, w_fro[item.key]]))
w_fro_sum = sumValues(Object.values(w_fro_input))
w_fro_norm = Object.fromEntries(
Object.entries(w_fro_input).map(([key, value]) => [
key,
w_fro_sum > 0 ? value / w_fro_sum : 1 / frota.length
])
)
htl.html`<p style="font-size:0.88em;
${w_fro_sum === 100 ? "color:#2e7d32" : "color:#c00"};
margin-top:-0.3em;">
Σ w<sub>j</sub> = <strong>${w_fro_sum}%</strong>
${w_fro_sum === 100 ? " ✓" : " — a soma deve ser exatamente 100% para calcular"}
</p>`Metodologia — cenário A
O cálculo segue três etapas: divisão do orçamento entre obras lineares e frota, estimativa por tipologia usando a regra subsidiária (\(KM_i = INV_i / C_{\text{med},i}\)), e agregação final.
Obras lineares — para cada tipologia \(i\):
\[ KM_i = \frac{w_i \times INV_{\text{lineares}}}{C_{\text{med},i}} \qquad \Rightarrow \qquad PB_{\text{lin},i} = KM_i \times FI_i \times DM_{\text{ref}} \times FT_i \times FC \]
Aquisição de frota — para cada tipo de veículo \(j\):
\[ PB_{\text{frota},j} = \frac{w_j \times INV_{\text{frota}}}{C_{\text{unit},j}} \times B_{\text{unit},j} \]
Agregação: \(PB_{orç} = \sum_i PB_{\text{lin},i} + \sum_j PB_{\text{frota},j}\)
Cálculo passo a passo — cenário A
inv_lineares_bi = (alpha_pct / 100) * inv_orc_bi
inv_frota_bi = inv_orc_bi - inv_lineares_bi
pb_lin = lineares_eff.map((item) => {
const peso = w_lin_norm[item.key]
const inv_i_mi = peso * inv_lineares_bi * 1000
const km_i = inv_i_mi / item.cmed
const area_i = km_i * item.fi
const pop_area_i = area_i * dens_ref
const pb_i_raw = pop_area_i * item.ft * fc_val
const pb_i = Math.round(pb_i_raw)
return {
...item,
w_i: peso,
inv_i_mi,
km_i,
area_i,
pop_area_i,
pb_i
}
})
pb_lin_total = sumValues(pb_lin.map((row) => row.pb_i))
pb_fro = frota_eff.map((item) => {
const peso = w_fro_norm[item.key]
const inv_j_mi = peso * inv_frota_bi * 1000
const n_j = Math.round(inv_j_mi / item.cunit)
const pb_j = n_j * item.bunit
return {
...item,
w_j: peso,
inv_j_mi,
n_j,
pb_j
}
})
pb_fro_total = sumValues(pb_fro.map((row) => row.pb_j))
pb_total = pb_lin_total + pb_fro_total
pb_antigo = Math.round((inv_orc_bi * 1e9 / 2500) * 2.96)
pb_fro_share = safePct(pb_fro_total, pb_total)
pb_lin_share = safePct(pb_lin_total, pb_total)
comparativo_ratio = safeRatio(pb_antigo, pb_total)Etapa 1 — Divisão do orçamento
htl.html`
<div style="background:#EAF3FF;border-left:5px solid #2C5AA0;padding:1em 1.5em;
border-radius:5px;font-size:0.92em;line-height:2;">
<strong>Etapa 1 · Substituição numérica</strong><br>
<code>INV<sub>lineares</sub> = α × INV<sub>orç</sub></code><br>
<code> = ${fmt1(alpha_pct)}% × R$ ${fmt2(inv_orc_bi)} bi</code><br>
<code> = <strong>R$ ${fmt2(inv_lineares_bi)} bi</strong></code><br><br>
<code>INV<sub>frota</sub> = β × INV<sub>orç</sub></code><br>
<code> = ${fmt1(100 - alpha_pct)}% × R$ ${fmt2(inv_orc_bi)} bi</code><br>
<code> = <strong>R$ ${fmt2(inv_frota_bi)} bi</strong></code><br>
<span style="color:#555;font-size:0.9em;">
Verificação: R$ ${fmt2(inv_lineares_bi)} bi + R$ ${fmt2(inv_frota_bi)} bi
= R$ ${fmt2(inv_lineares_bi + inv_frota_bi)} bi
</span>
</div>
`Etapa 2a — Obras lineares
htl.html`
<div class="calc-table-wrap">
<table class="calc-table calc-table--meta">
<colgroup>
<col style="width:30%">
<col style="width:15%">
<col style="width:20%">
<col style="width:15%">
<col style="width:20%">
</colgroup>
<thead>
<tr>
<th class="calc-table__head calc-table__head--left">Tipologia</th>
<th class="calc-table__head calc-table__head--center">w<sub>i</sub> (%)</th>
<th class="calc-table__head calc-table__head--right">INV<sub>i</sub> (R$ mi)</th>
<th class="calc-table__head calc-table__head--right">C<sub>med,i</sub></th>
<th class="calc-table__head calc-table__head--right">KM<sub>i</sub> (km)</th>
</tr>
</thead>
<tbody>
${pb_lin.map((row, i) => htl.html`<tr class="${i % 2 === 0 ? 'calc-table__row calc-table__row--alt' : 'calc-table__row'}">
<td class="calc-table__cell calc-table__cell--left">${row.tipologia}</td>
<td class="calc-table__cell calc-table__cell--center">${fmt1(w_lin_input[row.key])}%</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(row.inv_i_mi)}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt2(row.cmed)}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt1(row.km_i)}</td>
</tr>`)}
${htl.html`<tr class="calc-table__row calc-table__row--total">
<td class="calc-table__cell calc-table__cell--left">Total</td>
<td class="calc-table__cell calc-table__cell--center">${w_lin_sum}%</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(inv_lineares_bi * 1000)}</td>
<td class="calc-table__cell calc-table__cell--right">—</td>
<td class="calc-table__cell calc-table__cell--right">—</td>
</tr>`}
</tbody>
</table>
</div>
`htl.html`
<div class="calc-table-wrap">
<table class="calc-table calc-table--meta">
<colgroup>
<col style="width:30%">
<col style="width:15%">
<col style="width:20%">
<col style="width:15%">
<col style="width:20%">
</colgroup>
<thead>
<tr>
<th class="calc-table__head calc-table__head--left">Tipologia</th>
<th class="calc-table__head calc-table__head--right">FI<sub>i</sub></th>
<th class="calc-table__head calc-table__head--right">Área (km²)</th>
<th class="calc-table__head calc-table__head--right">FT<sub>i</sub></th>
<th class="calc-table__head calc-table__head--right">PB<sub>i</sub></th>
</tr>
</thead>
<tbody>
${pb_lin.map((row, i) => htl.html`<tr class="${i % 2 === 0 ? 'calc-table__row calc-table__row--alt' : 'calc-table__row'}">
<td class="calc-table__cell calc-table__cell--left">${row.tipologia}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt2(row.fi)}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt1(row.area_i)}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt2(row.ft)}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(row.pb_i)}</td>
</tr>`)}
${htl.html`<tr class="calc-table__row calc-table__row--total">
<td class="calc-table__cell calc-table__cell--left">Total</td>
<td class="calc-table__cell calc-table__cell--right">—</td>
<td class="calc-table__cell calc-table__cell--right">—</td>
<td class="calc-table__cell calc-table__cell--right">—</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(pb_lin_total)}</td>
</tr>`}
</tbody>
</table>
</div>
`Exemplo de cálculo detalhado — BRT:
{
const brt = pb_lin.find((row) => row.key === "brt")
return htl.html`
<div style="background:#F0F7FF;border:1px solid #AFCDF2;border-radius:6px;
padding:1em 1.5em;font-size:0.88em;line-height:2.1;">
<strong>BRT — substituição numérica passo a passo</strong><br>
<span style="color:#555;">1. Investimento alocado para BRT:</span><br>
<code>INV<sub>BRT</sub> = w<sub>BRT</sub> × INV<sub>lineares</sub>
= ${fmt1(brt.w_i * 100)}% × R$ ${fmt(inv_lineares_bi * 1000)} mi
= <strong>R$ ${fmt(brt.inv_i_mi)} mi</strong></code><br>
<span style="color:#555;">2. Extensão estimada:</span><br>
<code>KM<sub>BRT</sub> = INV<sub>BRT</sub> ÷ C<sub>med,BRT</sub>
= ${fmt(brt.inv_i_mi)} ÷ ${fmt2(brt.cmed)}
= <strong>${fmt1(brt.km_i)} km</strong></code><br>
<span style="color:#555;">3. Área de influência:</span><br>
<code>Área<sub>BRT</sub> = KM<sub>BRT</sub> × FI<sub>BRT</sub>
= ${fmt1(brt.km_i)} × ${fmt2(brt.fi)}
= <strong>${fmt1(brt.area_i)} km²</strong></code><br>
<span style="color:#555;">4. População na área de influência:</span><br>
<code>Pop<sub>BRT</sub> = Área<sub>BRT</sub> × DM<sub>ref</sub>
= ${fmt1(brt.area_i)} × ${fmt(dens_ref)}
= <strong>${fmt(Math.round(brt.pop_area_i))} hab</strong></code><br>
<span style="color:#555;">5. Aplicação dos fatores:</span><br>
<code>PB<sub>BRT</sub> = Pop<sub>BRT</sub> × FT<sub>BRT</sub> × FC
= ${fmt(Math.round(brt.pop_area_i))} × ${fmt2(brt.ft)} × ${fmt2(fc_val)}
= <strong>${fmt(brt.pb_i)} pessoas</strong></code>
</div>
`
}Etapa 2b — Aquisição de frota
htl.html`
<div class="calc-table-wrap">
<table class="calc-table calc-table--meta">
<colgroup>
<col style="width:30%">
<col style="width:15%">
<col style="width:20%">
<col style="width:15%">
<col style="width:20%">
</colgroup>
<thead>
<tr>
<th class="calc-table__head calc-table__head--left">Tipo de veículo</th>
<th class="calc-table__head calc-table__head--center">w<sub>j</sub> (%)</th>
<th class="calc-table__head calc-table__head--right">INV<sub>j</sub> (R$ mi)</th>
<th class="calc-table__head calc-table__head--right">C<sub>unit,j</sub></th>
<th class="calc-table__head calc-table__head--right">N<sub>j</sub> (un.)</th>
</tr>
</thead>
<tbody>
${pb_fro.map((row, i) => htl.html`<tr class="${i % 2 === 0 ? 'calc-table__row calc-table__row--alt' : 'calc-table__row'}">
<td class="calc-table__cell calc-table__cell--left">${row.tipo}</td>
<td class="calc-table__cell calc-table__cell--center">${fmt1(w_fro_input[row.key])}%</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(row.inv_j_mi)}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt2(row.cunit)}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(row.n_j)}</td>
</tr>`)}
${htl.html`<tr class="calc-table__row calc-table__row--total">
<td class="calc-table__cell calc-table__cell--left">Total</td>
<td class="calc-table__cell calc-table__cell--center">${w_fro_sum}%</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(inv_frota_bi * 1000)}</td>
<td class="calc-table__cell calc-table__cell--right">—</td>
<td class="calc-table__cell calc-table__cell--right">—</td>
</tr>`}
</tbody>
</table>
</div>
`htl.html`
<div class="calc-table-wrap">
<table class="calc-table calc-table--meta">
<colgroup>
<col style="width:50%">
<col style="width:25%">
<col style="width:25%">
</colgroup>
<thead>
<tr>
<th class="calc-table__head calc-table__head--left">Tipo de veículo</th>
<th class="calc-table__head calc-table__head--right">B<sub>unit,j</sub></th>
<th class="calc-table__head calc-table__head--right">PB<sub>j</sub></th>
</tr>
</thead>
<tbody>
${pb_fro.map((row, i) => htl.html`<tr class="${i % 2 === 0 ? 'calc-table__row calc-table__row--alt' : 'calc-table__row'}">
<td class="calc-table__cell calc-table__cell--left">${row.tipo}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(row.bunit)}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(row.pb_j)}</td>
</tr>`)}
${htl.html`<tr class="calc-table__row calc-table__row--total">
<td class="calc-table__cell calc-table__cell--left">Total</td>
<td class="calc-table__cell calc-table__cell--right">—</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(pb_fro_total)}</td>
</tr>`}
</tbody>
</table>
</div>
`Etapa 3 — Agregação
htl.html`
<div style="background:#EAF3FF;border-left:5px solid #2C5AA0;padding:1em 1.5em;
border-radius:5px;font-size:0.92em;line-height:2.2;">
<strong>Etapa 3 · Substituição numérica</strong><br>
<code>PB<sub>orç</sub> = PB<sub>lineares</sub> + PB<sub>frota</sub></code><br>
<code style="margin-left:4em;">= ${fmt(pb_lin_total)} + ${fmt(pb_fro_total)}</code><br>
<code style="margin-left:4em;">= <strong>${fmt(pb_total)} pessoas</strong></code><br>
<span style="font-size:0.88em;color:#555;">
Participação da frota: ${fmt1(pb_fro_share)}% ·
Participação das obras lineares: ${fmt1(pb_lin_share)}%
</span>
</div>
`Resultado — cenário A
pesos_validos
? htl.html`
<div style="background:#EAF7EA;border-left:6px solid #2E7D32;padding:1.3em 1.8em;
border-radius:6px;margin:1em 0;">
<div style="font-size:0.78em;color:#2E7D32;font-weight:700;text-transform:uppercase;
letter-spacing:0.07em;margin-bottom:0.25em;">
População potencialmente beneficiada — orçamento previsto
</div>
<div style="font-size:2.5em;font-weight:700;color:#1a4f1a;line-height:1.1;">
${fmt(pb_total)} pessoas
</div>
<div style="display:flex;gap:2em;font-size:0.85em;color:#444;margin-top:0.6em;flex-wrap:wrap;">
<span>Obras lineares: <strong>${fmt(pb_lin_total)}</strong> (${fmt1(pb_lin_share)}%)</span>
<span>Frota: <strong>${fmt(pb_fro_total)}</strong> (${fmt1(pb_fro_share)}%)</span>
</div>
<hr style="border:none;border-top:1px solid #b9e3b2;margin:0.8em 0;">
<div style="font-size:0.85em;color:#777;">
Fórmula atual, para comparação:
<strong style="color:#5a3e00;">${fmt(pb_antigo)} pessoas</strong>
<span style="margin-left:0.6em;background:#FFF3CD;padding:2px 8px;border-radius:4px;">
${fmt1(comparativo_ratio)}× a nova metodologia
</span>
</div>
</div>`
: htl.html`
<div style="background:#FEE;border-left:6px solid #c00;padding:1.3em 1.8em;
border-radius:6px;margin:1em 0;">
<strong style="color:#c00;">Resultado indisponível</strong><br>
<span style="font-size:0.9em;color:#555;">
A soma dos pesos de obras lineares ou de frota não é exatamente 100%.
Corrija os valores acima para calcular.
</span>
</div>`Comparativo metodológico
pesos_validos ? htl.html`
<div class="calc-table-wrap">
<table class="calc-table calc-table--compare">
<colgroup>
<col style="width:28%">
<col style="width:38%">
<col style="width:14%">
<col style="width:20%">
</colgroup>
<thead>
<tr class="calc-table__head-row">
<th class="calc-table__head calc-table__head--left">Componente / Metodologia</th>
<th class="calc-table__head calc-table__head--right">Fórmula</th>
<th class="calc-table__head calc-table__head--right">Investimento (R$ bi)</th>
<th class="calc-table__head calc-table__head--right">PB estimado</th>
</tr>
</thead>
<tbody>
<tr class="calc-table__row calc-table__row--alt">
<td class="calc-table__cell calc-table__cell--left">Obras lineares</td>
<td class="calc-table__cell calc-table__cell--right calc-table__cell--formula">
Σ (w_i × INV_lin / C_med,i) × FI_i × DM × FT_i × FC
</td>
<td class="calc-table__cell calc-table__cell--right">${fmt2(inv_lineares_bi)}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(pb_lin_total)}</td>
</tr>
<tr class="calc-table__row">
<td class="calc-table__cell calc-table__cell--left">Aquisição de frota</td>
<td class="calc-table__cell calc-table__cell--right calc-table__cell--formula">
Σ (w_j × INV_fro / C_unit,j) × B_unit,j
</td>
<td class="calc-table__cell calc-table__cell--right">${fmt2(inv_frota_bi)}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(pb_fro_total)}</td>
</tr>
<tr class="calc-table__row calc-table__row--success">
<td class="calc-table__cell calc-table__cell--left">Total — nova metodologia</td>
<td class="calc-table__cell calc-table__cell--right calc-table__cell--formula calc-table__cell--success-text">
PB_lin + PB_fro
</td>
<td class="calc-table__cell calc-table__cell--right">${fmt2(inv_orc_bi)}</td>
<td class="calc-table__cell calc-table__cell--right calc-table__cell--success-text"><strong>${fmt(pb_total)}</strong></td>
</tr>
<tr class="calc-table__row calc-table__row--warning">
<td class="calc-table__cell calc-table__cell--left">Fórmula atual</td>
<td class="calc-table__cell calc-table__cell--right calc-table__cell--formula calc-table__cell--warning-text">
(INV / 2.500) × 2,96
</td>
<td class="calc-table__cell calc-table__cell--right">${fmt2(inv_orc_bi)}</td>
<td class="calc-table__cell calc-table__cell--right calc-table__cell--warning-text">${fmt(pb_antigo)}</td>
</tr>
</tbody>
</table>
</div>
<p style="font-size:0.82em;color:#666;margin-top:0.5em;">
A fórmula atual produz uma estimativa <strong>${fmt1(comparativo_ratio)}×</strong> maior
que a aplicação operacional da nova metodologia para o mesmo orçamento.
</p>
` : htl.html`<p style="color:#c00;font-size:0.9em;">⚠ Corrija os pesos acima para 100% — o comparativo será exibido automaticamente.</p>`Exportar relatório — Cenário A
{
const data_hoje = new Date().toLocaleDateString("pt-BR", {
day: "2-digit", month: "long", year: "numeric"
});
function buildReport({forWord = false} = {}) {
const linhas_params = lineares_eff.map((item, i) =>
`<tr class="${i % 2 === 0 ? "alt" : ""}">
<td>${item.tipologia}</td>
<td class="r">${fmt2(item.fi)}</td>
<td class="r">${fmt2(item.ft)}</td>
<td class="r">${fmt2(item.cmed)}</td>
<td class="r">${fmt1(w_lin_input[item.key])}%</td>
</tr>`
).join("");
const linhas_fro_params = frota_eff.map((item, i) =>
`<tr class="${i % 2 === 0 ? "alt" : ""}">
<td>${item.tipo}</td>
<td class="r">${fmt2(item.cunit)}</td>
<td class="r">${fmt(item.bunit)}</td>
<td class="r">${fmt1(w_fro_input[item.key])}%</td>
</tr>`
).join("");
const linhas_lin = pb_lin.map((row, i) =>
`<tr class="${i % 2 === 0 ? "alt" : ""}">
<td>${row.tipologia}</td>
<td class="r">${fmt1(row.w_i * 100)}%</td>
<td class="r">${fmt(row.inv_i_mi)}</td>
<td class="r">${fmt1(row.km_i)}</td>
<td class="r">${fmt1(row.area_i)}</td>
<td class="r">${fmt2(row.ft)}</td>
<td class="r"><strong>${fmt(row.pb_i)}</strong></td>
</tr>`
).join("");
const linhas_fro = pb_fro.map((row, i) =>
`<tr class="${i % 2 === 0 ? "alt" : ""}">
<td>${row.tipo}</td>
<td class="r">${fmt1(row.w_j * 100)}%</td>
<td class="r">${fmt(row.inv_j_mi)}</td>
<td class="r">${fmt(row.n_j)}</td>
<td class="r">${fmt(row.bunit)}</td>
<td class="r"><strong>${fmt(row.pb_j)}</strong></td>
</tr>`
).join("");
const fc_label = fc_sens_enabled
? fc_sel
: "1,00 — regra subsidiária (Seção 18)";
return `<!DOCTYPE html>
<html lang="pt-BR"><head>
<meta charset="UTF-8">
<title>Relatório PB — Cenário A</title>
<style>
* { box-sizing: border-box; }
body { font-family: Arial, sans-serif; font-size: 10.5pt; color: #111; margin: 0; padding: 2cm 2.5cm; }
h1 { font-size: 13pt; color: #1a3d6e; margin-bottom: 0.2em; }
h2 { font-size: 11pt; color: #1a3d6e; border-bottom: 1.5px solid #2C5AA0; padding-bottom: 0.2em; margin-top: 1.6em; }
h3 { font-size: 10.5pt; color: #333; margin-top: 1em; margin-bottom: 0.3em; }
p { margin: 0.4em 0 0.6em; line-height: 1.5; }
table { width: 100%; border-collapse: collapse; margin: 0.5em 0 0.8em; font-size: 9.5pt; }
thead th { background: #2C5AA0; color: white; padding: 5px 7px; text-align: left; }
td { padding: 4px 7px; border-bottom: 1px solid #e0e0e0; vertical-align: middle; }
tr.alt td { background: #f5f8fc; }
tr.total td { background: #dce9f8; font-weight: bold; }
tr.success td { background: #e8f5e9; font-weight: bold; }
.r { text-align: right; }
.formula-box { background: #f0f5fc; border-left: 4px solid #2C5AA0; padding: 0.5em 1em; border-radius: 4px; font-family: monospace; font-size: 10pt; margin: 0.5em 0; }
.result-box { background: #e8f5e9; border-left: 5px solid #2e7d32; padding: 1em 1.5em; border-radius: 6px; margin: 1em 0; }
.result-num { font-size: 18pt; font-weight: bold; color: #1a4f1a; margin: 0.2em 0; }
.header-inst { text-align: center; border-bottom: 2px solid #2C5AA0; padding-bottom: 1em; margin-bottom: 1.5em; }
.nota { font-size: 8.5pt; color: #666; margin-top: 0.2em; }
.footer { margin-top: 2em; padding-top: 0.8em; border-top: 1px solid #ccc; font-size: 8.5pt; color: #666; line-height: 1.6; }
.print-btn { position: fixed; top: 1rem; right: 1rem; z-index: 99; }
.print-btn button { background: #2C5AA0; color: white; border: none; padding: 0.5em 1.2em; border-radius: 5px; cursor: pointer; font-size: 10pt; }
@media print {
body { padding: 1.5cm 2cm; }
.print-btn { display: none !important; }
h2 { page-break-after: avoid; }
}
</style>
</head>
<body>
${forWord ? "" : `<div class="print-btn">
<button onclick="window.print()">🖨️ Imprimir / Salvar PDF</button>
</div>`}
<div class="header-inst">
<div style="font-size:9pt;color:#555;margin-bottom:0.4em;">
Ministério das Cidades · Secretaria Nacional de Mobilidade Urbana · CGDI/DEREG
</div>
<h1>Estimativa de População Potencialmente Beneficiada</h1>
<div style="font-size:10pt;">Cenário A — Orçamento previsto | Metodologia CGDI/SEMOB (2026)</div>
<div style="font-size:8.5pt;color:#777;margin-top:0.4em;">Gerado em ${data_hoje}</div>
</div>
<h2>1. Fórmula geral</h2>
<p>A estimativa aplica a <em>Nota Metodológica CGDI/SEMOB</em>, que substitui a fórmula anterior
<span style="font-family:monospace;">(INV / 2.500) × 2,96</span> por um cálculo baseado na extensão
física das obras, tipo de intervenção e densidade populacional do território atendido.</p>
<div class="formula-box">PB = KM × FI × DM_ref × FT × FC</div>
<p>Para obras lineares, a extensão é estimada via regra subsidiária:
<span style="font-family:monospace;">KM_i = INV_i / C_med,i</span></p>
<h2>2. Parâmetros gerais</h2>
<table>
<thead><tr><th>Parâmetro</th><th>Descrição</th><th class="r">Valor utilizado</th></tr></thead>
<tbody>
<tr><td><strong>INV_orç</strong></td><td>Orçamento total previsto (item 18)</td><td class="r">R$ ${fmt2(inv_orc_bi)} bilhões</td></tr>
<tr class="alt"><td><strong>α</strong></td><td>Participação de obras lineares</td><td class="r">${fmt1(alpha_pct)}%</td></tr>
<tr><td><strong>β = 1 − α</strong></td><td>Participação de aquisição de frota</td><td class="r">${fmt1(100 - alpha_pct)}%</td></tr>
<tr class="alt"><td><strong>DM_ref</strong></td><td>Densidade populacional de referência</td><td class="r">${fmt(dens_ref)} hab/km²</td></tr>
<tr><td><strong>FC</strong></td><td>Fator de complexidade</td><td class="r">${fc_label}</td></tr>
</tbody>
</table>
<h2>3. Parâmetros técnicos</h2>
<h3>3.1 Obras lineares</h3>
<table>
<thead>
<tr>
<th>Tipologia</th>
<th class="r">FI_i (km)</th>
<th class="r">FT_i</th>
<th class="r">C_med,i (R$ mi/km)</th>
<th class="r">w_i (%)</th>
</tr>
</thead>
<tbody>
${linhas_params}
<tr class="total"><td>Total</td><td class="r">—</td><td class="r">—</td><td class="r">—</td><td class="r">100%</td></tr>
</tbody>
</table>
<p class="nota">FI = faixa de influência; FT = fator de tipologia; C_med = custo médio por km (regra subsidiária); w_i = peso histórico normalizado.</p>
<h3>3.2 Aquisição de frota</h3>
<table>
<thead>
<tr>
<th>Tipo de veículo</th>
<th class="r">C_unit,j (R$ mi/un.)</th>
<th class="r">B_unit,j (pessoas/un.)</th>
<th class="r">w_j (%)</th>
</tr>
</thead>
<tbody>
${linhas_fro_params}
<tr class="total"><td>Total</td><td class="r">—</td><td class="r">—</td><td class="r">100%</td></tr>
</tbody>
</table>
<p class="nota">C_unit = custo unitário de aquisição; B_unit = passageiros beneficiados por veículo; w_j = peso histórico normalizado.</p>
<h2>4. Cálculo passo a passo</h2>
<h3>4.1 Divisão do orçamento</h3>
<table>
<thead><tr><th>Componente</th><th>Substituição numérica</th><th class="r">Resultado</th></tr></thead>
<tbody>
<tr>
<td>INV_lineares = α × INV_orç</td>
<td>${fmt1(alpha_pct)}% × R$ ${fmt2(inv_orc_bi)} bi</td>
<td class="r"><strong>R$ ${fmt2(inv_lineares_bi)} bi</strong></td>
</tr>
<tr class="alt">
<td>INV_frota = β × INV_orç</td>
<td>${fmt1(100 - alpha_pct)}% × R$ ${fmt2(inv_orc_bi)} bi</td>
<td class="r"><strong>R$ ${fmt2(inv_frota_bi)} bi</strong></td>
</tr>
</tbody>
</table>
<h3>4.2 Obras lineares por tipologia</h3>
<p style="font-size:9pt;color:#555;">Fórmula: KM_i = INV_i / C_med,i → PB_i = KM_i × FI_i × DM_ref × FT_i × FC</p>
<table>
<thead>
<tr>
<th>Tipologia</th>
<th class="r">w_i</th>
<th class="r">INV_i (R$ mi)</th>
<th class="r">KM_i (km)</th>
<th class="r">Área (km²)</th>
<th class="r">FT_i</th>
<th class="r">PB_i</th>
</tr>
</thead>
<tbody>
${linhas_lin}
<tr class="total">
<td>Total</td>
<td class="r">100%</td>
<td class="r">${fmt(inv_lineares_bi * 1000)}</td>
<td class="r">—</td>
<td class="r">—</td>
<td class="r">—</td>
<td class="r">${fmt(pb_lin_total)}</td>
</tr>
</tbody>
</table>
<p class="nota">Constantes aplicadas: DM_ref = ${fmt(dens_ref)} hab/km²; FC = ${fmt2(fc_val)}.</p>
<h3>4.3 Aquisição de frota</h3>
<p style="font-size:9pt;color:#555;">Fórmula: N_j = INV_j / C_unit,j → PB_j = N_j × B_unit,j</p>
<table>
<thead>
<tr>
<th>Tipo de veículo</th>
<th class="r">w_j</th>
<th class="r">INV_j (R$ mi)</th>
<th class="r">N_j (unid.)</th>
<th class="r">B_unit,j</th>
<th class="r">PB_j</th>
</tr>
</thead>
<tbody>
${linhas_fro}
<tr class="total">
<td>Total</td>
<td class="r">100%</td>
<td class="r">${fmt(inv_frota_bi * 1000)}</td>
<td class="r">—</td>
<td class="r">—</td>
<td class="r">${fmt(pb_fro_total)}</td>
</tr>
</tbody>
</table>
<h3>4.4 Agregação final</h3>
<div class="formula-box">PB_total = PB_lineares + PB_frota = ${fmt(pb_lin_total)} + ${fmt(pb_fro_total)} = <strong>${fmt(pb_total)} pessoas</strong></div>
<h2>5. Resultado</h2>
<div class="result-box">
<div style="font-size:8.5pt;font-weight:700;text-transform:uppercase;letter-spacing:0.06em;color:#2e7d32;margin-bottom:0.3em;">
População potencialmente beneficiada — Cenário A (Orçamento previsto)
</div>
<div class="result-num">${fmt(pb_total)} pessoas</div>
<div style="margin-top:0.5em;font-size:9.5pt;color:#444;">
Obras lineares: <strong>${fmt(pb_lin_total)}</strong> (${fmt1(pb_lin_share)}%)
|
Frota: <strong>${fmt(pb_fro_total)}</strong> (${fmt1(pb_fro_share)}%)
</div>
</div>
<h2>6. Comparativo metodológico</h2>
<table>
<thead>
<tr><th>Metodologia</th><th>Fórmula</th><th class="r">PB estimado</th></tr>
</thead>
<tbody>
<tr class="success">
<td>Nova metodologia (CGDI/SEMOB 2026)</td>
<td>PB_lin + PB_fro</td>
<td class="r">${fmt(pb_total)}</td>
</tr>
<tr>
<td>Fórmula anterior</td>
<td>(INV / 2.500) × 2,96</td>
<td class="r">${fmt(pb_antigo)}</td>
</tr>
</tbody>
</table>
<div class="footer">
<strong>Referência metodológica:</strong>
Nota Metodológica CGDI/SEMOB —
<em>Revisão da metodologia de cálculo da população beneficiada nas operações de crédito
com recursos do FGTS — Mobilidade Urbana</em>, CGDI/DEREG/SEMOB, 2026.<br>
Relatório gerado automaticamente pela Calculadora Interativa de PB — SEMOB/MCID | ${data_hoje}
</div>
</body></html>`;
}
const btnStyle = (bg) => pesos_validos
? `background:${bg};color:white;border:none;padding:0.55em 1.3em;border-radius:6px;cursor:pointer;font-size:0.88em;font-weight:600;`
: `background:#ccc;color:#888;border:none;padding:0.55em 1.3em;border-radius:6px;cursor:not-allowed;font-size:0.88em;font-weight:600;`;
const btn_pdf = htl.html`<button style="${btnStyle('#2C5AA0')}">🖨️ Exportar PDF</button>`;
btn_pdf.disabled = !pesos_validos;
const btn_word = htl.html`<button style="${btnStyle('#2E7D32')}">📝 Baixar Word</button>`;
btn_word.disabled = !pesos_validos;
btn_pdf.onclick = () => {
if (!pesos_validos) return;
const w = window.open("", "_blank", "width=950,height=800");
w.document.write(buildReport());
w.document.close();
};
btn_word.onclick = () => {
if (!pesos_validos) return;
const blob = new Blob(["\ufeff" + buildReport({forWord: true})], {type: "application/msword"});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `relatorio_pb_cenario_a_${new Date().toISOString().slice(0, 10)}.doc`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
return htl.html`
<div style="margin:2rem 0 0.5rem;padding:1.3rem 1.5rem;background:#f8f9fb;border:1px solid #dde4ee;border-radius:8px;">
<div style="font-size:0.92em;font-weight:700;color:#1a3d6e;margin-bottom:0.5rem;">
📄 Exportar relatório — Cenário A
</div>
<p style="font-size:0.88em;color:#555;margin:0 0 1rem;line-height:1.5;">
Gera um relatório completo com metodologia, parâmetros e cálculo passo a passo,
refletindo os valores atuais da calculadora.
</p>
${!pesos_validos ? htl.html`<p style="font-size:0.85em;color:#c00;margin:0 0 0.8rem;"><strong>Exportação bloqueada:</strong> corrija os pesos acima para que somem exatamente 100%.</p>` : htl.html``}
<div style="display:flex;gap:0.8rem;flex-wrap:wrap;">
${btn_pdf}
${btn_word}
</div>
<p style="font-size:0.78em;color:#999;margin:0.8rem 0 0;line-height:1.5;">
<strong>PDF:</strong> abre nova janela — use Ctrl+P (ou ⌘P) e selecione "Salvar como PDF".<br>
<strong>Word:</strong> baixa arquivo .doc compatível com Microsoft Word e LibreOffice.
</p>
</div>`;
}Entradas — cenário B
Informe a extensão total (km) de cada tipologia presente na carteira. Se uma tipologia não tem obras, deixe em 0. Se há múltiplas obras de uma mesma tipologia, some as extensões.
Obras lineares — extensão por tipologia
viewof obras_b = {
const state = {};
lineares.forEach(d => {
state[`${d.key}_km`] = 0;
});
const inputStyle = "width:100px;text-align:center;border:1px solid #ccd5e0;border-radius:4px;padding:0.3em 0.4em;font-size:0.88em;";
const el = htl.html`<div class="calc-table-wrap">
<table class="calc-table calc-table--meta">
<colgroup>
<col style="width:45%">
<col style="width:15%">
<col style="width:15%">
<col style="width:25%">
</colgroup>
<thead>
<tr>
<th class="calc-table__head calc-table__head--left">Tipologia</th>
<th class="calc-table__head calc-table__head--center">FI<sub>i</sub></th>
<th class="calc-table__head calc-table__head--center">FT<sub>i</sub></th>
<th class="calc-table__head calc-table__head--center" style="background:#c8e6c9;color:#1b5e20;">KM<sub>i</sub> (km)</th>
</tr>
</thead>
<tbody>
${lineares.map((item, i) => htl.html`<tr class="${i % 2 === 0 ? 'calc-table__row calc-table__row--alt' : 'calc-table__row'}">
<td class="calc-table__cell calc-table__cell--left">${item.tipologia}</td>
<td class="calc-table__cell calc-table__cell--center">${item.fi}</td>
<td class="calc-table__cell calc-table__cell--center">${item.ft}</td>
<td class="calc-table__cell calc-table__cell--center">
<input type="number" value="0" min="0" step="0.1" style="${inputStyle}" data-key="${item.key}_km">
</td>
</tr>`)}
</tbody>
</table>
</div>
<p style="font-size:0.8em;color:#666;margin-top:0.4em;">FI e FT vêm dos parâmetros compartilhados acima. Informe a extensão real de cada tipologia na coluna KM.</p>`;
el.value = {...state};
el.querySelectorAll("input").forEach(inp => {
inp.addEventListener("input", () => {
state[inp.dataset.key] = +inp.value;
el.value = {...state};
el.dispatchEvent(new Event("input", {bubbles: true}));
});
});
return el;
}Frota — unidades por tipo de veículo
viewof frota_unid_b = {
const state = {};
frota.forEach(d => {
state[`${d.key}_n`] = 0;
});
const inputStyle = "width:100px;text-align:center;border:1px solid #ccd5e0;border-radius:4px;padding:0.3em 0.4em;font-size:0.88em;";
const el = htl.html`<div class="calc-table-wrap">
<table class="calc-table calc-table--meta">
<colgroup>
<col style="width:45%">
<col style="width:25%">
<col style="width:30%">
</colgroup>
<thead>
<tr>
<th class="calc-table__head calc-table__head--left">Tipo de veículo</th>
<th class="calc-table__head calc-table__head--center">B<sub>unit,j</sub> (pessoas/un.)</th>
<th class="calc-table__head calc-table__head--center" style="background:#c8e6c9;color:#1b5e20;">N<sub>j</sub> (unidades)</th>
</tr>
</thead>
<tbody>
${frota.map((item, i) => htl.html`<tr class="${i % 2 === 0 ? 'calc-table__row calc-table__row--alt' : 'calc-table__row'}">
<td class="calc-table__cell calc-table__cell--left">${item.tipo}</td>
<td class="calc-table__cell calc-table__cell--center">${item.bunit}</td>
<td class="calc-table__cell calc-table__cell--center">
<input type="number" value="0" min="0" step="1" style="${inputStyle}" data-key="${item.key}_n">
</td>
</tr>`)}
</tbody>
</table>
</div>
<p style="font-size:0.8em;color:#666;margin-top:0.4em;">Informe o número de unidades adquiridas por tipo de veículo.</p>`;
el.value = {...state};
el.querySelectorAll("input").forEach(inp => {
inp.addEventListener("input", () => {
state[inp.dataset.key] = +inp.value;
el.value = {...state};
el.dispatchEvent(new Event("input", {bubbles: true}));
});
});
return el;
}Metodologia — cenário B
Quando a extensão (KM) e a quantidade de veículos (N) são conhecidas, a fórmula é aplicada diretamente, sem regra subsidiária:
Obras lineares — para cada tipologia \(i\):
\[ PB_{\text{lin},i} = KM_i \times FI_i \times DM_{\text{ref}} \times FT_i \times FC \]
Aquisição de frota — para cada tipo de veículo \(j\):
\[ PB_{\text{frota},j} = N_j \times B_{\text{unit},j} \]
Agregação: \(PB = \sum_i PB_{\text{lin},i} + \sum_j PB_{\text{frota},j}\)
Cálculo passo a passo — cenário B
pb_lin_b = lineares_eff.map((item) => {
const km_i = obras_b[`${item.key}_km`]
const area_i = km_i * item.fi
const pop_area_i = area_i * dens_ref
const pb_i = Math.round(pop_area_i * item.ft * fc_val)
return {
...item,
km_i,
area_i,
pop_area_i,
pb_i
}
})
pb_lin_b_active = pb_lin_b.filter(row => row.km_i > 0)
pb_lin_b_total = sumValues(pb_lin_b.map(row => row.pb_i))
pb_fro_b = frota_eff.map((item) => {
const n_j = frota_unid_b[`${item.key}_n`]
const pb_j = n_j * item.bunit
return {
...item,
n_j,
pb_j
}
})
pb_fro_b_active = pb_fro_b.filter(row => row.n_j > 0)
pb_fro_b_total = sumValues(pb_fro_b.map(row => row.pb_j))
pb_total_b = pb_lin_b_total + pb_fro_b_total
pb_fro_b_share = safePct(pb_fro_b_total, pb_total_b)
pb_lin_b_share = safePct(pb_lin_b_total, pb_total_b)Obras lineares
htl.html`
${pb_lin_b_active.length === 0
? htl.html`<p style="color:#888;font-style:italic;">Nenhuma extensão informada. Preencha a coluna KM acima.</p>`
: htl.html`
<div class="calc-table-wrap">
<table class="calc-table calc-table--meta">
<colgroup>
<col style="width:25%">
<col style="width:12%">
<col style="width:12%">
<col style="width:17%">
<col style="width:12%">
<col style="width:22%">
</colgroup>
<thead>
<tr>
<th class="calc-table__head calc-table__head--left">Tipologia</th>
<th class="calc-table__head calc-table__head--right">KM<sub>i</sub></th>
<th class="calc-table__head calc-table__head--right">FI<sub>i</sub></th>
<th class="calc-table__head calc-table__head--right">Área (km²)</th>
<th class="calc-table__head calc-table__head--right">FT<sub>i</sub></th>
<th class="calc-table__head calc-table__head--right">PB<sub>i</sub></th>
</tr>
</thead>
<tbody>
${pb_lin_b_active.map((row, i) => htl.html`<tr class="${i % 2 === 0 ? 'calc-table__row calc-table__row--alt' : 'calc-table__row'}">
<td class="calc-table__cell calc-table__cell--left">${row.tipologia}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt1(row.km_i)}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt2(row.fi)}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt1(row.area_i)}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt2(row.ft)}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(row.pb_i)}</td>
</tr>`)}
${htl.html`<tr class="calc-table__row calc-table__row--total">
<td class="calc-table__cell calc-table__cell--left">Total</td>
<td class="calc-table__cell calc-table__cell--right">${fmt1(sumValues(pb_lin_b_active.map(r => r.km_i)))}</td>
<td class="calc-table__cell calc-table__cell--right">—</td>
<td class="calc-table__cell calc-table__cell--right">—</td>
<td class="calc-table__cell calc-table__cell--right">—</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(pb_lin_b_total)}</td>
</tr>`}
</tbody>
</table>
</div>
<p style="font-size:0.8em;color:#666;margin-top:0.4em;">
Constantes: <strong>DM = ${fmt(dens_ref)} hab/km²</strong>,
<strong>FC = ${fmt2(fc_val)}</strong>.
</p>`
}
`Aquisição de frota
htl.html`
${pb_fro_b_active.length === 0
? htl.html`<p style="color:#888;font-style:italic;">Nenhuma unidade informada. Preencha a coluna N acima.</p>`
: htl.html`
<div class="calc-table-wrap">
<table class="calc-table calc-table--meta">
<colgroup>
<col style="width:40%">
<col style="width:20%">
<col style="width:20%">
<col style="width:20%">
</colgroup>
<thead>
<tr>
<th class="calc-table__head calc-table__head--left">Tipo de veículo</th>
<th class="calc-table__head calc-table__head--right">N<sub>j</sub></th>
<th class="calc-table__head calc-table__head--right">B<sub>unit,j</sub></th>
<th class="calc-table__head calc-table__head--right">PB<sub>j</sub></th>
</tr>
</thead>
<tbody>
${pb_fro_b_active.map((row, i) => htl.html`<tr class="${i % 2 === 0 ? 'calc-table__row calc-table__row--alt' : 'calc-table__row'}">
<td class="calc-table__cell calc-table__cell--left">${row.tipo}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(row.n_j)}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(row.bunit)}</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(row.pb_j)}</td>
</tr>`)}
${htl.html`<tr class="calc-table__row calc-table__row--total">
<td class="calc-table__cell calc-table__cell--left">Total</td>
<td class="calc-table__cell calc-table__cell--right">—</td>
<td class="calc-table__cell calc-table__cell--right">—</td>
<td class="calc-table__cell calc-table__cell--right">${fmt(pb_fro_b_total)}</td>
</tr>`}
</tbody>
</table>
</div>`
}
`Resultado — cenário B
htl.html`
<div style="background:${pb_total_b > 0 ? "#EAF7EA" : "#f5f5f5"};
border-left:6px solid ${pb_total_b > 0 ? "#2E7D32" : "#ccc"};
padding:1.3em 1.8em;border-radius:6px;margin:1em 0;">
<div style="font-size:0.78em;color:${pb_total_b > 0 ? "#2E7D32" : "#888"};font-weight:700;
text-transform:uppercase;letter-spacing:0.07em;margin-bottom:0.25em;">
População potencialmente beneficiada — carteira definida
</div>
<div style="font-size:2.5em;font-weight:700;color:${pb_total_b > 0 ? "#1a4f1a" : "#aaa"};line-height:1.1;">
${pb_total_b > 0 ? fmt(pb_total_b) + " pessoas" : "—"}
</div>
${pb_total_b > 0
? htl.html`
<div style="display:flex;gap:2em;font-size:0.85em;color:#444;margin-top:0.6em;flex-wrap:wrap;">
<span>Obras lineares: <strong>${fmt(pb_lin_b_total)}</strong> (${fmt1(pb_lin_b_share)}%)</span>
<span>Frota: <strong>${fmt(pb_fro_b_total)}</strong> (${fmt1(pb_fro_b_share)}%)</span>
</div>`
: htl.html`<p style="font-size:0.9em;color:#888;margin-top:0.5em;">
Informe a extensão (km) e/ou unidades de frota nas tabelas acima para ver o resultado.
</p>`
}
</div>
`Sobre os parâmetros
Os parâmetros técnicos (FI, FT, C_med, C_unit, B_unit), bem como os pesos históricos e a divisão do orçamento (\(\alpha\) e \(\beta\)), devem ser recalculados anualmente pela CGDI com base nos últimos cinco anos de execução da carteira FGTS/MCID.
Os parâmetros de frota carregados refletem a metodologia da Nota Indicadores FGTS — Aquisição Frota (CGDI/SEMOB, 2026), com custos unitários derivados das contratações do Novo PAC (2024–2026) e beneficiários por unidade calculados com base em capacidade, ocupação média e viagens comerciais diárias.