In a dense downtown view from about 400 m up, the occlusion margin of
max(3 m, 2% of the range) came to 14 to 20 m, so a wall, awning, tree or
lower facade standing 10 to 20 m in front of a street was read as the
road itself: the vehicle's dot was hidden by the scene, but its bracket and
callout were still drawn. Checked against scene.pickPosition in one such
view, 18 of 470 vehicles in frame fell in that band.
The margin now depends on how steeply the camera looks down on the
contact. A point on the line of sight s metres in front of a vehicle
stands s * sin(elevation) above it, so a surface there hides the vehicle
once that rise passes 2 m. The margin stays at least 3 m and at most 2%
of the range, so grazing and street-level views are unchanged. In the
same views no vehicle with the drawn surface within 3 m in front of it
(its own road) is newly hidden.
From the review of the first change:
- A contact back in view after longer than a retest interval is held back
and retested like a new one, instead of being drawn from an answer
given before it left the view. It gets a fresh record, so a test still
in flight from before cannot end the hold-back either.
- The hold-back for a contact's first answer stretches with the retest
pace when a crowded view slows it.
- A batch whose control point failed no longer counts as a first answer.
- An unknown log-depth flag reads as "no depth" rather than as standard
depth, which would have put every surface in front of every contact.
- A shader that fails to build leaves no shader behind and is not built
again on the same context.
The detection overlay only hid a contact behind the Earth's curve, so a
street vehicle behind or under a building, a 3D tile or a hill still had
its bracket and "VEH-nnnn" callout painted over that geometry, while its
own dot was depth-tested and correctly hidden.
A contact a layer marks `depthTested: true` (only Street Traffic does) is
now tested against the depth the scene drew and left out of the overlay
while a drawn surface stands in front of it by more than max(3 m, 2% of
its range). Other layers are untouched: their markers are drawn over the
scene on purpose.
The test runs on the GPU and never waits for it. Each tested contact is
one point drawn inside an occlusion query; the vertex shader looks up the
frame's packed pick depth at the contact's pixel, decodes it and drops the
point when the surface is nearer than the contact's threshold. Query
results are collected on later frames. Reading depth back was measured
and rejected: `scene.pickPosition` per contact costs 0.2 to 1.4 ms each,
and Chromium answers `getBufferSubData` with a round trip to its GPU
process even behind a signalled sync object (1.5 ms on a quiet machine,
15 to 25 ms on a busy one).
- Contacts are retested every 100 to 200 ms, spread across frames, at
most 256 a frame; with more in view the pace stretches so none starves.
- Hiding is immediate. A hidden contact returns 600 ms after its last
"hidden" answer, so one on a silhouette edge does not blink, and a
contact new to the view is held back until its first answer (400 ms at
most), so orbiting does not flash brackets behind buildings.
- A callout whose contact goes behind something is dropped at once, not
carried to the next solve and not faded out on the facade.
- These times run on painted frames, so a hidden tab does not expire them.
- A control point in every batch must draw; if it does not, the batch is
ignored and after three the probe stands down for ten seconds. With no
pick depth (2D, Columbus view, depth picking off) nothing is hidden.
A WebGL 1 context reads depth back synchronously about 8 times a second.
The renderer internals this depends on are read in one function,
`readSceneDepthSource`, with the engine version they were checked against.
- layerStateTokenLedger.test: the retired-token reuse case now offers the
retired token (02) to the newcomer, not News's 00.
- he.json: the catalog's singular imperative, {count} first in the plural,
Wikidata read as one source, and smoother legend and info text.
- layerManifest: News after Shipwrecks, so the prompt-budget comment stays
next to the entries it explains.
- CURRENT-STATE: Prettier-formatted (table column, blank line before a heading).
Main added the News layer at the same places: both layers are kept in the
layer names, panel order (News stays last of Events), standalone sources,
voice manifest, credits, DATA_SOURCES and the CURRENT-STATE table. News took
share-link token 00 first, so Shipwrecks moves to 01 (ledger, registry,
CURRENT-STATE and the token tests). The catalog now has 40 layers and the
share-link registry 38. Shipwreck strings are added to the new Hebrew
catalog; the other catalogs keep both sides' strings.
Removing one source's wrecks on the page split merged records: a wreck both
sources list is one Wikidata record carrying EMODnet facts, so expiring
Wikidata dropped wrecks EMODnet still had, and expiring EMODnet kept its
facts. The page now keeps every shown wreck, marked stale, until the newest
source passes the limit, then clears them all. Adds a source test for the
reported ages and limit.
The page compared the proxy's fetchedAt with its own clock, so a skewed
browser clock cleared or kept wrecks wrongly; a snapshot without a timestamp
never expired; and one old source cleared the other's fresh wrecks. The proxy
now reports each source's age on its own clock and its seven-day limit; the
page turns them into per-source deadlines when the snapshot arrives (seven
days from arrival when no age is given) and, on a failed refresh, removes only
the sources past theirs. A refused short refresh is logged.
CURRENT-STATE said each source is asked at most once a day; a failed source
is retried after 15 minutes. It now also covers refused short refreshes and
the page clearing expired wrecks, and its two shipwreck paragraphs are
rewrapped. The card's comment no longer says the records carry no names.
- A source without a version (undefined, not null) compared as unchanged, so
an injected source's new snapshots were never drawn.
- When the page could not refresh (the proxy answers 502 once both sources
are past seven days), it kept showing the old wrecks as fresh. They are now
marked stale, and removed once older than the proxy's own seven-day limit.
A 200 answer that was valid but cut short (30 Wikidata wrecks instead of
about 1,450) replaced the last good set for a day. It now counts as a failed
refresh: the old records are served as stale and the source is retried after
15 minutes; once the old set is past seven days, the new one is taken.
- The legend counted each era twice, scanning every record eight times per
row refresh; it now counts once.
- Each year picker listed every year from the oldest dated wreck (well over
a thousand entries); it lists the years that have a wreck, plus the chosen
ends.
- Labels fell back to Portuguese and links to Portuguese Wikipedia while the
app ships 11 locales: labels now fall back through the app's languages
after English, and a wreck with no English article links its Wikidata
item, which lists the article in every language.
A report placed no closer than a country was a marker at the country's
centre, so a country's news piled there. They are now one disc with the
number of reports, named, and a click opens the Local list over the
country. Markers are 24 px at least: the category icon was unreadable at 16.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
DATA_SOURCES and CURRENT-STATE said text naming people is dropped. The filter
drops text with a title or role word (Mr, captain, owner, crew, reported by);
a bare name with none of them still passes. The docs and the code comment
now say so.
Air accidents (#1584) is not merged, so comments, the changelog fragment and
CURRENT-STATE no longer explain the shipwrecks markers, card and picker by
it; the row-range test fixture is the shipwrecks row. CURRENT-STATE now says
the markers are one billboard collection and the period is stored swapped.
They were listed as owned by the ghostmaps and ghostmaps-source packages,
which they are not part of; ghostmaps-source is meant to stay free of
rendering code, and shipwrecks/index.js imports Cesium. The app groups that
own every layer keep them.
A period set as 1950 to 1900 (a link or the voice tool) showed as 1900–1950
but stayed reversed, so changing "From" to 1920 gave 1920 to 1900, shown as
1900–1920: the end the reader did not touch moved.
- The ~8,000 markers were one Cesium entity each; they are now one
BillboardCollection (as FIRMS, vessels and wildlife sightings do), ordered
at the bottom of the shared sprite stack so moving traffic draws over it.
- eyeOffset.z was the death toll in metres, so a 1,500-death wreck's marker
sat 1.5 km toward the camera and clipped or drifted when close. It is gone;
markers are added by rising toll, so a bigger toll still draws on top.
- The page asks every 6 h but the proxy changes data at most daily: the proxy
now sends a version that changes with either source's data, and the layer
rebuilds only when it does.
- Adds a controller test: enable/disable, teardown, draw order, unchanged
snapshots, a late response after turning off, and the period closing an
open card.
A refresh with one source down (or Wikidata answering 200 with no results)
was cached as the day's snapshot, replacing the full set; and once the
snapshot was a day old, every request after a failure fetched both sources
again. Each source now keeps its own last good records, served as stale for
up to seven days, and a source that failed is not asked again for 15 minutes,
even when there is no data yet. A Wikidata answer without results, or with no
wreck in it, counts as a failure.
The query read P576 through wdt:, which carries no precision, and a missing
precision counted as a day. It now reads the best-rank statement's value with
wikibase:timePrecision, and a value without a precision is trusted to the
year only. Against the live query service the new query takes 23 s and dates
16 more wrecks while every date the old one gave is unchanged.
parseYear took the minus sign of -0299 as a separator, so a wreck from 300 BC
showed as year 300, sat at AD 300 in the period picker and stretched its
range. Years before AD 100 now keep their year as text ("300 BC", "AD 50")
and count as ancient, like EMODnet's period-dated wrecks; the query service's
XSD 1.1 years (0000 is 1 BC) are converted. Merging records now carries that
text with the date, and coarser-than-year precisions give no date.
The exclusion tested the provenance after it was cut to 80 characters, so a
long source list ending in wrecksite.eu kept the row, and a row whose only
trace was a wrecksite.eu page link was kept too. Both raw values are now
checked before anything is cut.
When the whole feed backs off, no poll counts a miss, so the tracked
aircraft read STALE at full brightness while no newer position came in.
It now takes the same stale fade as a missed update, in the flights and
military layers.
Local kept World News API's rows listed after the server had deleted them.
README names News among the routes refused behind a proxy; a timed-out
request is said to count; the cockpit cached state is near the same place.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Deleted World News API news is not restored by a refresh that fails; a
refund goes only to the day the read was counted in, and not to a request
that timed out; a cockpit brief from another place is dropped, not kept for
a language change; a feed list started over lets scrolling ask again.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Keep World News API results an hour at most and say caching needs its
permission; refund a read that never reached it; match Local by any pin;
assign a disputed area by a point inside it; untitled GDELT stories are
minor; hotspot counts leave out minor reports.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
A malformed request line crashed the server, any web page could spend the
news keys, and the forwarding proxy turned the news server's 503 key and
configuration states into a generic 502.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The route check allowed 200 km off the great circle between the airports, so a
coast-to-coast flight routed a few hundred kilometres around weather lost its
origin and destination (JFK–SFO over Kansas, 384 km south). The tolerance is
now 200 km or 15% of the route's length, whichever is larger: 623 km for
JFK–SFO, and still 200 km for a short hop, so a wrong leg stays hidden.