min(lon)/max(lon) is the wrong extent for anything that crosses the
antimeridian. A North Pacific species with cells at 165 E and 155 W has a
-180..180 span of ~360 deg – the whole globe – so fitBounds centres the
camera on longitude 0 and the viewer lands off Iceland while the animal lives
in the Bering Sea (apps#9). The span that describes it is 165..205.
Details
The rule: measure the span in BOTH frames (-180..180 and 0..360) and keep the narrower one. Longitudes are periodic, so the two frames cut the circle at opposite points (0 deg and 180 deg) and a distribution can straddle at most one of them unless it genuinely encircles the globe – in which case both spans are wide and the -180..180 answer is returned unchanged.
The returned xmax may exceed 180. That is deliberate and is what MapLibre
wants: map.fitBounds([160, 48, 210, 66]) centres at 185 -> -175 (verified
in-browser). Wrapping it back into -180..180 would put west east of east and
fit the COMPLEMENT of the intended box.
lon_span_agg() is the same rule applied to aggregates already computed
elsewhere – the form the apps use, because the four min/max come back
from one DuckDB query over millions of cells and must not be pulled into R
just to be reduced. Keeping both forms on one implementation is what stops
the SQL path and the vector path from drifting apart.