mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
Merge branch 'main' into docs/qa-gate-npm-alias-list
This commit is contained in:
commit
7a63afe659
6 files changed
+310
-9
No files matched your search
@@ -0,0 +1,4 @@
|
||||
- Keep missing launch-pad coordinates unknown so launches use the location
|
||||
fallback or stay off the map instead of appearing at 0°N 0°E. Missing stage
|
||||
flight numbers no longer show "FLIGHT 0"
|
||||
(Rudy Celekli, @rudycelekli, #1186).
|
||||
@@ -791,6 +791,11 @@ and `./layers/launches`. Each owns its catalog, render state, tracking, interact
|
||||
and teardown. Source adapters retain the existing CelesTrak and Launch Library
|
||||
endpoints; standalone entries provide scene services and the satellite dependency.
|
||||
The mission factory separates paths, replay, camera, panel, overlays and placement.
|
||||
Launch Library pad coordinates retain unknown values: missing fields use the
|
||||
reported location coordinates when available, and launches without a known pad
|
||||
position stay off the map. Genuine zero coordinates remain valid. Undisclosed
|
||||
stage coordinates, downrange distance and flight numbers remain unknown; the
|
||||
mission panel omits the flight-number label when no number was reported.
|
||||
Pending mission requests abort on disable/destruction and malformed launch
|
||||
snapshots preserve the last accepted display. Catalog groups, propagation cadence,
|
||||
tracking intent, replay timing, controls and source attribution remain unchanged.
|
||||
|
||||
@@ -732,6 +732,7 @@
|
||||
"src/voice/session.test.mjs",
|
||||
"src/layers/alpr/records.js",
|
||||
"src/layers/earthquakes/records.js",
|
||||
"src/layers/earthquakes/records.test.mjs",
|
||||
"src/sources/sourceSlot.js",
|
||||
"src/data/feedState.js",
|
||||
"src/app/localGeojsonServices.js",
|
||||
|
||||
@@ -677,6 +677,37 @@ test('uses Launch Library 2 pad latitude/longitude fields', () => {
|
||||
assert.equal(launches[0].lon, -120.611);
|
||||
});
|
||||
|
||||
test('null or blank pad coordinates stay unknown instead of becoming 0', () => {
|
||||
const base = { net: '2026-07-20T10:00:00Z' };
|
||||
for (const blank of [null, '', ' ', false]) {
|
||||
assert.deepEqual(
|
||||
normalizeRocketLaunches([{ ...base, id: 'blank', pad: { latitude: blank, longitude: blank } }], NOW),
|
||||
[],
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('null pad coordinates use the location fallback instead of 0,0', () => {
|
||||
const base = { net: '2026-07-20T10:00:00Z' };
|
||||
const [fallback] = normalizeRocketLaunches([{ ...base, id: 'fallback',
|
||||
pad: { latitude: null, longitude: null, location: { coordinates: '-80.6,28.6' } } }], NOW);
|
||||
assert.equal(fallback.lat, 28.6);
|
||||
assert.equal(fallback.lon, -80.6);
|
||||
});
|
||||
|
||||
test('an undisclosed landing keeps top-level position and stage numbers unknown', () => {
|
||||
const base = { net: '2026-07-20T10:00:00Z' };
|
||||
const [launch] = normalizeRocketLaunches([{ ...base, id: 'landing', pad: { latitude: 1, longitude: 2 },
|
||||
rocket: { launcher_stage: [{ launcher_flight_number: null,
|
||||
landing: { attempt: true, latitude: null, longitude: null, downrange_distance: null,
|
||||
landing_location: { latitude: null, longitude: null } } }] } }], NOW);
|
||||
const [stage] = launch.recoveryStages;
|
||||
assert.equal(stage.lat, null);
|
||||
assert.equal(stage.lon, null);
|
||||
assert.equal(stage.downrangeKm, null);
|
||||
assert.equal(stage.flightNumber, null);
|
||||
});
|
||||
|
||||
test('normalizes detailed payload and stage recovery records', () => {
|
||||
const launches = normalizeRocketLaunches({ results: [{
|
||||
id: 'recovery-details',
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { normalizeEarthquakeSnapshot } from './records.js';
|
||||
|
||||
test('normalizeEarthquakeSnapshot parses valid GeoJSON earthquake features', () => {
|
||||
const geojson = {
|
||||
type: 'FeatureCollection',
|
||||
features: [
|
||||
{
|
||||
id: 'us7000m123',
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [-122.4194, 37.7749, 10.5],
|
||||
},
|
||||
properties: {
|
||||
mag: 4.2,
|
||||
place: 'San Francisco Bay Area, CA',
|
||||
time: 1711929600000,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const rows = normalizeEarthquakeSnapshot(geojson);
|
||||
assert.equal(Array.isArray(rows), true);
|
||||
assert.equal(rows.length, 1);
|
||||
assert.deepEqual(rows[0], {
|
||||
stableId: 'us7000m123',
|
||||
usgsId: 'us7000m123',
|
||||
lon: -122.4194,
|
||||
lat: 37.7749,
|
||||
depthKm: 10.5,
|
||||
mag: 4.2,
|
||||
place: 'San Francisco Bay Area, CA',
|
||||
time: 1711929600000,
|
||||
});
|
||||
});
|
||||
|
||||
test('normalizeEarthquakeSnapshot generates index-based stable IDs when id is missing or blank', () => {
|
||||
const geojson = {
|
||||
features: [
|
||||
{
|
||||
id: null,
|
||||
geometry: { type: 'Point', coordinates: [139.6917, 35.6895, 25.0] },
|
||||
properties: { mag: 3.5, place: 'Tokyo, Japan', time: 1711930000000 },
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
geometry: { type: 'Point', coordinates: [-70.6693, -33.4489, 45.2] },
|
||||
properties: { mag: 5.1, place: 'Santiago, Chile', time: 1711931000000 },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const rows = normalizeEarthquakeSnapshot(geojson);
|
||||
assert.equal(rows.length, 2);
|
||||
assert.equal(rows[0].stableId, 'event-1');
|
||||
assert.equal(rows[0].usgsId, null);
|
||||
assert.equal(rows[1].stableId, 'event-2');
|
||||
assert.equal(rows[1].usgsId, '');
|
||||
});
|
||||
|
||||
test('normalizeEarthquakeSnapshot filters events below M2.5 without dropping the feed', () => {
|
||||
const geojson = {
|
||||
features: [
|
||||
{
|
||||
id: 'ev-sub',
|
||||
geometry: { type: 'Point', coordinates: [10, 20, 5] },
|
||||
properties: { mag: 2.49, place: 'Minor tremor' },
|
||||
},
|
||||
{
|
||||
id: 'ev-null-mag',
|
||||
geometry: { type: 'Point', coordinates: [11, 21, 5] },
|
||||
properties: { mag: null, place: 'Uncalculated' },
|
||||
},
|
||||
{
|
||||
id: 'ev-threshold',
|
||||
geometry: { type: 'Point', coordinates: [12, 22, 5] },
|
||||
properties: { mag: 2.5, place: 'Threshold event' },
|
||||
},
|
||||
{
|
||||
id: 'ev-strong',
|
||||
geometry: { type: 'Point', coordinates: [13, 23, 5] },
|
||||
properties: { mag: 6.8, place: 'Major event' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const rows = normalizeEarthquakeSnapshot(geojson);
|
||||
assert.equal(rows.length, 2);
|
||||
assert.equal(rows[0].stableId, 'ev-threshold');
|
||||
assert.equal(rows[0].mag, 2.5);
|
||||
assert.equal(rows[1].stableId, 'ev-strong');
|
||||
assert.equal(rows[1].mag, 6.8);
|
||||
});
|
||||
|
||||
test('normalizeEarthquakeSnapshot normalizes optional fields to null when omitted or invalid', () => {
|
||||
const geojson = {
|
||||
features: [
|
||||
{
|
||||
id: 'ev-sparse',
|
||||
geometry: { coordinates: [-100, 40] }, // depth omitted
|
||||
properties: { mag: 3.0, place: 12345, time: 'invalid' }, // place not string, time not number
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const rows = normalizeEarthquakeSnapshot(geojson);
|
||||
assert.equal(rows.length, 1);
|
||||
assert.equal(rows[0].depthKm, null);
|
||||
assert.equal(rows[0].place, null);
|
||||
assert.equal(rows[0].time, null);
|
||||
});
|
||||
|
||||
test('normalizeEarthquakeSnapshot returns null for malformed structures or non-array features', () => {
|
||||
assert.equal(normalizeEarthquakeSnapshot(null), null);
|
||||
assert.equal(normalizeEarthquakeSnapshot(undefined), null);
|
||||
assert.equal(normalizeEarthquakeSnapshot({}), null);
|
||||
assert.equal(normalizeEarthquakeSnapshot({ features: 'not-an-array' }), null);
|
||||
assert.equal(normalizeEarthquakeSnapshot({ features: null }), null);
|
||||
});
|
||||
|
||||
test('normalizeEarthquakeSnapshot returns null for invalid geometries or out-of-range coordinates', () => {
|
||||
const baseFeature = {
|
||||
id: 'test',
|
||||
geometry: { type: 'Point', coordinates: [0, 0, 0] },
|
||||
properties: { mag: 3.0 },
|
||||
};
|
||||
|
||||
// Non-Point geometry
|
||||
assert.equal(
|
||||
normalizeEarthquakeSnapshot({
|
||||
features: [
|
||||
{
|
||||
...baseFeature,
|
||||
geometry: {
|
||||
type: 'LineString',
|
||||
coordinates: [
|
||||
[0, 0],
|
||||
[1, 1],
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
null,
|
||||
);
|
||||
|
||||
// Missing or short coordinates
|
||||
assert.equal(
|
||||
normalizeEarthquakeSnapshot({
|
||||
features: [
|
||||
{ ...baseFeature, geometry: { type: 'Point', coordinates: [0] } },
|
||||
],
|
||||
}),
|
||||
null,
|
||||
);
|
||||
|
||||
// Longitude out of range [-180, 180]
|
||||
assert.equal(
|
||||
normalizeEarthquakeSnapshot({
|
||||
features: [
|
||||
{
|
||||
...baseFeature,
|
||||
geometry: { type: 'Point', coordinates: [180.1, 0, 0] },
|
||||
},
|
||||
],
|
||||
}),
|
||||
null,
|
||||
);
|
||||
assert.equal(
|
||||
normalizeEarthquakeSnapshot({
|
||||
features: [
|
||||
{
|
||||
...baseFeature,
|
||||
geometry: { type: 'Point', coordinates: [-180.1, 0, 0] },
|
||||
},
|
||||
],
|
||||
}),
|
||||
null,
|
||||
);
|
||||
|
||||
// Latitude out of range [-90, 90]
|
||||
assert.equal(
|
||||
normalizeEarthquakeSnapshot({
|
||||
features: [
|
||||
{
|
||||
...baseFeature,
|
||||
geometry: { type: 'Point', coordinates: [0, 90.1, 0] },
|
||||
},
|
||||
],
|
||||
}),
|
||||
null,
|
||||
);
|
||||
assert.equal(
|
||||
normalizeEarthquakeSnapshot({
|
||||
features: [
|
||||
{
|
||||
...baseFeature,
|
||||
geometry: { type: 'Point', coordinates: [0, -90.1, 0] },
|
||||
},
|
||||
],
|
||||
}),
|
||||
null,
|
||||
);
|
||||
|
||||
// Non-finite depth
|
||||
assert.equal(
|
||||
normalizeEarthquakeSnapshot({
|
||||
features: [
|
||||
{
|
||||
...baseFeature,
|
||||
geometry: { type: 'Point', coordinates: [0, 0, NaN] },
|
||||
},
|
||||
],
|
||||
}),
|
||||
null,
|
||||
);
|
||||
|
||||
// Magnitude > 10
|
||||
assert.equal(
|
||||
normalizeEarthquakeSnapshot({
|
||||
features: [{ ...baseFeature, properties: { mag: 10.1 } }],
|
||||
}),
|
||||
null,
|
||||
);
|
||||
|
||||
// Non-object or array properties
|
||||
assert.equal(
|
||||
normalizeEarthquakeSnapshot({
|
||||
features: [{ ...baseFeature, properties: 'invalid' }],
|
||||
}),
|
||||
null,
|
||||
);
|
||||
assert.equal(
|
||||
normalizeEarthquakeSnapshot({
|
||||
features: [{ ...baseFeature, properties: [] }],
|
||||
}),
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
test('normalizeEarthquakeSnapshot returns null when feed contains duplicate IDs', () => {
|
||||
const geojson = {
|
||||
features: [
|
||||
{
|
||||
id: 'duplicate-id',
|
||||
geometry: { type: 'Point', coordinates: [0, 0, 0] },
|
||||
properties: { mag: 3.0 },
|
||||
},
|
||||
{
|
||||
id: 'duplicate-id',
|
||||
geometry: { type: 'Point', coordinates: [1, 1, 1] },
|
||||
properties: { mag: 4.0 },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
assert.equal(normalizeEarthquakeSnapshot(geojson), null);
|
||||
});
|
||||
@@ -10,6 +10,9 @@ export const UNKNOWN_LAUNCH_STATUS = msg('Unknown');
|
||||
|
||||
export function createModel({ state: layerState, services, parts, source }) {
|
||||
function finiteCoordinate(value) {
|
||||
// Number(null), Number('') and Number(false) are 0: absent is not zero.
|
||||
if (value == null || typeof value === 'boolean') return null;
|
||||
if (typeof value === 'string' && value.trim() === '') return null;
|
||||
const number = Number(value);
|
||||
return Number.isFinite(number) ? number : null;
|
||||
}
|
||||
@@ -184,15 +187,11 @@ export function createModel({ state: layerState, services, parts, source }) {
|
||||
const pad = launch.pad || {};
|
||||
const location = pad.location || {};
|
||||
const coordinates = location.coordinates || '';
|
||||
const [coordinateLon, coordinateLat] = String(coordinates)
|
||||
.split(',')
|
||||
.map(Number);
|
||||
const lat = Number.isFinite(Number(pad.latitude))
|
||||
? Number(pad.latitude)
|
||||
: coordinateLat;
|
||||
const lon = Number.isFinite(Number(pad.longitude))
|
||||
? Number(pad.longitude)
|
||||
: coordinateLon;
|
||||
const [coordinateLon, coordinateLat] = String(coordinates).split(',');
|
||||
const lat =
|
||||
finiteCoordinate(pad.latitude) ?? finiteCoordinate(coordinateLat);
|
||||
const lon =
|
||||
finiteCoordinate(pad.longitude) ?? finiteCoordinate(coordinateLon);
|
||||
const payloads = normalizePayloadFlights(launch);
|
||||
return {
|
||||
id: String(
|
||||
|
||||
Reference in new issue
Block a user