@rr0/ufoathome 0.66.0 → 0.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -95
- package/dist-embed-scene/UfoMessages_fr-_6zR9Sw1.js +1 -0
- package/dist-embed-scene/rr0-scene.mjs +166 -166
- package/dist-embed-sighting/CoverageAssessor-CZfxbN9A.js +1 -0
- package/dist-embed-sighting/HynekAssessor-DPsLpEGQ.js +1 -0
- package/dist-embed-sighting/SightingMessages_fr-CdnlQOHg.js +1 -0
- package/dist-embed-sighting/UfoMessages_fr-_6zR9Sw1.js +1 -0
- package/dist-embed-sighting/rr0-sighting.mjs +113 -113
- package/dist-embed-sighting-editor/{BodyEditor-eKnfK1A2.js → BodyEditor-Q5FX46YD.js} +2 -2
- package/dist-embed-sighting-editor/CoverageAssessor-C_QKvYFk.js +1 -0
- package/dist-embed-sighting-editor/HynekAssessor-Onzg_YHp.js +1 -0
- package/dist-embed-sighting-editor/SightingEditorMessages_fr-lSHcWCCl.js +1 -0
- package/dist-embed-sighting-editor/UfoMessages_fr-_6zR9Sw1.js +1 -0
- package/dist-embed-sighting-editor/rr0-sighting-editor.mjs +289 -289
- package/dist-embed-sighting-editor/sightingSchema-k4pEltg7.js +1 -0
- package/package.json +1 -1
- package/dist-embed-scene/UfoMessages_fr-BbAWUSSI.js +0 -1
- package/dist-embed-sighting/CoverageAssessor-mVlehblF.js +0 -1
- package/dist-embed-sighting/HynekAssessor-BiP2di32.js +0 -1
- package/dist-embed-sighting/SightingMessages_fr-KwK5zJGF.js +0 -1
- package/dist-embed-sighting/UfoMessages_fr-BbAWUSSI.js +0 -1
- package/dist-embed-sighting-editor/CoverageAssessor-FIt771Wz.js +0 -1
- package/dist-embed-sighting-editor/HynekAssessor-DUqRa3Su.js +0 -1
- package/dist-embed-sighting-editor/SightingEditorMessages_fr-D2QBeLp7.js +0 -1
- package/dist-embed-sighting-editor/UfoMessages_fr-BbAWUSSI.js +0 -1
- package/dist-embed-sighting-editor/sightingSchema-DGLfQ-J1.js +0 -1
package/README.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
# UFO@home
|
|
4
4
|
|
|
5
|
-
**UFO@home** lets a UFO
|
|
5
|
+
**UFO@home** lets a UFO observer record the shape, appearance and movement of what they saw — and replay it like a VCR —
|
|
6
6
|
instead of relying only on a written or spoken account. The approach follows [Roger Shepard's
|
|
7
7
|
recommendation](https://rr0.org/time/1/9/6/8/07/29/Symposium/Shepard/index_fr.html) that a visual reconstruction of a
|
|
8
|
-
|
|
8
|
+
account is more faithful than an oral or written one.
|
|
9
9
|
|
|
10
10
|
Originally a Java applet (2003), the project has been rewritten from scratch in TypeScript: a small,
|
|
11
11
|
dependency-light engine (keyframe timeline, recording, playback, a three.js scene) wrapped in three vanilla
|
|
@@ -17,13 +17,13 @@ for what that buys.
|
|
|
17
17
|
### Naming
|
|
18
18
|
|
|
19
19
|
`<rr0-scene>` is named without "ufo" on purpose: it renders a generic 3D scene (sky/horizon/stars/decor) from a
|
|
20
|
-
real-world time and place, and stands the
|
|
20
|
+
real-world time and place, and stands the observer's own phenomenon in it. Under it lives a playback layer —
|
|
21
21
|
`UfoElement`, the timeline, the controls, the canvas the pointer works on — reached as `scene.ufoElement`; it was a
|
|
22
22
|
component of its own (`<rr0-ufo>`, the shape painted on a bare background) until 0.54.0, when the phenomenon moved
|
|
23
23
|
into the scene and a shape with no sky stopped being a thing this project draws. Read-only playback needs no
|
|
24
24
|
"player" suffix, since it is every component's default behavior, and `<rr0-sighting-editor>` is the one that needs
|
|
25
|
-
a qualifier (it *adds* recording on top). `<rr0-sighting>` (renamed from `<rr0-ufo-
|
|
26
|
-
standard way to display any real sighting, whether it has one
|
|
25
|
+
a qualifier (it *adds* recording on top). `<rr0-sighting>` (renamed from `<rr0-ufo-observers>` — see below) is the
|
|
26
|
+
standard way to display any real sighting, whether it has one observer or several: a observer account always implies
|
|
27
27
|
a real place and time, so it always composes `<rr0-scene>`.
|
|
28
28
|
|
|
29
29
|
The project's own site is **[ufoathome.org](https://ufoathome.org)**: [the demos](https://ufoathome.org/demos/)
|
|
@@ -84,7 +84,7 @@ attribute the three other elements take. That is what makes an address per obser
|
|
|
84
84
|
this attribute, and [its player](https://ufoathome.org/play/) does the same for read-only replay.
|
|
85
85
|
Any path the site does not otherwise serve becomes that parameter, so
|
|
86
86
|
|
|
87
|
-
- `https://ufoathome.org/play/?sighting=/demo-data/
|
|
87
|
+
- `https://ufoathome.org/play/?sighting=/demo-data/observer-socorro.json`, or simply
|
|
88
88
|
- `https://ufoathome.org/Socorro`
|
|
89
89
|
|
|
90
90
|
open that observation. A bare name with no `/` is looked for among the site's own demos first, then
|
|
@@ -110,11 +110,11 @@ the host page's own `lang` then `navigator.languages`, no picker UI.
|
|
|
110
110
|
|
|
111
111
|
The element the two others build on (~530KB gzip — [Three.js](https://threejs.org/) plus
|
|
112
112
|
[`astronomy-engine`](https://github.com/cosinekitty/astronomy)'s planetary/lunar position tables, which don't
|
|
113
|
-
tree-shake since they're one shared data table used internally for every body): the
|
|
113
|
+
tree-shake since they're one shared data table used internally for every body): the observer's own phenomenon
|
|
114
114
|
standing in a 3D sky/horizon/starfield/decor scene computed from the recording's real time and place, with the
|
|
115
115
|
playback controls under it. Its own members are `src`, `sightingData`, `loadFromSrc`, `enableClickToPlay` (forwarded
|
|
116
116
|
to the playback layer), `ufoElement` (that layer), `sceneRenderer`, and the attributes `show-compass`,
|
|
117
|
-
`show-
|
|
117
|
+
`show-observer-map` and `hide-milestones`. Click-to-play/pause works anywhere on the scene (the playback layer's
|
|
118
118
|
transparent canvas covers the whole stage), and the fullscreen button fullscreens the *whole* scene — it sets the
|
|
119
119
|
layer's `fullscreenTarget` to its own outer stage for this.
|
|
120
120
|
|
|
@@ -206,7 +206,7 @@ blurred one in a sharp frame was close.
|
|
|
206
206
|
The frame is letterboxed inside the widget's own box rather than resizing it, and the field it
|
|
207
207
|
implies becomes the recording's default — a recording that states its own (a zoom, binoculars) keeps
|
|
208
208
|
it. An eye has no rectangle and no format, and neither has a camera nobody identified: both fall back
|
|
209
|
-
to the scene's own 16:9 at the 60° vertical field this project draws an unaided
|
|
209
|
+
to the scene's own 16:9 at the 60° vertical field this project draws an unaided observer through
|
|
210
210
|
(about the middle half of a real human field, which is where acuity actually is). The catalogue is
|
|
211
211
|
dated, so the picker offers what existed: no telephone in 1964.
|
|
212
212
|
|
|
@@ -222,13 +222,13 @@ through a 50 mm at f/2 for twenty seconds (see `src/engine/instrument/LimitingMa
|
|
|
222
222
|
darkness/color follows the sun's altitude (day/twilight bands/night), and its dawn/dusk glow is anchored on the
|
|
223
223
|
sun's real compass direction, not spread uniformly around the horizon — see `src/render3d/skyColors.ts`.
|
|
224
224
|
|
|
225
|
-
The
|
|
226
|
-
the sighting's timeline via `
|
|
227
|
-
`timeline` — the model class is `ObserverTrack`, but the serialized key is `
|
|
225
|
+
The observer's own pose — geographic position, elevation, and viewing heading/pitch/field of view — can vary over
|
|
226
|
+
the sighting's timeline via `observerTrack` in the sighting JSON (a keyframe array of `{ t, pose }` alongside
|
|
227
|
+
`timeline` — the model class is `ObserverTrack`, but the serialized key is `observerTrack`, and writing the class's
|
|
228
228
|
name into a file is a mistake that costs an afternoon; same
|
|
229
229
|
hold-last/interpolated-lookup shape — see `src/engine/model/ObserverTrack.ts`), driving both the camera's own
|
|
230
230
|
orientation and which real-world instant the astronomy is computed for as playback advances. Older recordings
|
|
231
|
-
with no `
|
|
231
|
+
with no `observerTrack` fall back to the legacy static `place[0]` (see `resolveObserverPoseAt` in
|
|
232
232
|
`src/engine/model/Sighting.ts`) — usable for sky darkness/color and camera pitch/fov, but with no compass heading
|
|
233
233
|
to orient the camera by.
|
|
234
234
|
|
|
@@ -237,14 +237,14 @@ approximation) stays in the repo, tested, and still backs `skyBrightness()`'s tw
|
|
|
237
237
|
the live rendering path now uses `astronomy-engine` for the Sun too, for a single source of truth and to get the
|
|
238
238
|
Sun's azimuth from the same call used for the sky's directional glow.
|
|
239
239
|
|
|
240
|
-
`<rr0-sighting-editor>` has editor fields for the
|
|
241
|
-
date/time (all optional) — filling in lat+lng writes both the legacy `place` and a single t=0 `
|
|
240
|
+
`<rr0-sighting-editor>` has editor fields for the observer's latitude/longitude/heading and the observation's start
|
|
241
|
+
date/time (all optional) — filling in lat+lng writes both the legacy `place` and a single t=0 `observerTrack`
|
|
242
242
|
keyframe (elevation/pitch/field of view stay at neutral defaults; there's no UI yet for authoring the observer
|
|
243
243
|
*moving* over time, only a single static pose per recording).
|
|
244
244
|
|
|
245
245
|
Not yet done: a mirage, the supernumerary arcs crowded inside a bright rainbow and the corona round a Sun seen
|
|
246
246
|
through a thin water cloud (all three are interference, and nothing here models the wave — see `WaterDrop.ts`),
|
|
247
|
-
and a multi-keyframe `
|
|
247
|
+
and a multi-keyframe `observerTrack` authoring UI (today the editor can only set
|
|
248
248
|
one static pose; an observer that moves/re-orients mid-recording still needs hand-authored or scripted JSON). The
|
|
249
249
|
Moon's phase currently only dims/brightens its disc's overall
|
|
250
250
|
color rather than rendering a geometrically accurate crescent shape — a natural follow-up.
|
|
@@ -283,7 +283,7 @@ coverage floor:
|
|
|
283
283
|
recorded tail length and are drawn with no tail at all.
|
|
284
284
|
|
|
285
285
|
- **Satellites** (`src/engine/astronomy/Satellites.ts`). For every date, the ILLUMINATION: `h = R(sec B - 1)` gives how high the Earth's shadow stood
|
|
286
|
-
above the
|
|
286
|
+
above the observer, so deep in the night nothing in low orbit is lit and a light crossing the sky
|
|
287
287
|
then was not a satellite. Being lit and being seen are kept apart — everything in orbit is sunlit
|
|
288
288
|
by day, and an Iridium flare at magnitude -8 was genuinely watched at noon. Also complete, and
|
|
289
289
|
from CelesTrak's SATCAT: how many tracked objects were in orbit that month, and when each named
|
|
@@ -309,10 +309,10 @@ coverage floor:
|
|
|
309
309
|
family keeps a second, independent derivation in closed form (`IceHalos.ts`, `Rainbows.ts`) whose only job is to
|
|
310
310
|
disagree with the trace; that check has already caught one shipped error.
|
|
311
311
|
|
|
312
|
-
All of them appear in the editor's read-only "Sky:" line, with a button to turn the
|
|
312
|
+
All of them appear in the editor's read-only "Sky:" line, with a button to turn the observer toward
|
|
313
313
|
the meteor or the comet. The bow line is said only when rain was reported — everybody knows whether it was
|
|
314
314
|
raining, so the interesting answers are the negative ones: a Sun higher than 42° puts every bow below a ground
|
|
315
|
-
|
|
315
|
+
observer's horizon, and an unbroken deck between the Sun and the rain is the missing half of the famous
|
|
316
316
|
condition.
|
|
317
317
|
|
|
318
318
|
**Regenerating the satellite catalog.** `src/engine/astronomy/satelliteCatalog.ts` is generated by
|
|
@@ -339,9 +339,9 @@ apparitions, and the peak magnitudes and tail lengths recorded at the time, are
|
|
|
339
339
|
`scripts/build-comet-catalog.ts` — the orbits are looked up, the brightness is an observation, and the script's
|
|
340
340
|
own doc comment explains why the two cannot come from the same place. The generated file *is* checked in.
|
|
341
341
|
|
|
342
|
-
**The phenomenon stands in the scene, and is still only what the
|
|
342
|
+
**The phenomenon stands in the scene, and is still only what the observer saw.** The shape used to be painted on a
|
|
343
343
|
2D canvas laid over the 3D scene, so that nothing about it could be read as a claim about a solid at a distance. It
|
|
344
|
-
is now a plane *in* the three.js scene (`src/render3d/PhenomenonSystem.ts`), facing the
|
|
344
|
+
is now a plane *in* the three.js scene (`src/render3d/PhenomenonSystem.ts`), facing the observer and scaled so that
|
|
345
345
|
it subtends exactly the angle the recording states — which makes it look the same from their eye at any distance
|
|
346
346
|
whatever, and is what keeps the claim where it was: the recording states angles and nothing else, and the distance
|
|
347
347
|
the plane is drawn at is a parameter of the picture (`src/engine/shape/PhenomenonDepth.ts`, see *Where the shape
|
|
@@ -350,7 +350,7 @@ the same halo, blur, veil and spikes into its texture. What changed is who decid
|
|
|
350
350
|
depth, per pixel, so a patrol car in front of it hides exactly the part of it a patrol car would, where the overlay
|
|
351
351
|
sampled nine points and hid the shape whole or not at all. The ground and the terrain are kept out of that on
|
|
352
352
|
purpose — the phenomena are drawn in a pass of their own, depth-tested against the decor alone — because a relief
|
|
353
|
-
patch at thirty-metre resolution deciding what a
|
|
353
|
+
patch at thirty-metre resolution deciding what a observer saw would not be a reconstruction (Socorro's craft, a
|
|
354
354
|
hundred feet away in the arroyo below the road, sank two metres under one). It also means the phenomenon goes
|
|
355
355
|
through the instrument's own projection like everything else in the scene, and through the same long-exposure
|
|
356
356
|
accumulation, instead of an approximation of each on a separate layer. The overlay keeps the pointer's business:
|
|
@@ -359,17 +359,17 @@ to test "was it a helicopter" — is a different statement, and a different obje
|
|
|
359
359
|
|
|
360
360
|
## `<rr0-sighting>` — standard sighting view
|
|
361
361
|
|
|
362
|
-
The standard way to display any real sighting, whether it has one
|
|
363
|
-
`<rr0-ufo-
|
|
364
|
-
nested `<rr0-scene>` the same way `<rr0-sighting-editor>` does, since a
|
|
362
|
+
The standard way to display any real sighting, whether it has one observer or several — renamed from
|
|
363
|
+
`<rr0-ufo-observers>` once it stopped being just a multi-observer selector (see [Naming](#naming)). It composes a
|
|
364
|
+
nested `<rr0-scene>` the same way `<rr0-sighting-editor>` does, since a observer recording is
|
|
365
365
|
always a real sighting and always needs the real sky/ground backdrop.
|
|
366
366
|
|
|
367
367
|
```html
|
|
368
368
|
<rr0-sighting src="sighting.json"></rr0-sighting>
|
|
369
369
|
```
|
|
370
370
|
|
|
371
|
-
`src` accepts either a single
|
|
372
|
-
`events` of `eventType: "sighting"` each point at one
|
|
371
|
+
`src` accepts either a single observer's `sighting.json` directly or a **case**: RR0's own `case.json`, whose
|
|
372
|
+
`events` of `eventType: "sighting"` each point at one observer's `SightingRecordingJson` by `url`, read relative to
|
|
373
373
|
the case file itself (so the same case works from its dossier's page and from anywhere else). A case's other events
|
|
374
374
|
(analyses, articles, films, confessions) are not replayed. See `CaseJson` in `src/engine/persistence/caseJson.ts`:
|
|
375
375
|
|
|
@@ -378,45 +378,45 @@ the case file itself (so the same case works from its dossier's page and from an
|
|
|
378
378
|
"id": "ChilesWhitted",
|
|
379
379
|
"title": "Chiles et Whitted",
|
|
380
380
|
"events": [
|
|
381
|
-
{ "type": "event", "eventType": "sighting", "url": "
|
|
382
|
-
{ "type": "event", "eventType": "sighting", "url": "
|
|
381
|
+
{ "type": "event", "eventType": "sighting", "url": "observer-chiles.json" },
|
|
382
|
+
{ "type": "event", "eventType": "sighting", "url": "observer-whitted.json" }
|
|
383
383
|
]
|
|
384
384
|
}
|
|
385
385
|
```
|
|
386
386
|
|
|
387
387
|
The two shapes are told apart automatically — an object with `events` and no `timeline` is a case, anything else
|
|
388
|
-
one
|
|
389
|
-
naming `case.json`. No labels are duplicated in the case — each
|
|
390
|
-
case id grouping them together are read from that
|
|
388
|
+
one observer's own recording. A bare JSON array (the observer manifest read before cases) is refused with an error
|
|
389
|
+
naming `case.json`. No labels are duplicated in the case — each observer's display name and the shared
|
|
390
|
+
case id grouping them together are read from that observer's *own* file (`observer`/`caseId`, see
|
|
391
391
|
[Data format](#data-format)), so there's a single source of truth and nothing to drift out of sync. This means
|
|
392
|
-
every listed
|
|
393
|
-
scale a case's
|
|
394
|
-
the URL itself as a last resort. A mismatched `caseId` across the listed
|
|
392
|
+
every listed observer's recording is fetched upfront (to read its name), not lazily on selection — fine at the
|
|
393
|
+
scale a case's observer list actually has. If a observer has no `observer.title`, its `observer.id` is shown instead, or
|
|
394
|
+
the URL itself as a last resort. A mismatched `caseId` across the listed observers logs a console warning (doesn't
|
|
395
395
|
block) — likely means unrelated recordings got listed together by mistake.
|
|
396
396
|
|
|
397
|
-
**Where two
|
|
398
|
-
must differ —
|
|
399
|
-
difference that exists in the record and not in the files makes the
|
|
400
|
-
does nothing, and nothing *looks* broken. Chiles-Whitted shipped that way for months: one
|
|
397
|
+
**Where two observers described different things, the recordings have to show it.** Not that they
|
|
398
|
+
must differ — observers often agree, and two matching recordings are then simply true. But a
|
|
399
|
+
difference that exists in the record and not in the files makes the observer picker a control that
|
|
400
|
+
does nothing, and nothing *looks* broken. Chiles-Whitted shipped that way for months: one account
|
|
401
401
|
under two names. Its two pilots drew the object differently for Project Sign — the captain a slim
|
|
402
402
|
ribbed cigar with a pointed nose and no windows, the co-pilot a blunt cylinder with two rows of
|
|
403
403
|
lit windows — and only the co-pilot, in the right seat, saw the terminal phase (McDonald's 1968
|
|
404
|
-
cross-check). Each file now carries its own
|
|
404
|
+
cross-check). Each file now carries its own observer's account.
|
|
405
405
|
|
|
406
406
|
| Member | Kind | Description |
|
|
407
407
|
|---|---|---|
|
|
408
408
|
| `src` | attribute | URL of a single `sighting.json` or a `case.json` (above), fetched automatically on connect and whenever the attribute changes |
|
|
409
|
-
| `
|
|
410
|
-
| `sightingData` | property (get/set) | One
|
|
409
|
+
| `observerUrls` | property (get/set) | The recordings to show as a plain array of URLs, for programmatic use instead of `src` |
|
|
410
|
+
| `sightingData` | property (get/set) | One observer's recording, set directly instead of fetched — for a page holding one in memory (text pasted into a form, a file the reader picked). Its entry carries no URL, so the info panel's editor link and embed lines fall back to the bare application, which is the honest answer for something published nowhere |
|
|
411
411
|
| `scene` | property (readonly) | The `<rr0-scene>` this composes — and through `scene.ufoElement`, the playback members above |
|
|
412
412
|
| `loadFromSrc(url)` | method (async) | What the `src` attribute triggers internally; can be called directly too |
|
|
413
413
|
|
|
414
|
-
A toolbar row sits above the scene: a "
|
|
415
|
-
button on the right. The
|
|
414
|
+
A toolbar row sits above the scene: a "Account by <observer>" sentence on the left, and a round "?" info
|
|
415
|
+
button on the right. The observer portion is plain text for a single observer (a one-option `<select>` would be
|
|
416
416
|
pointless); once there's more than one, it becomes the live `<select>` instead — but the sentence itself, and the
|
|
417
|
-
info button, stay visible either way. The first
|
|
418
|
-
selector loads that
|
|
419
|
-
`
|
|
417
|
+
info button, stay visible either way. The first observer loads automatically once the list is known; switching the
|
|
418
|
+
selector loads that observer's already-fetched recording into the nested `<rr0-scene>` (no re-fetch). Setting
|
|
419
|
+
`observerUrls` again (e.g. a case re-read) keeps the current selection if that observer is still present, instead
|
|
420
420
|
of resetting back to the first.
|
|
421
421
|
|
|
422
422
|
Clicking "?" opens a panel anchored under the button (it never shifts the canvas below it). Where the browser has
|
|
@@ -425,9 +425,9 @@ wrapper cannot clip, which is what rr0.org's layout was doing to it — kept und
|
|
|
425
425
|
positioning, flipping above it or centring in the viewport when that side is too short, and closing on Escape or a
|
|
426
426
|
click outside. Browsers without the API get the plain absolutely-positioned overlay instead.
|
|
427
427
|
|
|
428
|
-
Its main content is the currently-selected
|
|
429
|
-
tags — whichever are actually present in that
|
|
430
|
-
here, since it's already in the toolbar's
|
|
428
|
+
Its main content is the currently-selected observer's observation metadata (date, location, case id, description,
|
|
429
|
+
tags — whichever are actually present in that observer's own `sighting.json`; the observer's own name isn't repeated
|
|
430
|
+
here, since it's already in the toolbar's account line). The date is shown on the OBSERVER's own clock, never
|
|
431
431
|
converted into the reader's time zone (see `utcOffsetHours` in [Data format](#data-format)).
|
|
432
432
|
|
|
433
433
|
A footer row holds the app's own name/version on the left — linking to that very observation in the editor (see
|
|
@@ -439,7 +439,7 @@ fold-outs on the right, both closed until asked for:
|
|
|
439
439
|
|
|
440
440
|
```html
|
|
441
441
|
<script type="module" src="https://ufoathome.org/lib/rr0-sighting.mjs"></script>
|
|
442
|
-
<rr0-sighting src="https://ufoathome.org/demo-data/
|
|
442
|
+
<rr0-sighting src="https://ufoathome.org/demo-data/observer-socorro.json"></rr0-sighting>
|
|
443
443
|
```
|
|
444
444
|
|
|
445
445
|
The script URL is derived from where the running bundle was itself loaded from (`import.meta.url`), never
|
|
@@ -452,7 +452,7 @@ fold-outs on the right, both closed until asked for:
|
|
|
452
452
|
- **Credits** reveals third-party credits (the live terrain imagery attribution, once a real relief patch has
|
|
453
453
|
resolved, plus the bundled thunder sound's own required attribution — see [`CREDITS.md`](CREDITS.md)).
|
|
454
454
|
|
|
455
|
-
All of this component's own labels (
|
|
455
|
+
All of this component's own labels (Account by, About, Close, Observation/Date/Location/Case, Credits) are
|
|
456
456
|
translated (English/French) the same way as the playback layer's own labels.
|
|
457
457
|
|
|
458
458
|
## Data format
|
|
@@ -465,10 +465,10 @@ interface SightingRecordingJson {
|
|
|
465
465
|
time?: { year?: number, month?: number, day?: number, hour?: number, minute?: number, second?: number }
|
|
466
466
|
endTime?: { year?: number, month?: number, day?: number, hour?: number, minute?: number, second?: number } // alternative to durationSeconds
|
|
467
467
|
durationSeconds?: number // alternative to endTime; takes precedence if both are set
|
|
468
|
-
utcOffsetHours?: number // the LEGAL time zone the
|
|
468
|
+
utcOffsetHours?: number // the LEGAL time zone the observer's clock was on (+1 for France in 1965, -7 for New Mexico in April 1964). Absent = approximated from the longitude, which cannot know legal time or a daylight-saving switch
|
|
469
469
|
place?: { lat: number, lng: number, name?: string }[] // `name` is the fully qualified place name the coordinates were resolved from — see Naming a place
|
|
470
|
-
|
|
471
|
-
caseId?: string // shared by every
|
|
470
|
+
observer?: { id?: string, dirName?: string, title?: string, lastName?: string, firstNames?: string[] } // every field optional — supply whichever is known; omit entirely for an anonymous observer
|
|
471
|
+
caseId?: string // shared by every observer's own sighting.json for the same case — see <rr0-sighting>
|
|
472
472
|
description?: string
|
|
473
473
|
tags?: string[]
|
|
474
474
|
timeline: {
|
|
@@ -485,7 +485,7 @@ interface SightingRecordingJson {
|
|
|
485
485
|
haloScale: number // 0 = no glow
|
|
486
486
|
selected: boolean
|
|
487
487
|
title?: string // shown as an on-canvas tooltip when hovered
|
|
488
|
-
angular?: { widthDeg: number, heightDeg: number } // how big it LOOKED — the only size a
|
|
488
|
+
angular?: { widthDeg: number, heightDeg: number } // how big it LOOKED — the only size a account holds, see Apparent size
|
|
489
489
|
points?: { x: number, y: number }[] // "polygon" shapes only
|
|
490
490
|
}
|
|
491
491
|
}>
|
|
@@ -493,13 +493,13 @@ interface SightingRecordingJson {
|
|
|
493
493
|
order?: string[] // back-to-front paint/hit-test order; absent = first-appearance order
|
|
494
494
|
groups?: string[][] // each inner array is one group's member sourceIds
|
|
495
495
|
}
|
|
496
|
-
|
|
496
|
+
observerTrack?: { keyframes: Array<{ t: number, pose: { lat?: number, lng?: number, elevationM: number, headingDeg?: number, pitchDeg: number, fovDeg: number } }> }
|
|
497
497
|
weatherTrack?: { keyframes: Array<{ t: number, weather: Weather }> }
|
|
498
498
|
weather?: Weather // legacy static fallback for recordings predating weatherTrack
|
|
499
|
-
weatherSource?: { id: string, name: string, url: string } // the meteorological record weatherTrack was looked up from — see Weather is looked up, not remembered. Absent = the
|
|
499
|
+
weatherSource?: { id: string, name: string, url: string } // the meteorological record weatherTrack was looked up from — see Weather is looked up, not remembered. Absent = the observer's own account
|
|
500
500
|
instrument?: "eye" | "rectilinear-lens" // what it was observed THROUGH — see Instrument. Absent = the naked eye
|
|
501
|
-
soundTrack?: { keyframes: Array<{ t: number, sound: { kind: "none" | "hum" | "whistle" | "rumble" | "crackle", volume: number, pitchHz: number, src?: string } }> } // what the
|
|
502
|
-
decor?: DecorObject[] // buildings, trees, streetlights, vehicles, other
|
|
501
|
+
soundTrack?: { keyframes: Array<{ t: number, sound: { kind: "none" | "hum" | "whistle" | "rumble" | "crackle", volume: number, pitchHz: number, src?: string } }> } // what the observer heard — see What it sounded like
|
|
502
|
+
decor?: DecorObject[] // buildings, trees, streetlights, vehicles, other observers — see src/engine/model/Decor.ts
|
|
503
503
|
references?: SceneReference[] // pictures of the place laid over the scene, each at a registered heading/pitch/roll/field — see Pictures of the place
|
|
504
504
|
}
|
|
505
505
|
```
|
|
@@ -515,12 +515,12 @@ same clock as the shapes, because a sound rarely starts when the object does: a
|
|
|
515
515
|
ground and heard only as it lifts off is two keyframes, `kind: "none"` at the start and a hum at the instant it
|
|
516
516
|
took off.
|
|
517
517
|
|
|
518
|
-
`volume` (0..1, how loud the
|
|
518
|
+
`volume` (0..1, how loud the observer could describe it, never a dB figure) and `pitchHz` blend between keyframes;
|
|
519
519
|
`kind` and `src` are **held**, like every other discrete field in this format — so the example above really is
|
|
520
520
|
silent right up to that second keyframe. To record a sound emerging gradually instead, give it two keyframes of
|
|
521
521
|
its own kind (hum at volume 0, then hum at full).
|
|
522
522
|
|
|
523
|
-
`kind: "none"` is a statement — the
|
|
523
|
+
`kind: "none"` is a statement — the observer reported hearing nothing. A recording with no `soundTrack` at all is
|
|
524
524
|
the different, weaker case: nobody was asked. Both replay as silence, and neither invents a noise.
|
|
525
525
|
|
|
526
526
|
Sounds are **synthesized** from that description (a drone, a whistle, a rumble, a crackle — `pitchHz` is the tone
|
|
@@ -535,7 +535,7 @@ audio without one.
|
|
|
535
535
|
The same rule governs the whole scene, not just the object's own sound: **paused is paused**. Falling
|
|
536
536
|
precipitation and its splashes, twinkling stars, lightning flashes, the sun's lens flare and the weather's own
|
|
537
537
|
ambient beds all stop with the player and resume with it, leaving the frozen frame on screen. A paused replay is
|
|
538
|
-
one instant of a sighting — weather still going on over it would be the reader's own room, not the
|
|
538
|
+
one instant of a sighting — weather still going on over it would be the reader's own room, not the observer's
|
|
539
539
|
evening. Clouds likewise use the recording's timeline: wind advection stops on pause and is
|
|
540
540
|
recomputed deterministically when seeking.
|
|
541
541
|
|
|
@@ -545,7 +545,7 @@ Everything the scene draws is computed, and a reader has no way to tell a faithf
|
|
|
545
545
|
one. A photograph of the same place does: `references` lays pictures over the render, each at the direction it was
|
|
546
546
|
registered in (`registration`: heading, pitch, roll and vertical field, angles and nothing else), at an opacity the
|
|
547
547
|
reader varies with the slider beside the 🖼 button — all picture, all render, and every step between, with the
|
|
548
|
-
|
|
548
|
+
observer's own phenomenon drawn over both. A `photo` is a flat panel at its lens's field, a `panorama` an
|
|
549
549
|
equirectangular sphere. `src` is an address (rr0.org's case pictures are served to any origin; the bytes must be, since
|
|
550
550
|
WebGL draws them) or the picture itself as a `data:` URL when it was added from a disk, which keeps the recording
|
|
551
551
|
self-contained at the price of its size. `credit` and `creditUrl` are shown in the info panel's credits, `t` says it
|
|
@@ -553,30 +553,30 @@ was taken during the observation at that instant, and `drawing` that somebody dr
|
|
|
553
553
|
spot carries the sphere and its spiral by hand.
|
|
554
554
|
|
|
555
555
|
Nothing in the scene hides a picture and a picture hides nothing: it is a field of directions from one point, valid
|
|
556
|
-
from that point alone, and the reconstruction stands it at the
|
|
556
|
+
from that point alone, and the reconstruction stands it at the observer's eye.
|
|
557
557
|
|
|
558
|
-
**Lining it up.** The editor's Pictures group types the registration, copies the
|
|
558
|
+
**Lining it up.** The editor's Pictures group types the registration, copies the observer's pose into it, and while that
|
|
559
559
|
group is open the canvas belongs to the selected picture: a drag turns it, the wheel changes its field, and *Add a
|
|
560
560
|
landmark* arms two clicks that name a detail on the picture, then the same detail in the render. Landmarks are kept in
|
|
561
561
|
the recording with a name ("Arbre masquant", "barrière") and listed with how far each still is from fitting; either end
|
|
562
562
|
of one can be dragged, and the selected one is bolder on the canvas — two landmarks turn the picture to fit them, three
|
|
563
563
|
or more fit its field too (`PictureRegistration`, a triad first guess refined by Gauss-Newton on the rotation), and the
|
|
564
|
-
status line says how far off the landmarks still are. *Adopt as the
|
|
564
|
+
status line says how far off the landmarks still are. *Adopt as the observer's pose* then writes the fitted heading,
|
|
565
565
|
pitch and roll into the pose at the playhead as a **measurement**, with a `derived` provenance naming the picture and
|
|
566
|
-
the residual — a heading read off a picture that fits the relief, where the one typed in the
|
|
567
|
-
|
|
568
|
-
pictures taken within 300 m of the
|
|
566
|
+
the residual — a heading read off a picture that fits the relief, where the one typed in the Observer group is the
|
|
567
|
+
observer's word. *Street-level pictures nearby* asks Panoramax (open imagery, CC BY-SA, served to any origin) for
|
|
568
|
+
pictures taken within 300 m of the observer's spot; each comes with where it was taken from and which way it looked, so
|
|
569
569
|
it arrives registered in heading, a full turn arriving as a panorama.
|
|
570
570
|
|
|
571
571
|
### Naming a place
|
|
572
572
|
|
|
573
|
-
|
|
573
|
+
Account names a place. It says "on the Valensole plateau", "near Socorro", "over Montgomery" —
|
|
574
574
|
never 43.8379 / 5.9840. So the Location group leads with a **Place** field: type a name, press Enter
|
|
575
575
|
(or **Locate**), and the latitude and longitude below are filled from
|
|
576
576
|
[Nominatim](https://nominatim.openstreetmap.org/), OpenStreetMap's own geocoder — which, unlike the
|
|
577
577
|
gazetteer-style services, knows the hamlets, farms and airfields that cases actually happen at.
|
|
578
578
|
A name is often ambiguous, so every candidate stays listed in **Matches** and the best one is
|
|
579
|
-
applied straight away; picking another moves the
|
|
579
|
+
applied straight away; picking another moves the observer. Results are named in the reader's own
|
|
580
580
|
language, and credited as their licence requires.
|
|
581
581
|
|
|
582
582
|
What gets stored is the *qualified* name the search resolved (`place[].name`), not the two words
|
|
@@ -587,9 +587,9 @@ recording can still say "the lavender field east of the farm" beside coordinates
|
|
|
587
587
|
The field reads both ways: move the latitude or longitude by hand and a resolved name is re-derived
|
|
588
588
|
from the new coordinates, or cleared if there is no place there. A name left describing somewhere
|
|
589
589
|
the sighting is no longer at is worse than no name — the recording would state, in writing, that it
|
|
590
|
-
happened there. A name the
|
|
590
|
+
happened there. A name the observer typed themselves is never replaced.
|
|
591
591
|
|
|
592
|
-
**Altitude is above sea level**, and the ground at the location sets its floor: a
|
|
592
|
+
**Altitude is above sea level**, and the ground at the location sets its floor: a observer in the
|
|
593
593
|
Alps is not at 0 m, and an editor that offers it invites a recording that says so. The ground's own
|
|
594
594
|
height is read from whichever elevation source is live and shown beside the field. What gets stored
|
|
595
595
|
is unchanged — `ObserverPose.elevationM` stays a height above the local ground, which is what the
|
|
@@ -598,7 +598,7 @@ terrain patch is built around.
|
|
|
598
598
|
### Time zones
|
|
599
599
|
|
|
600
600
|
An hour of `utcOffsetHours` is an hour of Earth's rotation *and* a different row of the weather
|
|
601
|
-
record. Pick the
|
|
601
|
+
record. Pick the observer's own zone (`Europe/Paris`, `America/Denver`, …) and the offset is derived
|
|
602
602
|
from that zone's rules **at the observation's date**: Valensole in July 1965 resolves to UTC+1, not
|
|
603
603
|
the UTC+2 the same place gives today — France only reintroduced summer time in 1976. Change the date
|
|
604
604
|
and it is derived again. The recording stores both: `timeZone` is the rule, `utcOffsetHours` is the
|
|
@@ -606,7 +606,7 @@ number it produced, and every consumer keeps reading only the number.
|
|
|
606
606
|
|
|
607
607
|
The rules come from the platform's own IANA database. What it cannot fix is a zone whose
|
|
608
608
|
*boundaries* are coarse: Montgomery, Alabama is `America/Chicago`, which observed summer time in
|
|
609
|
-
1948 while Alabama did not. That is why the zone is chosen by the
|
|
609
|
+
1948 while Alabama did not. That is why the zone is chosen by the observer rather than derived from
|
|
610
610
|
the coordinates — and why the plain entered offset remains available for exactly those cases.
|
|
611
611
|
|
|
612
612
|
The search runs only when asked, never per keystroke: Nominatim's usage policy allows the first and
|
|
@@ -638,14 +638,14 @@ the declared longitude is flagged on the field, with the meridian's own solar ti
|
|
|
638
638
|
Deliberately a wide net rather than a precise one. Legal time genuinely departs from solar time,
|
|
639
639
|
sometimes by hours (all of China runs on UTC+8), and the historical rules are worse — the check must
|
|
640
640
|
never cry wolf at a correct "France on UTC+1 in 1965". It flags only what no country has ever done,
|
|
641
|
-
and only as a warning: the recording states the
|
|
642
|
-
the
|
|
641
|
+
and only as a warning: the recording states the observer's clock, and nothing here knows better than
|
|
642
|
+
the observer.
|
|
643
643
|
|
|
644
644
|
### Weather is looked up, not remembered
|
|
645
645
|
|
|
646
|
-
The Circumstances group is the one part of this editor that isn't
|
|
646
|
+
The Circumstances group is the one part of this editor that isn't account. Weather is a
|
|
647
647
|
measurable fact about a place at an instant, and the recording already states both — so instead of
|
|
648
|
-
leaving a
|
|
648
|
+
leaving a observer (or an author reconstructing a case decades later) to set a cloud-cover slider
|
|
649
649
|
from memory, the editor looks the conditions up from [ERA5](https://open-meteo.com/en/docs/historical-weather-api),
|
|
650
650
|
the ECMWF reanalysis, hourly and worldwide from 1940 on. The fields then show the record's own
|
|
651
651
|
values, **read-only**, above a line naming the dataset and the exact UTC instant they describe (a
|
|
@@ -657,7 +657,7 @@ estimate weighted by cloud level, rain and thunderstorm. A low `baseM` starts fr
|
|
|
657
657
|
temperature/dew-point spread. Both derivations are documented in
|
|
658
658
|
`src/engine/weather/providers/OpenMeteoWeatherProvider.ts`; crystal alignment is never inferred.
|
|
659
659
|
|
|
660
|
-
Unchecking **From weather records** hands the fields back to the
|
|
660
|
+
Unchecking **From weather records** hands the fields back to the observer: the looked-up values stay
|
|
661
661
|
as a starting point, `weatherSource` is dropped, and no later lookup may overwrite them — the same
|
|
662
662
|
"declared outranks deduced" rule [Behind a cloud](#behind-a-cloud) follows. A recording that names
|
|
663
663
|
a `weatherSource` is replayed exactly as authored and never looked up again, so a published case
|
|
@@ -678,11 +678,11 @@ first recorded shape turns a fifteen-hour span into a few seconds, so the track
|
|
|
678
678
|
whenever it does. Getting either half wrong looks the same from outside — a sighting whose weather
|
|
679
679
|
never changes.
|
|
680
680
|
|
|
681
|
-
It follows the
|
|
681
|
+
It follows the observer too, not just the clock. Half of aviation account is given from a cockpit,
|
|
682
682
|
and an aircraft under observation for an hour is a long way from where it started — so each sample
|
|
683
|
-
is looked up at the position the `
|
|
683
|
+
is looked up at the position the `observerTrack` puts the observer at that instant. Positions inside
|
|
684
684
|
one ERA5 grid cell (~28 km) are one query, and several cells still travel in a single request, so
|
|
685
|
-
a stationary
|
|
685
|
+
a stationary observer costs exactly what it always did.
|
|
686
686
|
|
|
687
687
|
`scripts/infer-case-weather.ts` runs the same lookup over case files on disk:
|
|
688
688
|
|
|
@@ -695,7 +695,7 @@ diff shows the weather and nothing else.
|
|
|
695
695
|
|
|
696
696
|
### Apparent size — and why there is no real one
|
|
697
697
|
|
|
698
|
-
A
|
|
698
|
+
A observer never perceives meters. They perceive an angle: the thing covered a thumbnail at arm's length, or a fifth
|
|
699
699
|
of the windshield, or two full Moons. "About thirty meters long" is a conclusion they drew from a distance they
|
|
700
700
|
could not perceive either, and the two errors multiply. So a recording stores `angular` — how wide and how tall the
|
|
701
701
|
object *looked*, in degrees — and stores no real size and no real distance anywhere.
|
|
@@ -714,7 +714,7 @@ one of the other two follows, and the one the **Hold** select pins never moves.
|
|
|
714
714
|
is what a thing keeps: moving it further makes it look smaller, and editing either width moves the other at the
|
|
715
715
|
distance it stands (the shape is resized on the canvas). Hold the apparent width instead and a distance edit
|
|
716
716
|
leaves the shape looking the same, stood elsewhere — which is what asking what the decor would hide of it needs.
|
|
717
|
-
Only the angle is kept; the metres are an authoring aid, never
|
|
717
|
+
Only the angle is kept; the metres are an authoring aid, never account, and the distance is where the scene
|
|
718
718
|
*draws* the shape (see *Where the shape is drawn*).
|
|
719
719
|
|
|
720
720
|
### Decor that moves, and lights that blink
|
|
@@ -752,14 +752,14 @@ dot from the next rather than dotting the line at the sampler's own rate). A lam
|
|
|
752
752
|
candela (a wingtip strobe really is some twenty times one) — because a pose spreads a lamp's light over hundreds
|
|
753
753
|
of pixels, and at white a whole aircraft trail came out at a thousandth of white, i.e. invisible.
|
|
754
754
|
|
|
755
|
-
An aircraft in a scene is a **hypothesis**, not
|
|
755
|
+
An aircraft in a scene is a **hypothesis**, not account — "here is what a flight at that altitude and heading
|
|
756
756
|
would have looked like" — and belongs to the decor for that reason, next to the buildings and trees whose
|
|
757
757
|
positions are likewise known rather than reported.
|
|
758
758
|
|
|
759
759
|
### How big it was, and what it looked like
|
|
760
760
|
|
|
761
761
|
`DecorObject` used to have no size at all. Every building was a six-metre cube per storey, every vehicle the same
|
|
762
|
-
1.8 × 4.35 × 2.0 box, whatever the
|
|
762
|
+
1.8 × 4.35 × 2.0 box, whatever the account said. In a project that will not store an angle nobody perceived (see
|
|
763
763
|
*Apparent size*, above), that was the last place a number was invented rather than stated: Zamora's Pontiac and a
|
|
764
764
|
delivery van were the same object, and "the dynamite shack" was a warehouse.
|
|
765
765
|
|
|
@@ -807,10 +807,10 @@ and it is worth making: a car-shaped silhouette at forty metres in evening light
|
|
|
807
807
|
prism reads as a building, which is where this started. Socorro's own patrol car uses one of them, at the real
|
|
808
808
|
Pontiac's dimensions and with the substitution stated in the recording's description.
|
|
809
809
|
|
|
810
|
-
One thing a model does NOT yet do is let the
|
|
810
|
+
One thing a model does NOT yet do is let the observer look out from inside it. A recording can place them inside a
|
|
811
811
|
building or a vehicle, and what they then look at is the room the built-in shape builds around them — walls, window
|
|
812
812
|
openings sized from the data, the pillar between two door windows. A downloaded model is a hull with none of that, so
|
|
813
|
-
while the
|
|
813
|
+
while the observer is inside an object the built-in shape is kept. Looking out through a real model is the objective
|
|
814
814
|
(it is what makes "how much of the sky did the windscreen pillar hide?" answerable) and needs models with interiors,
|
|
815
815
|
glazing made genuinely transparent, and the viewpoint taken from the model rather than from the primitive's seat.
|
|
816
816
|
|
|
@@ -881,7 +881,7 @@ on each pose is read back through the first pose that stated one.
|
|
|
881
881
|
A pose is drawn in two moves, because a sky costs about 8 ms to rebuild and a five-minute pose is 37 of them: the
|
|
882
882
|
**viewfinder** immediately (one instant, a twentieth of a millisecond) and the **photograph** as the scene settles,
|
|
883
883
|
a dozen milliseconds of instants per animation frame, shown as the film fills and gained up to stay properly
|
|
884
|
-
exposed — so it goes from beady to smooth rather than from black to bright. Anything the
|
|
884
|
+
exposed — so it goes from beady to smooth rather than from black to bright. Anything the observer does interrupts it
|
|
885
885
|
and starts it again, which is why the editor stays answerable while a long pose is on. Doing it all inside one call
|
|
886
886
|
is what made it crawl: the dozen setters a single tick touches would each rebuild the whole pose, and the per-frame
|
|
887
887
|
animation loop (twinkle, rain, lightning — none of which survives a pose of minutes anyway) restarted it sixty
|
|
@@ -913,7 +913,7 @@ Two consequences of drawing a pose rather than an instant, both of which cost an
|
|
|
913
913
|
|
|
914
914
|
#### Where meters do come back
|
|
915
915
|
|
|
916
|
-
The only real distance a
|
|
916
|
+
The only real distance a account can support is an **inequality**, and only where the observer saw the object
|
|
917
917
|
cross something whose position is known: it passed *behind* that hangar (at least that far), or *in front of* that
|
|
918
918
|
tree (at most that far). `DecorObject.eastM/northM` give the decor its real position, `occludesSourceIds` says
|
|
919
919
|
which side of it the object was on, and `SceneRenderer.decorDistancesAt` raycasts the exact line of sight to
|
|
@@ -926,11 +926,11 @@ rather than clamping one, and the editor prints the result under the apparent si
|
|
|
926
926
|
|
|
927
927
|
#### Where the shape is drawn
|
|
928
928
|
|
|
929
|
-
A plane facing the
|
|
929
|
+
A plane facing the observer, scaled to the stated angle, looks the same from their eye at any distance — so the
|
|
930
930
|
scene has to put it *somewhere*, and where is a parameter of the picture, never a fact the recording states.
|
|
931
931
|
`PhenomenonDepth` (`src/engine/shape/PhenomenonDepth.ts`) is the one place that decides it, from five sources in
|
|
932
932
|
the order they outrank each other: a distance the recording **states** (none does yet — the tier exists for the
|
|
933
|
-
day a close encounter is written as a body in metres); a **hypothesis** the reader is trying; what the
|
|
933
|
+
day a close encounter is written as a body in metres); a **hypothesis** the reader is trying; what the observer's
|
|
934
934
|
own walk **derives** (`ShapeDistance`, see below); what the crossings **bound**, the geometric middle of the
|
|
935
935
|
interval they leave, since any distance in it draws every crossing correctly; and, for the majority that
|
|
936
936
|
establishes nothing, a **conventional** few metres — in front of everything that was not declared to hide it,
|
|
@@ -972,7 +972,7 @@ case's `sighting.json` from its `RR0Event`).
|
|
|
972
972
|
the editor reaches through to its public `ufoElement` property for the actual canvas/timeline/appearance work
|
|
973
973
|
(the toolbar edits the exact same `Sighting` instance the nested scene renders from, so an observer/time/
|
|
974
974
|
appearance change needs no separate sync step to reach the sky), while `SightingElement` reaches through to
|
|
975
|
-
its public `sightingData`/`currentTerrainAttribution` for its own toolbar (
|
|
975
|
+
its public `sightingData`/`currentTerrainAttribution` for its own toolbar (observer picker) and info panel.
|
|
976
976
|
- Playback linearly interpolates shapes between a source's surrounding keyframes for smooth motion
|
|
977
977
|
(`Timeline.getInterpolatedShapeAt`/`Shape.lerpShape`), holding at the ends of its recorded range.
|
|
978
978
|
- Recording samples the pointer position at a configurable rate via `requestAnimationFrame`, not on every
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e={play:"Lecture",pause:"Pause",noDuration:"Aucune durée d'observation",currentPosition:"Position actuelle",duration:"Durée",switchToElapsed:"cliquer pour afficher la durée écoulée",switchToClockTime:"cliquer pour afficher l'heure de l'observation",fullscreen:"Plein écran",exitFullscreen:"Quitter le plein écran",showObserverMap:"Voir où était l'observateur",hideObserverMap:"Masquer où était l'observateur",mapImageryUnavailable:"Vue aérienne indisponible",showReferences:"Voir les photos des lieux",hideReferences:"Masquer les photos des lieux",referenceOpacity:"Opacité des photos",showMilestones:"Voir les moments du récit",hideMilestones:"Masquer les moments du récit",observerHere:"L'observateur, ici",decorHere:"Élément de décor"};export{e as ufoMessages_fr};
|