@fxi/zartigl 0.4.2 → 0.6.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/dist/zartigl.d.ts CHANGED
@@ -60,12 +60,24 @@ export declare interface CatalogEntry {
60
60
  };
61
61
  }
62
62
 
63
+ /**
64
+ * An incremental archive of calendar-aligned artifacts listed by a mutable
65
+ * index; zartigl resolves it to the chunk covering the requested time.
66
+ */
67
+ declare interface CatalogGeoVideoArchiveSource extends CatalogSourceBase {
68
+ type: "geovideo";
69
+ indexUrl: string;
70
+ manifestUrl?: never;
71
+ }
72
+
73
+ /** One published artifact. */
63
74
  declare interface CatalogGeoVideoSource extends CatalogSourceBase {
64
75
  type: "geovideo";
65
76
  manifestUrl: string;
77
+ indexUrl?: never;
66
78
  }
67
79
 
68
- declare type CatalogRenderLayerBackend = "vector-zarr" | "scalar-zarr" | "scalar-geovideo" | "scalar-wmts";
80
+ declare type CatalogRenderLayerBackend = "vector-zarr" | "scalar-zarr" | "scalar-geovideo" | "vector-geovideo" | "scalar-wmts";
69
81
 
70
82
  declare type CatalogRenderLayerDebugInfo = {
71
83
  id: string;
@@ -81,7 +93,7 @@ declare type CatalogRenderLayerDebugInfo = {
81
93
  delegate: VectorLayerDebugInfo | ScalarLayerDebugInfo | GeoVideoLayerDebugInfo | null;
82
94
  };
83
95
 
84
- export declare type CatalogSource = CatalogZarrSource | CatalogWmtsSource | CatalogGeoVideoSource;
96
+ export declare type CatalogSource = CatalogZarrSource | CatalogWmtsSource | CatalogGeoVideoSource | CatalogGeoVideoArchiveSource;
85
97
 
86
98
  declare interface CatalogSourceBase {
87
99
  id: string;
@@ -144,6 +156,28 @@ declare interface CatalogZarrSource extends CatalogSourceBase {
144
156
  variables: CatalogVariables;
145
157
  }
146
158
 
159
+ export declare const COLOR_GRADE_OFF: Readonly<ColorGrade>;
160
+
161
+ export declare const COLOR_GRADE_VIBRANT: Readonly<ColorGrade>;
162
+
163
+ /**
164
+ * Optional color grade applied when particle trails are composited
165
+ * (`shaders/color-grade.glsl`): more intense highlights, deeper shadows and
166
+ * richer colors, without hard clipping. Off by default; when off, the
167
+ * composite returns the trail colors unchanged.
168
+ */
169
+ export declare interface ColorGrade {
170
+ enabled: boolean;
171
+ /** Mid-tone lift [0, 4]; 0 keeps the tones. */
172
+ exposure: number;
173
+ /** S-curve strength [0, 1]. */
174
+ contrast: number;
175
+ /** Vibrance-weighted saturation [-1, 1]. */
176
+ saturation: number;
177
+ /** Push of the bright half toward white [0, 1]. */
178
+ highlights: number;
179
+ }
180
+
147
181
  export declare type ColorRampInput = string | string[] | Record<number, string>;
148
182
 
149
183
  declare interface CoordArrays {
@@ -182,7 +216,15 @@ export declare interface FieldMeta {
182
216
  depth?: number;
183
217
  }
184
218
 
185
- export declare type GeoVideoBounds = [west: number, south: number, east: number, north: number];
219
+ /** GeoVideo playback speeds, in time steps per second. */
220
+ export declare const GEOVIDEO_SPEEDS: readonly number[];
221
+
222
+ export declare type GeoVideoBounds = [
223
+ west: number,
224
+ south: number,
225
+ east: number,
226
+ north: number
227
+ ];
186
228
 
187
229
  declare type GeoVideoEventMap = {
188
230
  loading: () => void;
@@ -191,6 +233,8 @@ declare type GeoVideoEventMap = {
191
233
  status: (status: ZartiglStatus) => void;
192
234
  timeChange: (time: number) => void;
193
235
  playbackChange: (playing: boolean) => void;
236
+ /** Playback reached the end of the timeline without looping. */
237
+ playbackEnd: () => void;
194
238
  };
195
239
 
196
240
  export declare class GeoVideoLayer implements CustomLayerInterface {
@@ -198,7 +242,8 @@ export declare class GeoVideoLayer implements CustomLayerInterface {
198
242
  readonly type: "custom";
199
243
  readonly renderingMode: "3d";
200
244
  private readonly source;
201
- private readonly autoplay;
245
+ /** Start playback once ready: autoplay, or a swap that was playing. */
246
+ private playOnReady;
202
247
  private loop;
203
248
  private playbackRate;
204
249
  private requestedTime;
@@ -213,6 +258,14 @@ export declare class GeoVideoLayer implements CustomLayerInterface {
213
258
  private gl;
214
259
  private manifest;
215
260
  private video;
261
+ /** Manifest of `video`, which still differs from `manifest` mid-swap. */
262
+ private videoManifest;
263
+ /** Incoming media of a swap, shown once its first frame is decodable. */
264
+ private pendingVideo;
265
+ private standby;
266
+ /** Ended on a preloaded artifact: playing until it is swapped in or paused. */
267
+ private awaitingNext;
268
+ private maskUrl;
216
269
  private colorTexture;
217
270
  private maskTexture;
218
271
  private colorRampTexture;
@@ -245,12 +298,33 @@ export declare class GeoVideoLayer implements CustomLayerInterface {
245
298
  private lastUploadDurationMs;
246
299
  private abortController;
247
300
  private resumePlayback;
301
+ private stepTimer;
302
+ private endEmitted;
248
303
  private listeners;
249
304
  constructor(options: GeoVideoLayerOptions);
250
- onAdd(map: Map_2, gl: WebGLRenderingContext): Promise<void>;
251
- render(gl: WebGLRenderingContext, options: CustomRenderMethodInput): void;
305
+ onAdd(map: Map_2, context: WebGLRenderingContext | WebGL2RenderingContext): Promise<void>;
306
+ render(context: WebGLRenderingContext | WebGL2RenderingContext, options: CustomRenderMethodInput): void;
252
307
  onRemove(): void;
253
308
  setTime(time: string | number): void;
309
+ /**
310
+ * Switch to another artifact of the same frame size, such as the next
311
+ * archive chunk, keeping the map layer and GL state. The current frame stays
312
+ * on screen until the new media can show its first one, at once when it was
313
+ * preloaded. Returns false when the artifact needs a new layer.
314
+ */
315
+ replaceManifest(manifest: GeoVideoManifest, options?: GeoVideoReplaceOptions): boolean;
316
+ /**
317
+ * Load the artifact expected next, such as the following archive chunk, so
318
+ * `replaceManifest` can show it without waiting for the network. Until then,
319
+ * reaching the end of the current artifact keeps the playing state and
320
+ * emits `playbackEnd` for the caller to swap it in, or to pause. Returns
321
+ * false when the artifact cannot replace the current one in place.
322
+ */
323
+ preloadManifest(manifest: GeoVideoManifest, options?: GeoVideoReplaceOptions): boolean;
324
+ /** Clamped time range of a compatible replacement, else null. */
325
+ private replacementTimeRange;
326
+ /** The preloaded artifact when it holds this media; any other is dropped. */
327
+ private takeStandby;
254
328
  setTimeAndDepth(time: string | number, _depth: number): void;
255
329
  setDepth(_depth: number): void;
256
330
  prefetchTime(_ms: number): Promise<void>;
@@ -266,6 +340,24 @@ export declare class GeoVideoLayer implements CustomLayerInterface {
266
340
  setOpacity(value: number): void;
267
341
  play(): Promise<void>;
268
342
  pause(): void;
343
+ /**
344
+ * Playback is a request, applied once ready, while the manifest loads,
345
+ * mid-swap, or at an end waiting for the preloaded artifact.
346
+ */
347
+ private deferredPlayback;
348
+ private isPlaying;
349
+ /**
350
+ * Below the slowest media rate browsers accept, keep the video paused and
351
+ * seek one sample (or frame) at a time.
352
+ */
353
+ private startStepping;
354
+ private stopStepping;
355
+ private step;
356
+ /**
357
+ * The timeline ended without looping. With a preloaded artifact, keep the
358
+ * playing state for the caller to swap it in; otherwise stop.
359
+ */
360
+ private reachEnd;
269
361
  setLoop(loop: boolean): void;
270
362
  setPlaybackRate(rate: number): void;
271
363
  setTimeRange(range: [number, number]): void;
@@ -275,8 +367,28 @@ export declare class GeoVideoLayer implements CustomLayerInterface {
275
367
  off<K extends keyof GeoVideoEventMap>(event: K, handler: GeoVideoEventMap[K]): this;
276
368
  private initGl;
277
369
  private initVideo;
370
+ /**
371
+ * Media element for a manifest. Its events drive the layer only while it is
372
+ * the current video and no swap is pending.
373
+ */
374
+ private createVideo;
375
+ /** Track presented frames of the current video. */
376
+ private startFrameCallbacks;
377
+ /**
378
+ * Time a loading video opens at: its preload target, or the requested time
379
+ * within the active range.
380
+ */
381
+ private startTime;
382
+ private checkVideoSize;
383
+ private cancelFrameCallback;
384
+ /** Stop the current video and free its media resources. */
385
+ private releaseVideo;
386
+ private discardStandby;
387
+ private discardPendingVideo;
278
388
  private bufferFrame;
279
- private loadStaticMask;
389
+ private createMaskImage;
390
+ /** Show the mask of a manifest once its image, possibly preloaded, loads. */
391
+ private applyMask;
280
392
  private emitReady;
281
393
  private createVideoTexture;
282
394
  private uploadTextureSource;
@@ -296,6 +408,8 @@ export declare interface GeoVideoLayerDebugInfo {
296
408
  currentTime: number;
297
409
  manifestId?: string;
298
410
  mediaUrl?: string;
411
+ /** Media loaded ahead for the next swap. */
412
+ preloadedMediaUrl?: string;
299
413
  decodedFrames: number;
300
414
  bufferedFrames: number;
301
415
  skippedFrames: number;
@@ -339,17 +453,7 @@ export declare interface GeoVideoManifest {
339
453
  durationSeconds: number;
340
454
  codec: "h264" | string;
341
455
  };
342
- encoding: {
343
- kind: "scalar-luma";
344
- bits: 8;
345
- codeMin: number;
346
- codeMax: number;
347
- valueMin: number;
348
- valueMax: number;
349
- transfer: "linear";
350
- colorSpace: "bt709";
351
- colorRange: "limited" | "full";
352
- };
456
+ encoding: GeoVideoScalarEncoding | GeoVideoVectorEncoding;
353
457
  mask: {
354
458
  kind: "static-validity";
355
459
  url: string;
@@ -379,7 +483,10 @@ export declare interface GeoVideoManifest {
379
483
  export declare interface GeoVideoOptions {
380
484
  autoplay?: boolean;
381
485
  loop?: boolean;
486
+ /** Media rate multiplier; prefer `stepsPerSecond`, which means the same on every artifact. */
382
487
  playbackRate?: number;
488
+ /** Time steps shown per second; takes precedence over `playbackRate`. */
489
+ stepsPerSecond?: number;
383
490
  }
384
491
 
385
492
  export declare interface GeoVideoRangeTimeline {
@@ -389,11 +496,34 @@ export declare interface GeoVideoRangeTimeline {
389
496
  interpolation: "linear" | "nearest";
390
497
  }
391
498
 
499
+ /**
500
+ * Playback state for an artifact swapped in with `replaceManifest`, or
501
+ * loaded ahead with `preloadManifest`.
502
+ */
503
+ declare interface GeoVideoReplaceOptions {
504
+ time?: number;
505
+ timeRange?: [number, number];
506
+ loop?: boolean;
507
+ playbackRate?: number;
508
+ }
509
+
392
510
  export declare interface GeoVideoSampleSequenceTimeline {
393
511
  kind: "sample-sequence";
394
512
  values: string[];
395
513
  }
396
514
 
515
+ declare interface GeoVideoScalarEncoding {
516
+ kind: "scalar-luma";
517
+ bits: 8;
518
+ codeMin: number;
519
+ codeMax: number;
520
+ valueMin: number;
521
+ valueMax: number;
522
+ transfer: "linear";
523
+ colorSpace: "bt709";
524
+ colorRange: "limited" | "full";
525
+ }
526
+
397
527
  export declare function geoVideoSecondsForTime(manifest: GeoVideoManifest, timeMs: number): number;
398
528
 
399
529
  export declare interface GeoVideoSnapshotLoopTimeline {
@@ -405,6 +535,24 @@ export declare function geoVideoTimeForSeconds(manifest: GeoVideoManifest, secon
405
535
 
406
536
  export declare function geoVideoTimelineValues(manifest: GeoVideoManifest): number[];
407
537
 
538
+ /**
539
+ * u and v stacked vertically in luma (u rows on top). Each component maps
540
+ * [-valueDomain, valueDomain] to [codeMin, codeMax]; "sqrt" stores
541
+ * sign(x)·sqrt(|x|/valueDomain) to keep precision on slow flows. The static
542
+ * mask covers one component (media height / 2).
543
+ */
544
+ declare interface GeoVideoVectorEncoding {
545
+ kind: "vector-luma";
546
+ bits: 8;
547
+ codeMin: number;
548
+ codeMax: number;
549
+ valueDomain: number;
550
+ transfer: "linear" | "sqrt";
551
+ layout: "stacked-uv";
552
+ colorSpace: "bt709";
553
+ colorRange: "limited" | "full";
554
+ }
555
+
408
556
  export declare function getPalettes(): PaletteMeta[];
409
557
 
410
558
  export declare function getVectorDerivationVariables(derivation: VectorDerivation): [string, string];
@@ -472,6 +620,7 @@ export declare class ParticleSimulation {
472
620
  private updateProgram;
473
621
  private drawProgram;
474
622
  private fadeProgram;
623
+ private compositeProgram;
475
624
  private gridProgram;
476
625
  private gridGlobeProgram;
477
626
  private particleStateTextures;
@@ -482,12 +631,9 @@ export declare class ParticleSimulation {
482
631
  private numParticles;
483
632
  private prevBounds;
484
633
  private redistributionBoost;
485
- private cameraMoving;
486
- private currentFadeOpacity;
487
- private fadeTransitionStartOpacity;
488
- private fadeTransitionTargetOpacity;
489
- private fadeTransitionStartMs;
490
- private fadeTransitionDurationMs;
634
+ /** Advection counter seeding the update shader random streams. */
635
+ private frame;
636
+ private readonly trailFade;
491
637
  private webglInfo;
492
638
  private stateFormat;
493
639
  private lastRgba8ParticlesSuppressed;
@@ -504,10 +650,11 @@ export declare class ParticleSimulation {
504
650
  private updateLocs;
505
651
  private drawLocs;
506
652
  private fadeLocs;
653
+ private compositeLocs;
507
654
  private gridLocs;
508
655
  private gridGlobeLocs;
509
656
  constructor(params?: Partial<SimulationParams>);
510
- init(gl: WebGLRenderingContext): void;
657
+ init(gl: WebGL2RenderingContext): void;
511
658
  private getUniforms;
512
659
  /** Random initial particle positions, matching the active state format. */
513
660
  private makeStateData;
@@ -564,6 +711,8 @@ export declare class ParticleSimulation {
564
711
  setOpacity(v: number): void;
565
712
  setLogScale(v: boolean): void;
566
713
  setVibrance(v: number): void;
714
+ /** Partial values take the vibrant preset; see resolveColorGrade. */
715
+ setColorGrade(grade: Partial<ColorGrade>): void;
567
716
  setScalarMode(v: boolean): void;
568
717
  setRgba8MaxParticleZoom(v: number): void;
569
718
  /**
@@ -599,6 +748,7 @@ export declare interface ParticleSimulationDebugInfo {
599
748
  opacity: number;
600
749
  logScale: boolean;
601
750
  vibrance: number;
751
+ colorGrade: ColorGrade;
602
752
  scalarMode: boolean;
603
753
  devicePixelRatio?: number;
604
754
  webgl: WebGLRendererInfo | null;
@@ -608,6 +758,17 @@ export declare type ParticleStateKind = StateTextureFormat["kind"];
608
758
 
609
759
  export declare type ParticleStateMode = "auto" | "rgba8";
610
760
 
761
+ export declare interface PlaybackState {
762
+ /** Whether the active source plays automatically: GeoVideo only. */
763
+ available: boolean;
764
+ playing: boolean;
765
+ loop: boolean;
766
+ /** Speed applied by the active source; vector stays within `speeds`. */
767
+ stepsPerSecond: number;
768
+ /** Speeds offered for the active layer kind. */
769
+ speeds: readonly number[];
770
+ }
771
+
611
772
  export declare interface QueryDepthProfileOptions {
612
773
  longitude: number;
613
774
  latitude: number;
@@ -622,7 +783,14 @@ export declare interface QueryPointOptions {
622
783
  maxPoints?: number;
623
784
  }
624
785
 
625
- export declare type RenderMode = 'raster' | 'particles' | 'raster+particles';
786
+ export declare type RenderMode = "raster" | "particles" | "raster+particles";
787
+
788
+ /**
789
+ * Complete grade from partial input. Missing or non-finite values take the
790
+ * vibrant preset, others are clamped to their range; `enabled` defaults to
791
+ * true when any input is given, so `{}` means "vibrant".
792
+ */
793
+ export declare function resolveColorGrade(input?: Partial<ColorGrade>): ColorGrade;
626
794
 
627
795
  /** Resolve a native date/time control value against actual available times. */
628
796
  export declare function resolveTimeInputSelection(values: readonly number[], value: string, granularity: TimeGranularity): number | undefined;
@@ -656,10 +824,10 @@ export declare class ScalarLayer implements CustomLayerInterface {
656
824
  private colorDomain;
657
825
  private textureUnit;
658
826
  constructor(options: ScalarLayerOptions);
659
- onAdd(map: Map_2, gl: WebGLRenderingContext): Promise<void>;
827
+ onAdd(map: Map_2, context: WebGLRenderingContext | WebGL2RenderingContext): Promise<void>;
660
828
  private moveEndHandler;
661
829
  private initAsync;
662
- render(gl: WebGLRenderingContext, options: CustomRenderMethodInput): void;
830
+ render(context: WebGLRenderingContext | WebGL2RenderingContext, options: CustomRenderMethodInput): void;
663
831
  onRemove(): void;
664
832
  setTime(time: string | number): void;
665
833
  setTimeAndDepth(time: string | number, depth: number): void;
@@ -744,6 +912,8 @@ declare interface SimulationParams {
744
912
  opacity: number;
745
913
  logScale: boolean;
746
914
  vibrance: number;
915
+ /** Grade of the trail composite; off by default. */
916
+ colorGrade: ColorGrade;
747
917
  scalarMode: boolean;
748
918
  particleState: ParticleStateMode;
749
919
  rgba8MaxParticleZoom: number;
@@ -829,8 +999,26 @@ export declare interface VariableMeta {
829
999
  units?: string;
830
1000
  }
831
1001
 
1002
+ /**
1003
+ * Vector GeoVideo speeds: each step re-seeds the particles, so trails need
1004
+ * time to settle (the projection demo plays 0.2 to 2 steps per second).
1005
+ */
1006
+ export declare const VECTOR_GEOVIDEO_SPEEDS: readonly number[];
1007
+
832
1008
  export declare type VectorDerivation = DirectionMagnitudeVectorDerivation;
833
1009
 
1010
+ /**
1011
+ * Field source contract used by VectorLayer. ZarrSource satisfies it; frame
1012
+ * sources such as GeoVideoVectorSource implement it to feed decoded frames.
1013
+ */
1014
+ declare type VectorFieldSource = Pick<ZarrSource, "init" | "cancelAll" | "getCoords" | "getDimensions" | "getChunkShape" | "findTimeIndex" | "findDepthIndex" | "getChunksForBounds" | "fetchSpatialChunkResult"> & {
1015
+ /**
1016
+ * Fixed symmetric component domain [-d, d]. When provided, it replaces the
1017
+ * per-frame min/max so speed and color normalization stay stable in time.
1018
+ */
1019
+ getValueDomain?(): number;
1020
+ };
1021
+
834
1022
  export declare class VectorLayer implements CustomLayerInterface {
835
1023
  readonly id: string;
836
1024
  readonly type: "custom";
@@ -864,7 +1052,7 @@ export declare class VectorLayer implements CustomLayerInterface {
864
1052
  private inflight;
865
1053
  private listeners;
866
1054
  constructor(options: VectorLayerOptions);
867
- onAdd(map: Map_2, gl: WebGLRenderingContext): Promise<void>;
1055
+ onAdd(map: Map_2, context: WebGLRenderingContext | WebGL2RenderingContext): Promise<void>;
868
1056
  private initAsync;
869
1057
  private timeToMs;
870
1058
  private computeFieldMeta;
@@ -876,14 +1064,15 @@ export declare class VectorLayer implements CustomLayerInterface {
876
1064
  */
877
1065
  private fetchVelocityData;
878
1066
  private loadViewportVelocity;
879
- render(gl: WebGLRenderingContext, options: CustomRenderMethodInput): void;
1067
+ render(context: WebGLRenderingContext | WebGL2RenderingContext, options: CustomRenderMethodInput): void;
880
1068
  onRemove(): void;
881
1069
  setTime(time: string | number): void;
882
1070
  setTimeAndDepth(time: string | number, depth: number): void;
883
1071
  /**
884
1072
  * Pre-fetch velocity data for a future time step without swapping the active
885
1073
  * field. Fires `frameBuffered(ms)` when ready so playback can advance
886
- * immediately without a loading gap.
1074
+ * immediately without a loading gap. Rejects when the frame cannot be
1075
+ * loaded, so playback can stop instead of waiting for it.
887
1076
  */
888
1077
  prefetchTime(ms: number): Promise<void>;
889
1078
  /** True when the frame for the given time (ms) is already in the cache. */
@@ -902,6 +1091,7 @@ export declare class VectorLayer implements CustomLayerInterface {
902
1091
  setOpacity(v: number): void;
903
1092
  setLogScale(v: boolean): void;
904
1093
  setVibrance(v: number): void;
1094
+ setColorGrade(grade: Partial<ColorGrade>): void;
905
1095
  getDebugInfo(): VectorLayerDebugInfo;
906
1096
  on<K extends keyof LayerEventMap_2>(event: K, handler: LayerEventMap_2[K]): this;
907
1097
  off<K extends keyof LayerEventMap_2>(event: K, handler: LayerEventMap_2[K]): this;
@@ -925,7 +1115,7 @@ export declare interface VectorLayerOptions {
925
1115
  id: string;
926
1116
  source: string;
927
1117
  /** Optional initialized/shared source. When omitted, the layer creates one from `source`. */
928
- zarrSource?: ZarrSource;
1118
+ zarrSource?: VectorFieldSource;
929
1119
  variableU?: string;
930
1120
  variableV?: string;
931
1121
  vectorDerivation?: VectorDerivation;
@@ -950,6 +1140,11 @@ export declare interface VectorLayerOptions {
950
1140
  logScale?: boolean;
951
1141
  /** Vibrance adjustment [-1, 1]. Default 0.0. */
952
1142
  vibrance?: number;
1143
+ /**
1144
+ * Particle trail color grade. Omitted: off; missing values take the
1145
+ * vibrant preset (see resolveColorGrade).
1146
+ */
1147
+ colorGrade?: Partial<ColorGrade>;
953
1148
  unit?: string;
954
1149
  }
955
1150
 
@@ -995,7 +1190,7 @@ export declare class VelocityField {
995
1190
  };
996
1191
  scalarMode: boolean;
997
1192
  private linearFilter;
998
- init(gl: WebGLRenderingContext): void;
1193
+ init(gl: WebGL2RenderingContext): void;
999
1194
  /**
1000
1195
  * Upload velocity data as a 2-channel (RG) texture.
1001
1196
  * Input: u/v Float32Arrays with real physical values.
@@ -1201,8 +1396,12 @@ export declare class Zartigl {
1201
1396
  private timeRange?;
1202
1397
  private fullTimeMeta;
1203
1398
  private autoplay;
1399
+ /** Autoplay applies to the first layer only: later sources start paused. */
1400
+ private autoplayPending;
1401
+ private playing;
1204
1402
  private loop;
1205
1403
  private playbackRate;
1404
+ private stepsPerSecond;
1206
1405
  private readonly metadata?;
1207
1406
  private readonly before?;
1208
1407
  private visible;
@@ -1211,7 +1410,19 @@ export declare class Zartigl {
1211
1410
  private layer;
1212
1411
  private time;
1213
1412
  private pendingTime;
1413
+ /** Latest time the caller asked for, ahead of the queued update applying it. */
1414
+ private requestedTime;
1415
+ /** Bumped by caller pauses and time requests, which cancel a chunk handoff. */
1416
+ private playbackIntent;
1417
+ /** Order of caller requests: updates and play() calls. */
1418
+ private requestSeq;
1419
+ /** Request that started the current playback; 0 once paused. */
1420
+ private playSeq;
1214
1421
  private depth;
1422
+ /** Depth the caller selected, kept across sources; null shows the surface. */
1423
+ private depthRequest;
1424
+ /** GeoVideo manifests that failed to load; "auto" falls back to Zarr. */
1425
+ private failedGeoVideo;
1215
1426
  private settings;
1216
1427
  private colorDomainOverridden;
1217
1428
  private paletteOverridden;
@@ -1224,6 +1435,9 @@ export declare class Zartigl {
1224
1435
  private geoVideoManifest;
1225
1436
  private wmtsMetadata;
1226
1437
  private fieldSources;
1438
+ private archiveChunks;
1439
+ /** Manifest of the archive chunk expected next, requested ahead of it. */
1440
+ private nextChunkManifest;
1227
1441
  private activeFieldSource;
1228
1442
  private switchGeneration;
1229
1443
  private destroyed;
@@ -1240,9 +1454,23 @@ export declare class Zartigl {
1240
1454
  constructor(options: ZartiglOptions);
1241
1455
  init(): Promise<void>;
1242
1456
  update(change: ZartiglUpdate): Promise<void>;
1457
+ /**
1458
+ * Queue a change after earlier ones. Internal seeks, such as an archive
1459
+ * chunk handoff, come here directly and keep playback going.
1460
+ */
1461
+ private enqueue;
1243
1462
  private applyUpdate;
1244
1463
  private loadLayer;
1464
+ private loadSource;
1245
1465
  private changeSource;
1466
+ /**
1467
+ * Source switch after init: leave the current GeoVideo when it cannot show
1468
+ * the requested time, window, or depth and another source can. A time
1469
+ * change alone never switches back, so scrubbing a Zarr timeline does not
1470
+ * flip sources; in "auto", selecting the surface depth, alone or with a
1471
+ * date GeoVideo covers, returns to GeoVideo.
1472
+ */
1473
+ private needsSourceSwitch;
1246
1474
  private applyVisible;
1247
1475
  /** Pause rendering and abort field requests without discarding layer state. */
1248
1476
  suspend(): void;
@@ -1250,10 +1478,31 @@ export declare class Zartigl {
1250
1478
  resume(): void;
1251
1479
  destroy(): void;
1252
1480
  private applyTime;
1481
+ /** Start GeoVideo playback; does nothing where playback is unavailable. */
1253
1482
  play(): Promise<void>;
1483
+ private startPlayback;
1484
+ /**
1485
+ * Show the previous or next time step, stopping playback. Stays at the
1486
+ * first and last steps: resolves false when there is no step to move to.
1487
+ */
1488
+ stepTime(offset: -1 | 1): Promise<boolean>;
1489
+ /**
1490
+ * Time step before or after the latest requested time, so quick successive
1491
+ * steps add up; undefined past the first or last step.
1492
+ */
1493
+ getSteppedTime(offset: -1 | 1): number | undefined;
1254
1494
  pause(): void;
1255
1495
  private applyLoop;
1256
1496
  private applyPlaybackRate;
1497
+ private applyPlaybackSpeed;
1498
+ /** Media rate for the loaded artifact: steps per second when set, else the raw multiplier. */
1499
+ private effectivePlaybackRate;
1500
+ private playbackSpeeds;
1501
+ /**
1502
+ * Steps per second the active source shows. Vector GeoVideo stays inside
1503
+ * its speeds; scalar media follows the request, or the raw multiplier.
1504
+ */
1505
+ private appliedStepsPerSecond;
1257
1506
  /** Apply or clear a time window without rebuilding source metadata or the map layer. */
1258
1507
  private applyTimeRange;
1259
1508
  private applyDepth;
@@ -1275,9 +1524,14 @@ export declare class Zartigl {
1275
1524
  render: true;
1276
1525
  time: boolean;
1277
1526
  depth: boolean;
1527
+ /** Automatic playback (play, stop, loop, speed): GeoVideo only. */
1528
+ playback: boolean;
1278
1529
  pointQuery: boolean;
1279
1530
  sourceTypes: CatalogSource["type"][];
1280
1531
  };
1532
+ getPlaybackState(): PlaybackState;
1533
+ /** GeoVideo with more than one step, or a scalar looping snapshot, plays. */
1534
+ private hasPlayback;
1281
1535
  /** Whether palette/domain styling is applied to values in the active renderer. */
1282
1536
  supportsDynamicStyle(): boolean;
1283
1537
  getDebugInfo(): ZartiglDebugInfo;
@@ -1285,10 +1539,66 @@ export declare class Zartigl {
1285
1539
  queryTimeSeries(options: QueryPointOptions): Promise<ZarrPointSeriesResult>;
1286
1540
  queryDepthProfile(options: QueryDepthProfileOptions): Promise<ZarrPointSeriesResult>;
1287
1541
  private attachWhenReady;
1542
+ /** GeoVideo playback options shared by a new layer and an in-place swap. */
1543
+ private geoVideoPlayback;
1544
+ /**
1545
+ * Show another artifact of the same scalar GeoVideo entry, such as the next
1546
+ * archive chunk, in the attached layer. The last frame stays on screen until
1547
+ * the new media is ready, instead of a blank map while a new layer loads.
1548
+ */
1549
+ private replaceGeoVideoInPlace;
1288
1550
  private detach;
1551
+ /**
1552
+ * In "auto", a GeoVideo that fails while it renders (media, mask) yields
1553
+ * to another source, paused, keeping the date and depth.
1554
+ */
1555
+ private fallBackFromGeoVideo;
1289
1556
  private applyMutableSettings;
1290
1557
  private getQuerySource;
1291
1558
  private getFieldSource;
1559
+ /**
1560
+ * Time the caller asked for, so selection skips fixed-period sources (such
1561
+ * as a GeoVideo artifact) that cannot show it. A trailing window targets now.
1562
+ */
1563
+ private sourceTimeRequest;
1564
+ /**
1565
+ * Source for a preference and target. In "auto", a GeoVideo that cannot show
1566
+ * the requested depth (it renders the surface level) yields to Zarr.
1567
+ */
1568
+ private selectSource;
1569
+ private isFailedGeoVideo;
1570
+ /** The entry without GeoVideo artifacts that failed to load. */
1571
+ private withoutFailedGeoVideo;
1572
+ /** Whether `depth` resolves to the surface level that GeoVideo renders. */
1573
+ private geoVideoServesDepth;
1574
+ private isArchiveChunk;
1575
+ /**
1576
+ * The archive chunk that follows the current one inside the active time
1577
+ * range, or with loop on, the first one, with the time playback resumes at.
1578
+ */
1579
+ private nextArchiveChunk;
1580
+ /** A chunk manifest, reusing the one requested ahead for the next chunk. */
1581
+ private loadChunkManifest;
1582
+ /**
1583
+ * While a scalar archive chunk plays, load the next chunk into the layer so
1584
+ * playback moves on without waiting for its manifest and media.
1585
+ */
1586
+ private preloadNextArchiveChunk;
1587
+ /**
1588
+ * Keep playing across an archive: when a chunk ends, play the next chunk
1589
+ * inside the active time range, or with loop on, the first one.
1590
+ */
1591
+ private continueArchivePlayback;
1592
+ /** The entry's initialised field Zarr, or null when it has none. */
1593
+ private entryFieldZarr;
1594
+ /** Zarr time and vertical axes; unavailable metadata leaves the artifact's own timeline. */
1595
+ private zarrAxes;
1596
+ /**
1597
+ * Entry whose archive sources are replaced by one fixed-period source per
1598
+ * published chunk. An unreachable index drops the archive, so selection
1599
+ * falls back to the entry's other sources.
1600
+ */
1601
+ private resolveArchives;
1292
1602
  private resolveSource;
1293
1603
  private querySource;
1294
1604
  private isMapReady;
@@ -1329,7 +1639,9 @@ export declare interface ZartiglDebugInfo {
1329
1639
  depth: number;
1330
1640
  settings: Partial<ZartiglSettings>;
1331
1641
  timeRange?: TimeRange;
1332
- geoVideo: Required<GeoVideoOptions>;
1642
+ geoVideo: Required<Omit<GeoVideoOptions, "stepsPerSecond">> & {
1643
+ stepsPerSecond: number | null;
1644
+ };
1333
1645
  layer: CatalogRenderLayerDebugInfo | null;
1334
1646
  }
1335
1647
 
@@ -1342,6 +1654,11 @@ declare type ZartiglEventMap = {
1342
1654
  cacheInvalidated: () => void;
1343
1655
  timeChange: (time: number) => void;
1344
1656
  playbackChange: (playing: boolean) => void;
1657
+ /** The active source changed, including an automatic fallback. */
1658
+ sourceChange: (source: {
1659
+ id: string;
1660
+ type: CatalogSource["type"];
1661
+ }) => void;
1345
1662
  };
1346
1663
 
1347
1664
  export declare class ZartiglFrameUnavailableError extends Error {
@@ -1376,6 +1693,11 @@ export declare interface ZartiglSettings {
1376
1693
  opacity: number;
1377
1694
  logScale: boolean;
1378
1695
  vibrance: number;
1696
+ /**
1697
+ * Particle trail color grade; off by default. Missing values take the
1698
+ * vibrant preset (see resolveColorGrade).
1699
+ */
1700
+ colorGrade: Partial<ColorGrade>;
1379
1701
  /** Fixed physical-value domain for scalar colors. Null uses frame extrema. */
1380
1702
  colorDomain: [number, number] | null;
1381
1703
  particleState: ParticleStateMode;