Skip to content

ThermalGroup ​

The ThermalGroup is a container of thermal files (ThermalFileInstance) within a ThermalRegistry. It represents a logical grouping of thermograms, such as a time series, a set of related camera captures, or an inspection batch.

Each group belongs to exactly one ThermalRegistry, from which it inherits the shared thermal palette, display range, and worker pool.

For the reactivity mechanisms used below (addListener and the CallbacksManager events), see CallbacksManager.

Creating and managing groups ​

A group is identified by its ID string within its parent registry. You can also specify an optional human-readable name and description.

To create or retrieve a group, use registry.groups.addOrGetGroup:

ts
import { ThermalManager, ThermalGroup } from "@labirthermal/core";

const manager = new ThermalManager();
const registry = manager.registries.addOrGetRegistry( "display-set" );

// Create or retrieve a group
const group = registry.groups.addOrGetGroup(
    "morning-series",
    "Morning Series",
    "IR recordings taken in the morning"
);

// Calling addOrGetGroup with an existing ID returns the same group instance
const sameGroup = registry.groups.addOrGetGroup( "morning-series" );
console.log( group === sameGroup ); // true

// Human-readable label (resolves name, id, or internal hash)
console.log( group.label ); // "Morning Series"

// Groups can also be created directly and added to the registry
const customGroup = new ThermalGroup( registry, "evening-series", "Evening Series" );
registry.groups.addExistingGroup( customGroup );

// Access parent hierarchy
console.log( group.registry === registry ); // true
console.log( group.registry.manager === manager ); // true

// Remove a group and its instances from the registry
registry.groups.removeGroup( "morning-series" );

Example ​

The example below shows an implementation of ThermalGroup using the @labirthermal/webcomponents package. A group-provider creates or retrieves one group identified by its group-slug attribute; the nested file-provider elements load files into it – see the context providers guide.

Group: Morning Series

files ​

Handled by
FilesState
Value type
ThermalFileInstance[]
Source
packages/core/src/properties/lists/filesState.ts

The group.files property manages all thermal file instances belonging to this group.

Managing group files ​

  • Ordered by timestamp: the instances are automatically sorted chronologically (group.files.sortedFiles).
  • Loading files: files should be loaded via the registry's batch loader with this group as target: registry.batch.request(thermalUrl, visibleUrl, group, callback) – see FilesService & batches.
  • Direct operations:
    • group.files.value or group.getInstances() returns an array of ThermalFileInstance.
    • group.files.removeFile(urlOrInstance) unmounts and removes a file.
    • group.files.removeAllInstances() destroys all instances in the group.
    • group.files.downloadAllFiles() packages all original files in this group into a .zip archive and triggers browser download.
    • group.files.forEveryInstance(fn) iterates through every instance; group.forEveryInstance(fn) provides the same traversal.

Treat group.files.value as read-only and use the collection's methods to modify it.

Setter methods ​

  • group.files.addFile( instance ) – register an existing ThermalFileInstance (called automatically by the loading pipeline).
  • group.files.removeFile( urlOrInstance ) – unmount and remove a file.
  • group.files.removeAllInstances() – destroy all instances in the group.

Events ​

  • group.files.addListener( id, files => ... ) – listener on the membership changes.
ts
import {
    ThermalManager,
    ThermalFileInstance,
    ThermalFileFailure
} from "@labirthermal/core";

const manager = new ThermalManager();
const registry = manager.registries.addOrGetRegistry( "display-set" );
const group = registry.groups.addOrGetGroup( "series" );

// Load a file into the group via batch
registry.batch.request(
    "/files/klokan.lrc",
    undefined,
    group,
    async ( result: ThermalFileInstance | ThermalFileFailure ) => {
        if ( result instanceof ThermalFileInstance ) {
            const container = document.createElement( "div" );
            document.body.appendChild( container );
            result.mountToDom( container );
        }
    }
);

// Iterate through loaded files in this group
group.files.forEveryInstance( instance => {
    console.log( "File:", instance.fileName, instance.timestamp );
} );

// Download all original files in this group as a zip archive
// group.files.downloadAllFiles();

// Remove a specific file from the group
group.files.removeFile( "/files/klokan.lrc" );

Example ​

The example below shows an implementation of FilesState using the @labirthermal/webcomponents package. The files are loaded into the group by file-provider elements and the group-download-dropdown control downloads the original files, PNGs or analysis CSV of the whole group.

minmax ​

The group.minmax property computes temperature limits specifically for files in this group.

How group minmax works ​

  • Calculated from group instances: whenever files are processed, group.minmax.recalculateFromInstances() computes the lowest minimum and highest maximum temperatures across files in this group alone.
  • Distinction from registry minmax: while registry.minmax spans all groups in the registry, group.minmax is scoped exclusively to this group.
  • Range propagation: using the group-range-propagator control, a user can hover to highlight this group's temperature window on the registry scale, or click to impose this group's min/max range across the entire registry.
ts
import { ThermalManager } from "@labirthermal/core";

const manager = new ThermalManager();
const registry = manager.registries.addOrGetRegistry( "display-set" );
const group = registry.groups.addOrGetGroup( "series" );

// Read this group's temperature limits
const groupMinmax = group.minmax.value;

if ( groupMinmax ) {
    console.log( `Group min: ${groupMinmax.min} °C, max: ${groupMinmax.max} °C` );

    // Propagate this group's minmax to the registry range
    registry.range.imposeRange({
        from: groupMinmax.min,
        to: groupMinmax.max
    });
}

// Listen for group minmax changes
group.minmax.addListener( "group-minmax-listener", value => {
    console.log( "Group minmax updated:", value );
} );

Setter methods ​

The property is read-only in normal use; it recalculates itself during the batch post-processing.

  • group.minmax.recalculateFromInstances() – recalculate the bounds from the group's instances (called automatically by registry.postLoadedProcessing()).

Events ​

  • group.minmax.addListener( id, value => ... ) – listener on the bounds changes.

Example ​

The example below shows an implementation of MinmaxGroupProperty using the @labirthermal/webcomponents package. Each group contains a group-range-propagator control: hovering it highlights the group's temperature window on the registry scale and clicking it imposes the group's min/max as the registry range – see the thermal range guide.

Group A:
Group B:

analysisSync ​

The group.analysisSync property allows points, rectangles, and ellipses created for one image to be automatically mirrored across all images within the group.

How analysis sync works ​

  • Turn on synchronization: group.analysisSync.turnOn( sourceInstance ) enables synchronization. Any analysis created, modified, or moved on one image will immediately replicate on the others.
  • Turn off synchronization: group.analysisSync.turnOff().
  • Value: group.analysisSync.value: boolean indicates whether sync is currently active.
  • Data export helpers:
    • group.analysisSync.csv.downloadAsCsv() downloads a tabulated CSV of analysis values across all files.
    • group.analysisSync.png.downloadPng(...) exports a multi-image composite PNG of all group images with rendered analysis layers.
ts
import { ThermalManager } from "@labirthermal/core";

const manager = new ThermalManager();
const registry = manager.registries.addOrGetRegistry( "display-set" );
const group = registry.groups.addOrGetGroup( "series" );

const firstInstance = group.files.value[0];

if ( firstInstance ) {
    // Turn on analysis synchronization using the first file as the basis
    group.analysisSync.turnOn( firstInstance );
}

// Turn off analysis synchronization
group.analysisSync.turnOff();

// Listen for sync status changes
group.analysisSync.addListener( "sync-listener", ( isSynced: boolean ) => {
    console.log( "Analysis synchronization:", isSynced );
} );

Setter methods ​

  • group.analysisSync.turnOn( sourceInstance ) – enable synchronization using the given instance as the source.
  • group.analysisSync.turnOff() – disable synchronization.
  • group.analysisSync.copyOneSlotToAllInstances( instance, slot ) – copy one analysis slot from an instance to all other instances.
  • group.analysisSync.copyAllSlotsToAllInstances( instance ) – copy all analysis slots from an instance to all other instances.

Events ​

  • group.analysisSync.addListener( id, isSynced => ... ) – listener on the sync status changes.
  • group.analysisSync.onSlotSync – CallbacksManager called with the serialised analysis and the slot number whenever a slot synchronizes.

Example ​

The example below shows an implementation of AnalysisSyncDrive using the @labirthermal/webcomponents package. The group-analysis-sync-button control toggles the synchronization; the tools for drawing analyses are selected with the manager-tool-bar control.

Select a tool and draw on either image — the analysis synchronizes across both.

cursorPosition ​

Handled by
CursorPositionDrive
Value type
ThermalCursorPositionOrUndefined
Source
packages/core/src/properties/cursor/CursorPositionDrive.ts

The group.cursorPosition property mirrors hover coordinates across all images in the group. When hovering over one image, the cursor position [x, y] is broadcast to the other instances, highlighting the exact same spatial pixel across every image. Every instance then calculates its own temperature under the cursor – see instance.cursorValue.

ts
import { ThermalManager } from "@labirthermal/core";

const manager = new ThermalManager();
const registry = manager.registries.addOrGetRegistry( "display-set" );
const group = registry.groups.addOrGetGroup( "series" );

// Current cursor coordinates: { x: number, y: number } | undefined
const cursor = group.cursorPosition.value;

// Programmatically broadcast a cursor position
group.cursorPosition.recieveCursorPosition({ x: 120, y: 80 });

// Clear the cursor hover
group.cursorPosition.recieveCursorPosition( undefined );

// Listen for cursor position updates
group.cursorPosition.addListener( "cursor-listener", position => {
    if ( position ) {
        console.log( `Cursor at [${position.x}, ${position.y}]` );
    }
} );

Setter methods ​

  • group.cursorPosition.recieveCursorPosition( position ) – broadcast a cursor position ({ x, y } or undefined) to all instances of the group.

Events ​

  • group.cursorPosition.addListener( id, position => ... ) – listener on the cursor position changes.

playback ​

The group.playback property coordinates synchronized timeline playback when images within the group are video sequences (such as recorded radiometric .lrc sequences). The value is the current relative playback time in milliseconds.

  • Sequence detection: group.playback.hasAnyPlayback indicates if any instance contains multiple frames.
  • Playback state: group.playback.playing indicates whether the sequence is currently playing.
  • Timeline tracking: group.playback.value represents the current relative playback time in milliseconds.
ts
import { ThermalManager } from "@labirthermal/core";

const manager = new ThermalManager();
const registry = manager.registries.addOrGetRegistry( "display-set" );
const group = registry.groups.addOrGetGroup( "sequence-group" );

// Check if any file in the group has playback sequence
console.log( "Has sequences:", group.playback.hasAnyPlayback );

// Current playback time in ms
const currentMs: number = group.playback.value;

// Listen for playback status changes
group.playback.onPlayingStatusChange.set( "playback-listener", isPlaying => {
    console.log( "Playing status:", isPlaying );
} );

Setter methods ​

  • group.playback.play() – start the synchronized playback of all sequences in the group.
  • group.playback.stop() – stop the playback.
  • group.playback.setValueByPercent( percent ) – jump to a position given in percent of the duration.
  • group.playback.setValueByRelativeMs( ms ) – jump to a relative time in milliseconds.
  • group.playback.setLoopInterval( ms ) – change the interval of the playback loop timer.

Events ​

  • group.playback.addListener( id, ms => ... ) – listener on the playback time changes.
  • group.playback.onPlayingStatusChange – CallbacksManager called when the playback starts or stops.
  • group.playback.onHasAnyCallback – CallbacksManager called when the presence of sequences in the group changes.
  • group.playback.onDurationChanged – CallbacksManager called when the overall duration changes.
  • group.playback.onLoopIntervalChanged – CallbacksManager called when the loop interval changes.

Example ​

The example below shows an implementation of GroupPlayback using the @labirthermal/webcomponents package. The group-timeline control drives the synchronized playback of all sequences within its group-provider.

analysisGraph ​

Handled by
AnalysisGroupGraph
Value type
ThermalGraphGroupDataOrUndefined
Source
packages/core/src/properties/analysis/group/AnalysisGroupGraph.ts

Aggregated graph data of the analyses across all sequences in the group, used by the group-chart component. When turned on, the property collects the time series of every analysis of every instance into one dataset.

Setter methods ​

  • group.analysisGraph.turnOn() – start collecting the graph data.
  • group.analysisGraph.turnOff() – stop collecting and clear the data.

Events ​

  • group.analysisGraph.addListener( id, data => ... ) – listener on the aggregated graph data changes.

Summary of group controls ​

PropertyTypeDescriptionControl elements
FilesThermalFileInstance[]Instances sorted chronologicallygroup-download-dropdown, group-download-buttons
Minmax{ min, max } | undefinedGroup-scoped temperature limitsgroup-range-propagator
Analysis syncbooleanMirrors ROI analyses across all group filesgroup-analysis-sync-button
Cursor position{ x, y } | undefinedMirrors cursor hover coordinates across group files– (automatic on hover)
Playbacknumber (ms)Synchronized timeline playback for sequencesgroup-timeline