diff --git a/src/hud/index.ts b/src/hud/index.ts index 16aaaae..c39d9be 100644 --- a/src/hud/index.ts +++ b/src/hud/index.ts @@ -23,12 +23,15 @@ function showAttribution (visible: true) { if (attributionElem) { if (visible) { const satelliteStore = viewer.getSatelliteStore(); - if (satelliteStore && satelliteStore.getAttribution()) { - const attribution = satelliteStore.getAttribution() || {}; + if (satelliteStore?.getAttribution()) { + const attribution = satelliteStore.getAttribution(); const updatedDate = satelliteStore.getUpdatedDate(); - attributionElem.innerHTML = `Orbital object data from ${attribution.name} (updated ${updatedDate})`; + + if (attribution) { + attributionElem.innerHTML = `Orbital object data from ${attribution.name} (updated ${updatedDate})`; + attributionElem.classList.remove('hidden'); + } } - attributionElem.classList.remove('hidden'); } else { attributionElem.classList.add('hidden'); } @@ -274,12 +277,12 @@ function getSupportedEvents () { } function initMenus () { - const elements = document.querySelectorAll('.menu-item'); - for (let i = 0; i < elements.length; i++) { - const element = elements[i] as HTMLElement; + const elements = Array.from(document.querySelectorAll('.menu-item')); + + for (const element of elements) { element.addEventListener('click', () => { - const action = element.dataset.action; - if (action && action.startsWith('open:')) { + const action = (element as HTMLElement).dataset.action; + if (action?.startsWith('open:')) { const parts = action.split(':'); windowManager.openWindow(parts[1]); } diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 14ab4c9..f38a015 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -3,7 +3,12 @@ const defaultLogLevel = 'debug'; const logLevels = ['error', 'warn', 'info', 'debug']; -let allOutputs: Record = {}; +let allOutputs = { + error: (..._args: any) => {}, + warn: (..._args: any) => {}, + info: (..._args: any) => {}, + debug: (..._args: any) => {} +}; let globalLogger = new Proxy({ logLevel: defaultLogLevel, enabledOutputs: {} as Record, @@ -41,8 +46,8 @@ function init () { const enabledOutputs = scope.enabledOutputs; - for (let i = 0; i < logLevels.length; i++) { - enabledOutputs[logLevels[i]] = true; + for (const logLevel of logLevels) { + enabledOutputs[logLevel] = true; } allOutputs = { diff --git a/src/viewer/Earth.ts b/src/viewer/Earth.ts index 851e9ac..0ba6c3f 100644 --- a/src/viewer/Earth.ts +++ b/src/viewer/Earth.ts @@ -2,6 +2,7 @@ import { ShaderMaterial, UniformsUtils, Texture } from 'three'; import { Color, TextureLoader, MeshPhongMaterial, SphereGeometry, Mesh, Group, BackSide, AdditiveBlending } from '../utils/three'; import SceneComponent from './interfaces/SceneComponent'; import SatelliteOrbitScene from './SatelliteOrbitScene'; +import { ViewerContext } from './interfaces/ViewerContext'; class Earth implements SceneComponent { baseUrl = ''; @@ -91,7 +92,7 @@ class Earth implements SceneComponent { group.add(mesh); } - async init (scene: SatelliteOrbitScene, context: Record) { + async init (scene: SatelliteOrbitScene, context: ViewerContext) { if (context.config) { this.baseUrl = context.config.baseUrl; } @@ -122,7 +123,9 @@ class Earth implements SceneComponent { this.group.add(this.sphere); if (this.addClouds) { - this.initClouds(scene, this.group); + this.initClouds(scene, this.group).catch(error => { + console.error('Error loading clouds', error); + }); } if (this.addAtmosphere) { diff --git a/src/viewer/Orbits.ts b/src/viewer/Orbits.ts index fa1dd31..10812be 100644 --- a/src/viewer/Orbits.ts +++ b/src/viewer/Orbits.ts @@ -7,6 +7,7 @@ import SatelliteOrbitScene from './SatelliteOrbitScene'; import logger from '../utils/logger'; import SatelliteGroups from './SatelliteGroups'; import SelectableSatellite from './interfaces/SelectableSatellite'; +import { ViewerContext } from './interfaces/ViewerContext'; class Orbits implements SceneComponent, SelectableSatellite { config: Record = {}; @@ -221,12 +222,12 @@ class Orbits implements SceneComponent, SelectableSatellite { // calculate tracks if (this.satelliteGroup) { const satellites = this.satelliteGroup.sats; - const satelliteIds = satellites.map((entry: Record) => entry.satId as number); + const satelliteIds = satellites.map((entry) => entry.satId as number); this.calculateOrbits(satelliteIds); } } - init (scene: SatelliteOrbitScene, context: Record) { + init (scene: SatelliteOrbitScene, context: ViewerContext) { this.config = context.config; this.scene = scene; this.orbitWorker = new OrbitCalculationWorker(); diff --git a/src/viewer/SatelliteGroups.ts b/src/viewer/SatelliteGroups.ts index b8b9656..2a0c86c 100644 --- a/src/viewer/SatelliteGroups.ts +++ b/src/viewer/SatelliteGroups.ts @@ -1,6 +1,7 @@ import SatGroup from './SatelliteGroup'; import logger from '../utils/logger'; import SatelliteStore from './SatelliteStore'; +import type SatelliteGroup from './SatelliteGroup'; class SatelliteGroups { groups: Record = {}; @@ -8,7 +9,7 @@ class SatelliteGroups { sats: any[] = []; satelliteStore: SatelliteStore; - constructor (satelliteGroups: Record[], satelliteStore: SatelliteStore) { + constructor (satelliteGroups: SatelliteGroup[], satelliteStore: SatelliteStore) { if (!satelliteStore) { throw new Error('satelliteStore is required'); } @@ -26,13 +27,12 @@ class SatelliteGroups { this.selectedGroup = group; if (!group) { this.clearSelect(); - return; } } forEach (callback: (satId: number) => void) { - for (let i = 0; i < this.sats.length; i++) { - callback(this.sats[i].satId); + for (const sat of this.sats) { + callback(sat.satId); } } @@ -54,21 +54,21 @@ class SatelliteGroups { reloadGroups () { const keys = Object.keys(this.groups); - for (let i = 0; i < keys.length; i++) { - this.groups[keys[i]].reload(); + for (const key of keys) { + this.groups[key].reload(); } } - resetConfig (satelliteGroups: Record[]) { + resetConfig (satelliteGroups: SatelliteGroup[]) { const groupConfigs = satelliteGroups; - for (let i = 0; i < groupConfigs.length; i++) { - logger.debug(`registering satellite group ${groupConfigs[i].name} (id: ${groupConfigs[i].id})`); - this.groups[groupConfigs[i].id.toLowerCase()] = new SatGroup( - groupConfigs[i].id.toLowerCase(), - groupConfigs[i].name, - groupConfigs[i].groupType, - groupConfigs[i].data, - this.satelliteStore as SatelliteStore + for (const groupConfig of groupConfigs) { + logger.debug(`registering satellite group ${groupConfig.name} (id: ${groupConfig.id})`); + this.groups[groupConfig.id.toLowerCase()] = new SatGroup( + groupConfig.id.toLowerCase(), + groupConfig.name, + groupConfig.groupType, + groupConfig.data, + this.satelliteStore ); } } diff --git a/src/viewer/SatelliteStore.ts b/src/viewer/SatelliteStore.ts index 33663fc..ac623b9 100644 --- a/src/viewer/SatelliteStore.ts +++ b/src/viewer/SatelliteStore.ts @@ -11,7 +11,10 @@ class SatelliteStore { tleUrl = `${config.baseUrl}/data/attributed-TLE.json`; eventManager: EventManager; satData: SatelliteObject[] = []; - attribution?: Record; + attribution?: { + name: string; + url: string; + }; updateDate?: Date; satelliteVelocities: Float32Array = new Float32Array(); satellitePositions: Float32Array = new Float32Array(); @@ -65,7 +68,10 @@ class SatelliteStore { } } - getAttribution (): Record | undefined { + getAttribution (): { + name: string; + url: string; + } | undefined { return this.attribution; } diff --git a/src/viewer/Satellites.ts b/src/viewer/Satellites.ts index c6336b2..8366687 100644 --- a/src/viewer/Satellites.ts +++ b/src/viewer/Satellites.ts @@ -21,6 +21,8 @@ import DefaultColorScheme from './color-schemes/DefaultColorScheme'; import SelectableSatellite from './interfaces/SelectableSatellite'; import ShaderStore from './ShaderStore'; import GroupColorScheme from './color-schemes/GroupColorScheme'; +import { SatelliteObject } from './interfaces/SatelliteObject'; +import { ViewerContext } from './interfaces/ViewerContext'; class Satellites implements SceneComponent, SelectableSatellite { baseUrl = ''; @@ -94,7 +96,7 @@ class Satellites implements SceneComponent, SelectableSatellite { /** * update point colours */ - private updateSatellitesMaterial (satCount: number, satellites: Record[]) { + private updateSatellitesMaterial (satCount: number, satellites: SatelliteObject[]) { if (this.geometry?.attributes.color && this.currentColorScheme && this.satelliteStore) { // Adjust if the satellite count adjusts if (this.satelliteColors.length === 0 || (satCount * 4 !== this.satelliteColors.length)) { @@ -332,7 +334,7 @@ class Satellites implements SceneComponent, SelectableSatellite { })); } - async init (scene: SatelliteOrbitScene, context: Record) { + async init (scene: SatelliteOrbitScene, context: ViewerContext) { this.satelliteStore = context.satelliteStore; this.shaderStore = context.shaderStore; this.scene = scene; diff --git a/src/viewer/Universe.ts b/src/viewer/Universe.ts index 1cfe126..4c79d33 100644 --- a/src/viewer/Universe.ts +++ b/src/viewer/Universe.ts @@ -1,9 +1,10 @@ +import { ViewerContext } from './interfaces/ViewerContext'; import { TextureLoader } from '../utils/three'; import SceneComponent from './interfaces/SceneComponent'; import SatelliteOrbitScene from './SatelliteOrbitScene'; class Universe implements SceneComponent { - init (scene: SatelliteOrbitScene, context: Record) { + init (scene: SatelliteOrbitScene, context: ViewerContext) { const baseUrl = context.config.baseUrl; const texture = new TextureLoader().load(`${baseUrl}textures/example_render.jpg`); diff --git a/src/viewer/color-schemes/ColorScheme.ts b/src/viewer/color-schemes/ColorScheme.ts index 671be38..0ffcb7f 100644 --- a/src/viewer/color-schemes/ColorScheme.ts +++ b/src/viewer/color-schemes/ColorScheme.ts @@ -1,15 +1,16 @@ import SatelliteGroup from '../SatelliteGroup'; +import { SatelliteObject } from '../interfaces/SatelliteObject'; class ColorScheme { name: string; - colorizer: (satellite: Record, group?: SatelliteGroup) => { color: number[], pickable: boolean }; + colorizer: (satellite: SatelliteObject, group?: SatelliteGroup) => { color: number[], pickable: boolean }; - constructor (name: string, colorizer: (satellite: Record) => { color: number[], pickable: boolean }) { + constructor (name: string, colorizer: (satellite: SatelliteObject) => { color: number[], pickable: boolean }) { this.name = name; this.colorizer = colorizer; } - getSatelliteColor (satellite: Record, group?: SatelliteGroup): { color: number[], pickable: boolean } { + getSatelliteColor (satellite: SatelliteObject, group?: SatelliteGroup): { color: number[], pickable: boolean } { return this.colorizer(satellite, group); } } diff --git a/src/viewer/color-schemes/DefaultColorScheme.ts b/src/viewer/color-schemes/DefaultColorScheme.ts index 64c0e3d..97289cc 100644 --- a/src/viewer/color-schemes/DefaultColorScheme.ts +++ b/src/viewer/color-schemes/DefaultColorScheme.ts @@ -1,8 +1,9 @@ +import { SatelliteObject } from '../interfaces/SatelliteObject'; import ColorScheme from './ColorScheme'; class DefaultColorScheme extends ColorScheme { constructor () { - super ('Default color scheme', (satellite: Record) => { + super ('Default color scheme', (satellite: SatelliteObject) => { let color = [1.0, 1.0, 0.0, 1.0]; let pickable = false; diff --git a/src/viewer/color-schemes/GroupColorScheme.ts b/src/viewer/color-schemes/GroupColorScheme.ts index 3a46d5b..45585e7 100644 --- a/src/viewer/color-schemes/GroupColorScheme.ts +++ b/src/viewer/color-schemes/GroupColorScheme.ts @@ -1,11 +1,12 @@ import SatelliteGroup from '@satellite-viewer/SatelliteGroup'; import ColorScheme from './ColorScheme'; +import { SatelliteObject } from '../interfaces/SatelliteObject'; class GroupColorScheme extends ColorScheme { constructor () { - super ('Group color scheme', (satellite: Record, group?: SatelliteGroup) => { + super ('Group color scheme', (satellite: SatelliteObject, group?: SatelliteGroup) => { if (satellite) { - if (group && group.hasSat(satellite.id)) { + if (group?.hasSat(satellite.id)) { return { color: [1.0, 0.2, 0.0, 1.0], pickable: true diff --git a/src/viewer/index.ts b/src/viewer/index.ts index 71106e2..577a820 100644 --- a/src/viewer/index.ts +++ b/src/viewer/index.ts @@ -16,6 +16,8 @@ import SatelliteGroup from './SatelliteGroup'; import ShaderStore from './ShaderStore'; import logger from '@/utils/logger'; import { ArrowHelper, Raycaster, Vector2, Vector3 } from 'three'; +import { SatelliteObject } from './interfaces/SatelliteObject'; +import { ViewerContext } from './interfaces/ViewerContext'; class Viewer { config: Record = { @@ -28,7 +30,12 @@ class Viewer { camera?: PerspectiveCamera; controls?: OrbitControls; renderer?: WebGLRenderer; - context: Record = {}; + context: ViewerContext = { + satelliteGroups: null as unknown as SatelliteGroups, + config: null as unknown as Record, + satelliteStore: null as unknown as SatelliteStore, + shaderStore: null as unknown as ShaderStore + }; satelliteGroups?: SatelliteGroups; satelliteStore?: SatelliteStore; shaderStore?: ShaderStore; @@ -82,7 +89,7 @@ class Viewer { } } - private onSatDataLoaded (satData: Record) { + private onSatDataLoaded (satData: SatelliteObject[]) { this.eventManager.fireEvent('satdataloaded', satData); this.ready = true; } @@ -404,7 +411,7 @@ class Viewer { this.satellites?.setSatelliteGroup(satelliteGroup); } - getSelectedSatellite (): Record | undefined { + getSelectedSatellite (): SatelliteObject | undefined { if (this.satelliteStore) { return this.satelliteStore.getSatellite(this.selectedSatelliteIdx); } diff --git a/src/viewer/interfaces/SceneComponent.ts b/src/viewer/interfaces/SceneComponent.ts index 399ae68..465b1bc 100644 --- a/src/viewer/interfaces/SceneComponent.ts +++ b/src/viewer/interfaces/SceneComponent.ts @@ -1,7 +1,8 @@ +import { ViewerContext } from './ViewerContext'; import SatelliteOrbitScene from '../SatelliteOrbitScene'; interface SceneComponent { - init (scene: SatelliteOrbitScene, context: Record): void | Promise; + init (scene: SatelliteOrbitScene, context: ViewerContext): void | Promise; update (scene?: SatelliteOrbitScene): void; } diff --git a/src/viewer/interfaces/ViewerContext.ts b/src/viewer/interfaces/ViewerContext.ts new file mode 100644 index 0000000..ea3c3d1 --- /dev/null +++ b/src/viewer/interfaces/ViewerContext.ts @@ -0,0 +1,11 @@ +import type SatelliteGroups from '../SatelliteGroups'; +import type SatelliteStore from '../SatelliteStore'; +import type ShaderStore from '../ShaderStore'; + + +export interface ViewerContext { + satelliteGroups: SatelliteGroups; + config: Record; + satelliteStore: SatelliteStore; + shaderStore: ShaderStore; +}