v63 — 2026-07-25
A gravidade de São Paulo, não a do livro-texto. O
g do modelo passa de 9,81 para 9,7864
m/s² — o valor local, medido por gravimetria
absoluta no IAG-USP. Os 9,81 são ≈0,24% altos aqui, e todo MDT que
o app carrega e todo passeio contra o qual o modelo foi calibrado
ficam na região metropolitana de São Paulo. Resultado:
β = m·g/k_eff cai de 0,7585 para 0,7567
kJ/m (veja o leitor de coeficientes em Parâmetros) e
toda energia cai ≈0,24% em terreno de subida
(≈0,2% em terreno misto). As rotas ótimas quase não mudam — o
custo inteiro reescala por um fator praticamente comum; um bundle
antigo, recarregado, recalcula com o g novo e lê
≈0,2% abaixo do raster que guardou. A mudança re-sincroniza o app
com o modelo do repositório irmão
(bicycling-energy-model, Entry 27): enquanto os dois
discordavam, o applet de lá lia exatamente essa diferença. É
mudança só de constante — nenhum motor mudou, e a paridade bit a
bit com o backend Rust segue exata.
v62 — 2026-07-12
Direções ≠ 8 no backend nativo/nuvem. O backend
Rust (0.2.0) agora serve o recurso completo de direções de
movimento (4–128): conjuntos de Farey, arestas longas integradas
ao perfil e carimbo de passagens nas células varridas, em
paridade bit a bit com o motor JS (21 casos novos de
paridade, todos com delta zero). O app confere a versão do
backend (≥ 0.2.0) antes de despachar — um binário antigo
ignoraria o campo silenciosamente — e diz o motivo quando o
backend é velho demais. A estimativa de tempo modela os termos
de memória das direções extras.
v61 — 2026-07-12
Fallback silencioso agora é opcional. Nova opção
Falhar em vez de cair pro navegador no painel da nuvem:
marcada, uma falha na nuvem (boot da VM, senha, ou o próprio
cálculo) aborta o run com o erro na linha de status, em vez de
recomputar silenciosamente nos workers do navegador — o fallback
é amigável pra runs casuais, mas mascara problemas da nuvem e
gasta o laptop num cálculo dimensionado pra 128 vCPUs.
v60 — 2026-07-12
Ergonomia da troca de máquina na nuvem. Timeout
do /cloud/start 15→45 s (o redimensionamento roda
dentro dessa chamada — ~11 s medidos — e o aborto do cliente
virava um falso "orquestrador inacessível"); o religamento no
meio do boot leva o machineType selecionado; um
aviso de estado indica quando a VM ligada (keep-warm) difere do
tamanho selecionado (a seleção só vale no próximo boot); e o novo
botão Desligar VM agora força esse próximo boot — trocar
de máquina vira desligar → selecionar → calcular.
v59 — 2026-07-12
Nuvem funcionando de verdade — e com escolha de máquina.
A CSP agora permite o host real do orquestrador (o modo Nuvem
estava silenciosamente bloqueado pela própria CSP desde que foi
lançado), a URL padrão aponta pro serviço vivo, e uma URL colada
sem esquema ganha https:// automático. Novo seletor
Máquina da nuvem (8/32/128 vCPUs, com preço de
referência por hora — a VM roda em modo spot, tipicamente
60–90% mais barato): o tamanho é aplicado ao criar/religar a VM
e a estimativa de tempo modela o tamanho selecionado (curva de
contenção de banda agora sublinear — 128 vCPUs não estimam mais
um absurdo 1,4× sobre 8). Cálculos na nuvem dizem "Calculando
na nuvem…", e qualquer retorno aos workers do navegador
agora diz o motivo na linha de status.
v58 — 2026-07-11
Recortar e reduzir resolução do DEM (1A).
Recortar à janela atual recorta o DEM carregado para a
janela visível do mapa — aproxime de um bairro num raster enorme
(ex. o Sítio Urbano de 135 M células) e fique só com aquela
extensão, com cálculos mais rápidos e menos memória (só DEMs
geográficos, como os pulls OSM). Reduzir resolução
(fator 2/3/4/6×) faz média de blocos N×N ciente da máscara — a
mesma transformação do gdalwarp -r average: 6× num
raster IGC de 5 m cai em ~30 m, o regime onde o modelo v2 é mais
preciso (journal, Entries 19/21); funciona também em DEMs
projetados. Ambos passam pelo caminho padrão de carregamento
(exportação→reimportação em memória), então herdam tudo:
cancelamento de cálculo, máscara nodata, CRS, invalidação de
rede/grafo/sonda e a marca de suavização (uma derivação de MDT
já suavizado NÃO é re-suavizada), e removem a URL de origem dos
metadados de bundle (são derivações locais).
v57 — 2026-07-11
Direções de movimento, string pulling e correção de
grade — a nota de pesquisa de conectividade da grade
virou opção. Novo seletor "Direções de
movimento" (4/8/16/32/64/128; padrão 8 = motor
clássico, bit-idêntico): mais direções (escada de Farey, com
movimentos longos integrados ao perfil do terreno) reduzem a
sobre-estimativa de energia ótima causada pelo serrilhado das
rotas (≈⅔ a menos com 16 direções, medido no MDT de SP), a
custo de cálculo proporcional. Cálculos de densidade amortizam
tabelas de custo pré-computadas por worker; as passagens são
carimbadas nas células que os movimentos longos atravessam
(corredores contínuos). Valores ≠ 8 calculam no navegador — o
backend nativo segue servindo o motor de 8 movimentos. Nova
opção "string pulling": encurta pós-cálculo a
rota exibida e as top-N (ida-e-volta e maximizar ficam de
fora) — a energia mostrada é a soma integrada da própria linha
exibida. Novo campo "correção de grade ×c" no
bloco de acessibilidade: infla os limiares E₁/E₂ (equivale a
deflacionar as energias sobre-estimadas da grade; c* ≈
1,09–1,12 medido em 8 direções), com aviso explícito de que a
garantia de piso conservador deixa de valer; desnecessária com
≥ 16 direções. As três opções persistem nos bundles; a
estimativa de tempo acompanha o número de direções.
v56 — 2026-07-11
KPIs de acessibilidade — iniciativa "cidade 300 kJ".
Cálculos de densidade agora também amostram o campo de energia de
cada referência na célula de todas as outras — uma matriz K×K
par-a-par sem custo extra de Dijkstra. Novo bloco
"3B. Acessibilidade" nos resultados avalia os dois
KPIs ao vivo a partir da matriz em cache: fração da população que
alcança ≥ K pessoas com ≤ E₁ kJ (padrão 1 milhão / 200 kJ) e
fração que alcança ≥ J% da população com ≤ E₂ kJ (padrão 80% /
600 kJ). A amostragem censitária (IBGE 2022) agora registra a
população total M da extensão — cada referência representa M/K
pessoas; outras estratégias aceitam um M digitado (o KPI 2
dispensa M). Editar os limiares reavalia na hora, sem recalcular;
os marcadores das referências são recoloridos
vermelho→amarelo→verde pelo acesso atingido; exportações CSV por
referência e da matriz completa. O backend Rust ganha
want_matrix no /density (f32×K² anexado,
"matrix":K no meta; paridade bit-a-bit com o worker
JS, garantida por novos casos +matrix no
test-backend.mjs, incluindo regressão de índice com
referência descartada — um binário antigo degrada para "KPI
indisponível", nunca uma matriz errada). O bloco avisa quando um
limiar excede o orçamento de energia do cálculo — ali a matriz é
truncada e os KPIs viram um piso.
v55 — 2026-07-07
Suavização do MDT + meta de precisão validada
(bicycling-energy-model journal Entry 20). MDTs finos
(pixel ≤ 10 m, como os rasters IGC-SP de 5 m) agora recebem uma
suavização gaussiana estática (σ = 10 m, normalizada por máscara)
aplicada uma vez ao carregar — a mitigação da sobrecarga de
resolução do Entry 19, na configuração validada. Novo controle
"Suavização do MDT" em 1A (auto / desligada / 10 / 20 / 30 m; o
auto pula fontes grossas como FABDEM e re-importações já
suavizadas, que o dem.tif exportado agora marca via
tag). As alturas são suavizadas no app antes de irem aos motores —
paridade JS/Rust intocada; mudar o controle vale no próximo
carregamento de MDT. Com esse σ + parâmetros calibrados por
ciclista (CdA, Crr, k_s ajustados no próprio histórico —
procedimento no journal), o erro validado em passeios não vistos
ficou em mediana |Δ%| 3.7 / 2.7 / 4.9 com viés < ±1% em três
ciclistas independentes — atingindo a meta de ±5% / ±2%. Nota
honesta: a calibração é a alavanca maior; a suavização é o padrão
validado que também ameniza o viés sem calibração. Novo
test-dem-smoothing.mjs trava a transformação.
v54 — 2026-07-07
Energy-model journal alignment (bicycling-energy-model
journal Entries 18/19). Reference-geometry (imported/drawn track)
energy readout only — routed-path/field energy is untouched.
refEnergyKJ's descent-recovery ε now ports the champion
harness's epsGeom estimator exactly: drop-weighted over
30 m-resampled cells, replacing a lumped mean-descent-grade
approximation its own comment had claimed but never implemented.
Also clarified (comments only, no behaviour change): the v2 engine's
trailing descent-cost clamp is provably dead code — a 1.78 M-combo
sweep plus 1 402 real rides never triggered it — kept only for
JS/Rust bit-parity. New disclosures: the v2 model is tuned for
~30 m DEM sampling, so on the deployed 5 m IGC-SP DTM energies read
conservatively high on hilly terrain; and FABDEM is not recommended
for energy work on flat/urban terrain, since its per-pixel noise
inflates measured ascent.
v53 — 2026-07-03
Icon cache-bust fix. v50's icon redesign kept every
filename identical, colliding with the 30-day cache deploy.sh
sets on icons/ — a browser or CDN edge that had cached the
old bytes wouldn't see the new icon for up to 30 days regardless of
redeploys. Every shipped icon file now carries a -v2
suffix, bypassing every existing cache outright.
favicon.ico can't be renamed (fixed well-known path), so
its cache lifetime is cut from 30 days to 1 instead.
v52 — 2026-07-03
Cross-repo energy-model audit fixes. An audit against
bicycling-energy-model (the energy law's spec repo) confirmed
v2Edge is correct everywhere it's called, but found graph-mode
maximize ignoring mode "to" (always scored forward, unlike
top-N and the raster path) and round-mode path energy
reporting only the outbound leg instead of the round-trip total. Both fixed,
with new regression tests. Removed the legacy QGIS plugins this app was
ported from.
v51 — 2026-07-03
Second full-repo review fix batch (see
docs/review-2026-07-02-round2-workorder.md). Top-N and
length-constrained-DP correctness fixes (true route energy, "até"-mode
direction, repulsion clamp); graph-mode spatial-hash rewrite and a
T-junction merge fix; bundle geotransform check and a zip-bomb cap;
DEM-load and OSM-loader identity guards; worker-pool teardown; export
georeferencing fixes; cloud VM lease + boot-order + spoofing
hardening; a Content-Security-Policy and help-dialog focus trap;
remaining i18n gaps and doc corrections. An adversarial
re-review of the batch's own diff caught two more races it had
introduced: a water-mask rebuild's staleness check and a DEM
load's generation guard could each be fooled by unrelated
concurrent activity — both now check the right thing.
v50 — 2026-07-02
New app icon — a yellow ascent arrow crossing a
blue topographic contour field, replacing the old bicycle glyph.
Regenerated every size (favicon, 192/512, apple-touch, maskable
192/512) from the new design source; same file names throughout,
so no manifest changes were needed. Also placed beside the
"Simujaules" title in the panel header, which was
text-only before.
v49 — 2026-07-02
Full-repo review fix batch (see
docs/review-2026-07-01.md). Top-N: an admissible A*
heuristic (the old one overcharged gentle descents, so route #1 could
cost more than the field's own optimum) and correct route direction in
"até" mode. "Maximizar" no longer silently empties under an energy
budget. Graph mode: fixed a half-cell terrain/rasterisation/snapping
registration error, T-junctions now join the network, chained
bridge/tunnel decks flatten as one continuous span, and node snap
tolerance is capped at 15 m on coarse DEMs. Native backend: maximize
now matches the browser engine under a network constraint, and
/single passes are exact on very large DEMs. Hardened a
crafted-.gpkg XSS, bundle import now cancels an in-flight compute
first, and the compute-time estimate no longer sticks on
"estimating…" if calibration fails. Deploy no longer deletes the
cloud VM startup script; the service worker installs past the HTTP
cache and stops caching large DEM/GeoPackage files. i18n and
v1→v2 cost-model doc fixes; the offline census pipeline works again.
v48 — 2026-06-29
Comparação de rotas origem→destino. Com
“Comparar com cenário sem rede” ligado, o seletor
“Cenário exibido” passa a alternar também a melhor
rota, não só o campo: rota no terreno (sem
restrição) em azul, rota na rede (restrita) em laranja, as
duas juntas na vista de diferença — as mesmas cores da diferença de
densidade. Uma melhor rota por cenário. Vale no modo raster (máscara
de rede) e no modo grafo (“seguir os vetores”). Passe o mouse ou
toque em qualquer uma das rotas para ver a energia e o comprimento
das duas de uma vez (mais o Δ — o custo de ficar na rede);
funciona com hover no desktop e com toque no celular.
v47 — 2026-06-29
Reverted the MTPI basemaps added in v46 — they were meant for amora,
not Simujaules.
v46 — 2026-06-29
Two MTPI (multi-scale topographic position index) basemaps in
the basemap dropdown — Pindorama 90 m (COP90, South America, native
z10) and Bacia do Paraná 30 m (native z12), XYZ tiles from
telhas.pedalhidrografi.co.
v45 — 2026-06-29
Bridge/tunnel deck passes read as a continuous line again. A
deck is a 1-cell line over water (unreached by the compute), so its
stamped density was faint and dropped by the passes-overlay downsample
on big DEMs — a gap along the bridge. The stamp is now dilated by the
render stride. Render-only.
v44 — 2026-06-29
v2 energy model. The cost is now physics-based —
mass, Crr, CdA, air density, drivetrain efficiency, power on the flat
and a climb threshold replace the α/β/η knobs. Per edge it splits rolling
from aero (aero charged only off the climbs) with a per-grade descent
recovery ε = clamp(min(1, (α/β)/s) − 0.13). Worker, graph engine and
the Rust backend move together (bit-parity); reference-geometry energy
uses the closed form with a 2 m elevation deadband. Defaults are São
Paulo-context (ρ=1.1, Crr=0.008, 75 kg, ~80 W flat); the panel shows the
derived α_r / α_a / β coefficients (kJ/m) and a k_smooth knob. See
bicycling-energy-model/notas.md.
v43 — 2026-06-26
New Geometria de referência layer: its layer-control row has
an ↑ upload button (instead of opacity) that loads a GPX track, drawn
as a magenta overlay. Hover or click the track to see its metrics —
distance, total energy (the app's α·dist+β·dh model with the current
parameters), total ascent and total descent; elevation comes from the
GPX or is sampled from the loaded DEM. Also: energy now reads as a
plain decimal (e.g. “3357 kJ”) instead of scientific notation. JS/UI
only.
v42 — 2026-06-26
Removed the on-map Leaflet attribution strip; the same credits
(Leaflet, Leaflet-Geoman, OpenStreetMap, CARTO, Esri, and the
pedalhidrografi.co hydrography overlay) now live in the
Atribuições / Attributions section above. JS/UI only.
v41 — 2026-06-26
Mobile layout fix (JS/UI only). On phones the locate, hamburger and
layer-control buttons now mirror the desktop top-right stack instead
of being scattered to the bottom corners, and the layer-control
modal opens below them — before, the tall modal covered the
bottom-corner buttons so you couldn't tap the layer button to close
its own modal. The modal also scrolls vertically when space is tight
(its 2-column flow moved into a height:auto child) instead of
spilling sideways. Sized for an iPhone SE.
v40 — 2026-06-25
Passes are now always shown as a normalized density rather
than raw counts (JS/UI only; display-only and colour-invariant). A
single run's passes are subtree-size counts, while the
multi-reference density already normalizes by H·W twice — so the two
read on very different scales (a 3C channel showing 12
instead of the density's ~1e-10). Count-based passes are
scaled by 1/(H·W)² into the same units as the density at
every render point; already-normalized fields are left untouched.
The colour/pattern is unchanged (a constant scale) — only the
numbers and placeholders move to density units. The GeoTIFF export
stays counts.
v39 — 2026-06-25
Density-display polish, dataset export/import, and map fixes (JS/UI
only; engine untouched). Graph-mode passes: network vectors keep a
true orange (terrain a true azure) — intensity is now opacity, not a
multiply that muddied mid-values into yellow-brown — and the
filtro média N filter applies to the network channel
(precise vectors when empty, a rasterised + mean-smoothed field when
set; the difference view composites both channels into one
orange/azure raster). The 3C.a passes and 3B energy min/max inputs
now show the resolved auto numbers instead of the static
p10/p90/auto. The sidebar balances its columns evenly once
they overflow (was column-1-heavy). The rmsampa-v2 hydrography is
crisp on HiDPI displays and no longer vanishes past z17. New in
Group 0: export/import of the four inputs in matching
formats — DEM and mask as GeoTIFF, network and bridges as GeoPackage.
The layer-control panel is resizable to two columns.
v38 — 2026-06-25
A UI/UX review pass (JS/UI only; engine untouched). Units: the budget
stays in kJ, so α/β are labelled kJ/m again (reverting v37's
J/m); downhill recovery is now a 0–100 % input (default 10), mapped to
the engine's 0–1 fraction. The collapse/expand group cue is a quiet
grey bar now (was a loud near-white border that out-shouted the
status colours); Baixar bundle is secondary so Calcular
is the only primary. Drawing shows an armed button state and
Esc cancels; the “Concluído em…” pill auto-dismisses; the disabled
Calcular button explains what it needs; enabling density
auto-opens the points/references group. Accessibility: bilingual
accessible names on the icon buttons, file pickers and layer-panel
rows, accent-color on native controls, ≥24px touch targets,
reduced-motion support. Plus i18n/copy fixes, shorter (non-clipping)
select options, and mobile button placement fixes. New cloud example
datasets: Viário RMSampa (road network, 1B) and
Águas RMSampa (water mask, 1C), covering the full RMSampa region.
v37 — 2026-06-25
UI refinements on top of v36 (JS/UI only; engine untouched). Sidebar
groups reordered/renamed (1B–1D, 2A–2C; "maximizar energia" and
"origem das referências" removed); the multi-column sidebar fills
each column top-to-bottom before the next and scrolls vertically
only. The 1B network params and 2A parameters are now 2-col
label|input tables (cost coefficients in J/m, budget in kJ).
The energy-field / density / legend controls moved into the
Controle de camadas panel (its × removed — the layer button
toggles it, Esc closes it); Statistics stay in the sidebar. Collapsed
groups read grey, expanded white (under the green/orange/yellow/red
status colours); 1C/1D go green on drawn geometry alone. The app is
now titled Simujaules with a tagline. Fixes: collapsing the
sidebar no longer blanks the map; the rmsampa-v2 overlay no longer
404s tiles past zoom 16.
v36 — 2026-06-24
Big UI overhaul (JS/UI only — engine, worker and backend untouched,
bit-parity preserved). The sidebar is reorganised into nested,
collapsible numbered groups (0 Import/Export · 1
Inputs [1A–1D] · 2 Compute setup [2A–2C] · 3 Results
[3A–3D]) with per-group status colours, auto-expand, and a draggable
1–4-column resizer. Layer visibility/opacity/order + basemap (now with
an Esri satellite option) + reference markers moved into a
non-blocking on-map Controle de camadas panel (floating
button, unified per-layer rows). Compute progress/log float in a
dismissable bottom pill. Full config persistence + Export/Import/Reset
(Group 0), and bundles embed the entire UI state. On-map drawing
(Leaflet-Geoman): barrier + passable corridor
polygons (1C) and portal lines (1D) feed the compute and
round-trip through config/bundles. Smaller: density tweaks follow the
displayed channel (terrain vs network), passes auto-max = p90 above
auto-min, scientific-notation density bounds, desktop sidebar collapse,
locate marker + accuracy circle, credit moved here. Sampling order is
now Censo / Sobol / Uniforme (Halton dropped).
v35 — 2026-06-24
App moved to its own domain: now served from the root of
simujaules.pedalhidrografi.co (dedicated bucket) instead of
telhas.pedalhidrografi.co/simujoules/, and branded
Simujaules (an affective typo of joules); absolute URLs
repoint to the new root (the RDF vocabulary namespace stays on telhas as a
stable identifier, resolved via redirect). And cloud compute
(Nuvem — VM orquestrada) now works when the app is accessed
remotely: a public, password-gated Cloud Run orchestrator creates/starts/
stops/deletes the GCP VM on demand, and the browser sends the big compute
payloads directly to the VM over HTTPS — fill in the Orchestrator URL and
the new Cloud password to use it. The VM uses an ephemeral IP and is
deleted after 30 days idle (cost → 0). No engine changes.
v32 — 2026-06-24
New IBGE 2022 census (population density) reference-sampling
strategy. Instead of spreading references uniformly, it samples them by
where people actually live: a live, in-browser port of
census/sample_census.py that fetches the census setores
inside the DEM from a cloud FlatGeobuf over HTTP Range requests (only the
bbox slice transfers, not the ~450 MB national file), weights each
setor by population × (area inside the DEM / full area),
picks setores by a 1-D Sobol inverse-CDF and drops points inside them with
a 2-D Sobol rejection draw. Brazil-only and online-only (like the OSM and
cloud features); replaces the current reference set.
v30 — 2026-06-23
Bundles now round-trip the "Comparar com cenário sem rede" (compare) view:
the unconstrained energy/passes and the saved difference field are written
to the bundle, the compare toggle + e-max-mode are recorded, and load
rebuilds the scenario picker and difference view — no recompute. (Graph-mode
compare restore is still a follow-up.)
v29 — 2026-06-23
Difference (network-cost) passes view recoloured from red/green to
orange (network) + azure-blue (terrain) — additive
complements that sum to white (max brightness on overlap) and stay
distinguishable under red–green colour-blindness. The two passes control
groups now have colour-chip labels (Network / Terrain).
v28 — 2026-06-23
Faster network interpolation on large DEMs: the post-compute IDW fill now
sizes its worker pool by deviceMemory instead of a fixed budget
that pinned big DEMs to a single worker. Output unchanged.
v27 — 2026-06-23
Cloud keep VM warm between runs toggle: when ticked, a run leaves the
VM up to reuse on the next run (only the first run pays the boot); the
orchestrator lease + in-VM idle-watchdog stop it after ~15 min idle.
Tab-hide no longer stops the VM in keep-warm mode. Default stays
stop-after-each-run.
v26 — 2026-06-23
Cloud compute source. The "Use native backend (Rust)" checkbox is now a
three-way Compute source selector: Browser (in-page worker
pool, the default), Localhost (the native Rust backend, with its URL
field), and Cloud. Cloud drives a small local orchestrator
(http://127.0.0.1:8079) that boots a pre-baked VM on demand,
waits for it to be healthy, runs the compute through it, then stops the VM
after the run (and on tab hide). A transfer-size estimate (upload / download /
wire time) shows beforehand. Cloud is only available when the applet is served
locally (the orchestrator listens on loopback). On any orchestrator or boot
failure the run falls back to the in-browser worker pool. Browser/Localhost
behaviour and engine bit-parity are unchanged.
v25 — 2026-06-22
Graph mode now RUNS the comparison instead of disabling it. "Constrain to
network" is forced on + locked while "Compute on network graph (follow the
vectors)" is on (graph mode is inherently network-constrained), but "Compare
with unconstrained" stays togglable: graph mode then computes a full-DEM
unconstrained raster scenario alongside the graph compute and exposes the
energy difference (the cost of being restricted to the network) through the
"Displayed scenario" picker (graph / unconstrained / difference). Native backend:
"Use native backend (Rust)" moved out of the density panel (always visible) and
now also accelerates single-source from/to/round ENERGY fields via a new
POST /single endpoint — top-N routes, the destination path and
"maximize" stay browser-only (the backend produces no routes), auto-fallback
unchanged. energy-worker.js + backend/src/main.rs kept bit-parity
(test-backend.mjs +single cases).
v24 — 2026-06-21
Fix: "Calcular sobre o grafo da rede (seguir os vetores)" takes precedence over the
raster "Restringir cálculo à rede" / "Comparar com cenário sem rede" toggles, so with
graph mode on (it's remembered between sessions) the comparison never ran and its
"Cenário exibido" dropdown never appeared. Graph mode now greys out and disables those
two toggles (with a tooltip) while it's on, so the precedence is visible.
v23 — 2026-06-21
OSM ele on bridge/tunnel decks (group 1d): "Pull bridges & tunnels from
OSM" now also reads node ele tags and uses the mapped deck elevation for the
raster portal endpoint heights (per-abutment, with way-ele / nearest-node
fallback) instead of guessing from the bare-earth DEM. Unmapped ends fall back to the
DEM, so a pull without ele is identical to v22. Persisted in the bundle;
engine/backend kept bit-parity. Graph mode stays DEM-based.
v22 — 2026-06-21
New 1c source: "Pull water from OSM" builds the impassable mask straight from
Overpass — water areas (natural=water / riverbank / reservoir, incl. multipolygon
relations), the open sea/ocean (from natural=coastline, with land-left/water-right
orientation), and non-tunnelled waterway=river lines (with a "Rivers (lines)
impassable" toggle). Streams and tunnelled waterways stay passable. It reuses the
uploaded-mask pipeline (Invert, corridors, overlay, bundle). Needs a geographic DEM.
v21 — 2026-06-21
Fix: "Load FABDEM for current viewport" crashed with "TypeError: t is not a
function" — the tile-mosaic loop variable shadowed the i18n t() function after
status strings were migrated to it. Renamed the loop variable; the button works
again.
v20 — 2026-06-20
Bridge follow-ups. Deck passes/density now show up: a bridge portal jumps
abutment→abutment, so deck cells previously rendered no passes even when the bridge
carried heavy traffic — they're now painted with the crossing flow. Groups 1c
(impassable mask) and 1d (bridges) light up when their data is loaded. And a new 1d
"Extract from loaded network" button derives bridge/tunnel decks from the network
already loaded in 1b (gpkg tags / OSM other_tags) — offline, no Overpass. Both 1c and
1d now have an "Apply to compute" toggle to enable/disable their effect without
clearing the data.
v19 — 2026-06-19
Optional OSM bridges & tunnels (group 1d). A dedicated "Pull bridges & tunnels from
OSM" control queries Overpass for bridge (and optionally tunnel) ways over the DEM
extent and models each as a level deck between its two ground abutments — useful for
inland viaducts over valleys/saddles, not just water (a bare-earth DEM omits the deck,
so routing over a bridge otherwise dives into the gap below). Each deck becomes a portal
edge between its end cells at the flat-deck cost, relaxed alongside the grid edges, so
the route over a bridge and the route under it (a cross-street beneath a viaduct) both
stay correct — true multi-level routing on a 2.5-D grid. Graph mode ("follow the
vectors") also goes multi-level: the OSM streets pull captures bridge/tunnel/layer tags,
so a deck crossing a road at a different layer doesn't connect and deck edges read ~flat.
Decks render on the map and persist in bundles as bridges.geojson. The native backend
gained matching portal support (bit-for-bit parity). With no bridges loaded, results are
unchanged.
v18 — 2026-06-19
Optional impassable mask (group 1c). Upload a binary GeoTIFF (1 = impassable, e.g.
water bodies); it's resampled onto the DEM grid by area-coverage majority (≥50% of a
cell's footprint impassable ⇒ blocked; outside the mask's extent = passable), so it can
have a different extent / resolution / CRS than the DEM. Masked cells block routing.
With a vector network loaded (1b), an optional toggle carves narrow passable "bridge"
corridors across the mask, each levelled to a smooth profile (land elevation at the
shores, ramping to a ±offset at the bridge centre, clamped −5…+15 m). A "show on map"
toggle paints blocked water (red) and corridors (green), and bundles gain
impassable.tif. Engines unchanged — a run with no mask reproduces prior results
exactly.
v17 — 2026-06-19
UI / accessibility pass. The "Load DEM" and "vector network" groups now light up once
their data is loaded. Parameter & visualization choices persist across reloads
(localStorage; session data like the DEM and points isn't saved — that's what bundles
are for). The clear-points / place-random / clear-refs buttons are clustered together,
and only the src/dst picker fades in density mode. The sidebar hint paragraphs were
removed and their content moved into this Help modal (now also covering maximize, the
OSM pull, the native backend, worker sizing and QMC sampling). Accessibility: the
status line is an aria-live region, field labels are associated with their inputs,
opacity sliders have labels, there's a keyboard focus ring, and the example loaders
are buttons.
v16 — 2026-06-19
Time estimate fixed for network/graph runs. Graph mode ("follow the vectors") was
estimated with the raster model (~1000× too high — a graph search is ∝ edges, not
135 M cells); it now uses a graph-size model. The IDW interpolation fill is now a
separate phase (often the larger one) that scales with the max ray distance, and it's
corrected independently of the compute. Toggling interpolate / compute-on-graph /
constrain-to-network now updates the estimate. Native backend log line now echoes
Emax / mode / type.
v15 — 2026-06-19
Reloading a bundle now restores all saved layers, not just the energy/passes fields.
Top-N routes and the maximize path were already in the exported zip
(routes.geojson / path.geojson) but the loader never read them back — it redrew the
fields and asked you to recompute. They now come back and recolor exactly like a fresh
compute, no recompute needed. (Graph-mode edges aren't restored yet.)
v14 — 2026-06-19
Compute-time estimate accuracy fix (was up to ~3× low, now ~±20% with no bias).
Backend density on a large DEM was the main culprit: the estimate assumed all cores
parallelise, ignoring that the server caps concurrent slices to a memory budget
(1-2 fit on a 135 M-cell DEM, not cores-many) — it now replicates that cap
(/health reports the memory budget). The calibration probe is now
cell-capped (≤~1.5 s on any DEM, anchored on an unsaturated point — fixes a separate
up-to-3.8× error on small DEMs), and an online correction learns actual/predicted per
engine so the estimate converges to your machine within a run or two.
v13 — 2026-06-18
"Follow the vectors" network-graph mode (new graph-engine.js): with a vector network loaded,
an optional toggle routes on the real polyline graph (planarised — junctions at crossings or
only at shared endpoints) instead of the rasterised mask, so passes/paths trace the vectors
with no staircase; all compute modes; colored-vector overlay; JS-only.
v12 — 2026-06-12
Density worker pool (multi-core) + Dijkstra heap optimisations (~10× density runs).
Optional native Rust backend (off by default, density only, auto-fallback).
QMC (Sobol/Halton) sampling option for random reference placement.
Round-trip budget mode: cap each leg or the round-trip total; round-mode passes
count only completable (in-budget) trajectories.
"Energy color" passes blend (hue from energy, opacity from passes).
Optional vector-network rendering (black lines, configurable ground width + opacity; passes overlay stacks above the network; stacking reorderable via a modal) and a
"constrain compute to network" toggle. Basemap selector (OSM / Carto minimal /
solid colours). Export of rendered energy/passes PNGs with world files. OSM (Overpass) street-network pull. Constrained-vs-unconstrained comparison: interpolated energy difference + additive red/green passes overlay with per-channel controls (regular and density modes). Interpolation prefilter + worker-pool banding. Density radix-heap engine (in-browser density within ~20% of the native backend on huge DEMs). Compute-time estimate is now budget- and engine-aware (per-DEM calibration probe at load): changing the energy budget, alpha, worker count, or backend now moves the estimate (it previously ignored the budget entirely). Density scales to large workloads: leaner f32 workers + deviceMemory-budgeted pool + opt-in "max compute workers" override; the native backend caps parallel slices to a memory budget (SIMU_MAX_MEM_GB) so it no longer OOM-crashes at high ref counts. Backend liveness/progress fixes. SEO/LLM metadata (description, canonical, Open Graph, JSON-LD, llms.txt, sitemap). Fixes: PWA manifest restored, service-worker install bug, offline
caching of CDN libs (SRI + crossorigin), 3-D GeoPackage (Z/M) parsing, stale-result
race on DEM/network swap mid-compute, worker-crash handling, XSS hardening of
Bundle-before-DEM loading order now restores correctly (pending-bundle re-apply). Density+network Compute no longer aborts silently (null-src re-snap bug); network clicks snap grid-wide (no more snap-radius dead-end); .gpkg geometry column name read from metadata; empty-on-DEM networks rejected loudly.
v11 — 2026-05-13
Wasm engine removed (JS worker only). Fix layered-DP backtrack direction bug
(spurious "backtrack_fail"); fix ReferenceError in result metadata.
v8–v10 — 2026-05
Reverse-optimisation (maximize energy) toggle; length-constrained max-cost path
via layered DP; DP failures surfaced in the UI instead of the console.
v4–v7 — 2026-05
DEM relief layer (cmocean.phase + slope); locate-me button; OOM fixes for huge
DEMs (reservoir-sampled percentiles, stride-capped canvas); drawer/z-index and
iOS private-browsing fixes.
v1–v3 — 2026-05-08
First public shell: GeoTIFF DEM loading, asymmetric-cost Dijkstra energy fields,
passes count, top-N routes, multi-reference density, vector-network constraint,
bundle export (GeoTIFF + JSON-LD), PT/EN, FABDEM viewport loader, PWA shell.