@fxi/zartigl 0.5.0 → 0.7.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
@@ -2,6 +2,21 @@ import { CustomLayerInterface } from 'maplibre-gl';
2
2
  import { CustomRenderMethodInput } from 'maplibre-gl';
3
3
  import { Map as Map_2 } from 'maplibre-gl';
4
4
 
5
+ /** Particle backdrop off: see BackdropStyle. */
6
+ export declare const BACKDROP_OFF: Readonly<BackdropStyle>;
7
+
8
+ /**
9
+ * Solid color under the particle trails, drawn on valid data cells only: it
10
+ * keeps particles readable over a light basemap. Off by default; an object
11
+ * without `enabled` is on.
12
+ */
13
+ declare interface BackdropStyle {
14
+ enabled: boolean;
15
+ /** Hex color, `#rgb` or `#rrggbb`. */
16
+ color: string;
17
+ opacity: number;
18
+ }
19
+
5
20
  export declare function buildMapxWidgetSnippet(options: MapxWidgetSnippetOptions): string;
6
21
 
7
22
  export declare function buildStandaloneDemoHtml(options: StandaloneDemoSnippetOptions): string;
@@ -41,31 +56,31 @@ export declare interface CatalogEntry {
41
56
  tags?: string[];
42
57
  kind: "scalar" | "vector";
43
58
  sources: CatalogSource[];
44
- defaults: {
59
+ /** Default source, plus the layer's base style. */
60
+ defaults: LayerStyleInput & {
45
61
  sourceId: string;
46
62
  querySourceId?: string;
47
- palette?: string;
48
- renderMode?: "particles" | "raster" | "raster+particles";
49
- particles?: {
50
- density?: number;
51
- speed?: number;
52
- fade?: number;
53
- };
54
- raster?: {
55
- opacity?: number;
56
- logScale?: boolean;
57
- vibrance?: number;
58
- colorDomain?: [number, number];
59
- };
60
63
  };
61
64
  }
62
65
 
66
+ /**
67
+ * An incremental archive of calendar-aligned artifacts listed by a mutable
68
+ * index; zartigl resolves it to the chunk covering the requested time.
69
+ */
70
+ declare interface CatalogGeoVideoArchiveSource extends CatalogSourceBase {
71
+ type: "geovideo";
72
+ indexUrl: string;
73
+ manifestUrl?: never;
74
+ }
75
+
76
+ /** One published artifact. */
63
77
  declare interface CatalogGeoVideoSource extends CatalogSourceBase {
64
78
  type: "geovideo";
65
79
  manifestUrl: string;
80
+ indexUrl?: never;
66
81
  }
67
82
 
68
- declare type CatalogRenderLayerBackend = "vector-zarr" | "scalar-zarr" | "scalar-geovideo" | "scalar-wmts";
83
+ declare type CatalogRenderLayerBackend = "vector-zarr" | "scalar-zarr" | "scalar-geovideo" | "vector-geovideo" | "scalar-wmts";
69
84
 
70
85
  declare type CatalogRenderLayerDebugInfo = {
71
86
  id: string;
@@ -81,7 +96,7 @@ declare type CatalogRenderLayerDebugInfo = {
81
96
  delegate: VectorLayerDebugInfo | ScalarLayerDebugInfo | GeoVideoLayerDebugInfo | null;
82
97
  };
83
98
 
84
- export declare type CatalogSource = CatalogZarrSource | CatalogWmtsSource | CatalogGeoVideoSource;
99
+ export declare type CatalogSource = CatalogZarrSource | CatalogWmtsSource | CatalogGeoVideoSource | CatalogGeoVideoArchiveSource;
85
100
 
86
101
  declare interface CatalogSourceBase {
87
102
  id: string;
@@ -144,6 +159,31 @@ declare interface CatalogZarrSource extends CatalogSourceBase {
144
159
  variables: CatalogVariables;
145
160
  }
146
161
 
162
+ export declare const COLOR_GRADE_OFF: Readonly<ColorGrade>;
163
+
164
+ export declare const COLOR_GRADE_VIBRANT: Readonly<ColorGrade>;
165
+
166
+ /**
167
+ * Optional color grade applied when particle trails are composited
168
+ * (`shaders/color-grade.glsl`): more intense highlights, deeper shadows and
169
+ * richer colors, without hard clipping. Off by default; when off, the
170
+ * composite returns the trail colors unchanged.
171
+ */
172
+ export declare interface ColorGrade {
173
+ enabled: boolean;
174
+ /** Mid-tone lift [0, 4]; 0 keeps the tones. */
175
+ exposure: number;
176
+ /** S-curve strength [0, 1]. */
177
+ contrast: number;
178
+ /** Vibrance-weighted saturation [-1, 1]. */
179
+ saturation: number;
180
+ /** Push of the bright half toward white [0, 1]. */
181
+ highlights: number;
182
+ }
183
+
184
+ /** Style `particles.grade` of a color grade: a preset when it is one. */
185
+ export declare function colorGradeToGrade(input: Partial<ColorGrade>): ParticleGrade;
186
+
147
187
  export declare type ColorRampInput = string | string[] | Record<number, string>;
148
188
 
149
189
  declare interface CoordArrays {
@@ -182,7 +222,15 @@ export declare interface FieldMeta {
182
222
  depth?: number;
183
223
  }
184
224
 
185
- export declare type GeoVideoBounds = [west: number, south: number, east: number, north: number];
225
+ /** GeoVideo playback speeds, in time steps per second. */
226
+ export declare const GEOVIDEO_SPEEDS: readonly number[];
227
+
228
+ export declare type GeoVideoBounds = [
229
+ west: number,
230
+ south: number,
231
+ east: number,
232
+ north: number
233
+ ];
186
234
 
187
235
  declare type GeoVideoEventMap = {
188
236
  loading: () => void;
@@ -191,6 +239,8 @@ declare type GeoVideoEventMap = {
191
239
  status: (status: ZartiglStatus) => void;
192
240
  timeChange: (time: number) => void;
193
241
  playbackChange: (playing: boolean) => void;
242
+ /** Playback reached the end of the timeline without looping. */
243
+ playbackEnd: () => void;
194
244
  };
195
245
 
196
246
  export declare class GeoVideoLayer implements CustomLayerInterface {
@@ -198,7 +248,8 @@ export declare class GeoVideoLayer implements CustomLayerInterface {
198
248
  readonly type: "custom";
199
249
  readonly renderingMode: "3d";
200
250
  private readonly source;
201
- private readonly autoplay;
251
+ /** Start playback once ready: autoplay, or a swap that was playing. */
252
+ private playOnReady;
202
253
  private loop;
203
254
  private playbackRate;
204
255
  private requestedTime;
@@ -213,6 +264,14 @@ export declare class GeoVideoLayer implements CustomLayerInterface {
213
264
  private gl;
214
265
  private manifest;
215
266
  private video;
267
+ /** Manifest of `video`, which still differs from `manifest` mid-swap. */
268
+ private videoManifest;
269
+ /** Incoming media of a swap, shown once its first frame is decodable. */
270
+ private pendingVideo;
271
+ private standby;
272
+ /** Ended on a preloaded artifact: playing until it is swapped in or paused. */
273
+ private awaitingNext;
274
+ private maskUrl;
216
275
  private colorTexture;
217
276
  private maskTexture;
218
277
  private colorRampTexture;
@@ -245,12 +304,33 @@ export declare class GeoVideoLayer implements CustomLayerInterface {
245
304
  private lastUploadDurationMs;
246
305
  private abortController;
247
306
  private resumePlayback;
307
+ private stepTimer;
308
+ private endEmitted;
248
309
  private listeners;
249
310
  constructor(options: GeoVideoLayerOptions);
250
- onAdd(map: Map_2, gl: WebGLRenderingContext): Promise<void>;
251
- render(gl: WebGLRenderingContext, options: CustomRenderMethodInput): void;
311
+ onAdd(map: Map_2, context: WebGLRenderingContext | WebGL2RenderingContext): Promise<void>;
312
+ render(context: WebGLRenderingContext | WebGL2RenderingContext, options: CustomRenderMethodInput): void;
252
313
  onRemove(): void;
253
314
  setTime(time: string | number): void;
315
+ /**
316
+ * Switch to another artifact of the same frame size, such as the next
317
+ * archive chunk, keeping the map layer and GL state. The current frame stays
318
+ * on screen until the new media can show its first one, at once when it was
319
+ * preloaded. Returns false when the artifact needs a new layer.
320
+ */
321
+ replaceManifest(manifest: GeoVideoManifest, options?: GeoVideoReplaceOptions): boolean;
322
+ /**
323
+ * Load the artifact expected next, such as the following archive chunk, so
324
+ * `replaceManifest` can show it without waiting for the network. Until then,
325
+ * reaching the end of the current artifact keeps the playing state and
326
+ * emits `playbackEnd` for the caller to swap it in, or to pause. Returns
327
+ * false when the artifact cannot replace the current one in place.
328
+ */
329
+ preloadManifest(manifest: GeoVideoManifest, options?: GeoVideoReplaceOptions): boolean;
330
+ /** Clamped time range of a compatible replacement, else null. */
331
+ private replacementTimeRange;
332
+ /** The preloaded artifact when it holds this media; any other is dropped. */
333
+ private takeStandby;
254
334
  setTimeAndDepth(time: string | number, _depth: number): void;
255
335
  setDepth(_depth: number): void;
256
336
  prefetchTime(_ms: number): Promise<void>;
@@ -266,6 +346,24 @@ export declare class GeoVideoLayer implements CustomLayerInterface {
266
346
  setOpacity(value: number): void;
267
347
  play(): Promise<void>;
268
348
  pause(): void;
349
+ /**
350
+ * Playback is a request, applied once ready, while the manifest loads,
351
+ * mid-swap, or at an end waiting for the preloaded artifact.
352
+ */
353
+ private deferredPlayback;
354
+ private isPlaying;
355
+ /**
356
+ * Below the slowest media rate browsers accept, keep the video paused and
357
+ * seek one sample (or frame) at a time.
358
+ */
359
+ private startStepping;
360
+ private stopStepping;
361
+ private step;
362
+ /**
363
+ * The timeline ended without looping. With a preloaded artifact, keep the
364
+ * playing state for the caller to swap it in; otherwise stop.
365
+ */
366
+ private reachEnd;
269
367
  setLoop(loop: boolean): void;
270
368
  setPlaybackRate(rate: number): void;
271
369
  setTimeRange(range: [number, number]): void;
@@ -275,8 +373,28 @@ export declare class GeoVideoLayer implements CustomLayerInterface {
275
373
  off<K extends keyof GeoVideoEventMap>(event: K, handler: GeoVideoEventMap[K]): this;
276
374
  private initGl;
277
375
  private initVideo;
376
+ /**
377
+ * Media element for a manifest. Its events drive the layer only while it is
378
+ * the current video and no swap is pending.
379
+ */
380
+ private createVideo;
381
+ /** Track presented frames of the current video. */
382
+ private startFrameCallbacks;
383
+ /**
384
+ * Time a loading video opens at: its preload target, or the requested time
385
+ * within the active range.
386
+ */
387
+ private startTime;
388
+ private checkVideoSize;
389
+ private cancelFrameCallback;
390
+ /** Stop the current video and free its media resources. */
391
+ private releaseVideo;
392
+ private discardStandby;
393
+ private discardPendingVideo;
278
394
  private bufferFrame;
279
- private loadStaticMask;
395
+ private createMaskImage;
396
+ /** Show the mask of a manifest once its image, possibly preloaded, loads. */
397
+ private applyMask;
280
398
  private emitReady;
281
399
  private createVideoTexture;
282
400
  private uploadTextureSource;
@@ -296,6 +414,8 @@ export declare interface GeoVideoLayerDebugInfo {
296
414
  currentTime: number;
297
415
  manifestId?: string;
298
416
  mediaUrl?: string;
417
+ /** Media loaded ahead for the next swap. */
418
+ preloadedMediaUrl?: string;
299
419
  decodedFrames: number;
300
420
  bufferedFrames: number;
301
421
  skippedFrames: number;
@@ -339,17 +459,7 @@ export declare interface GeoVideoManifest {
339
459
  durationSeconds: number;
340
460
  codec: "h264" | string;
341
461
  };
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
- };
462
+ encoding: GeoVideoScalarEncoding | GeoVideoVectorEncoding;
353
463
  mask: {
354
464
  kind: "static-validity";
355
465
  url: string;
@@ -379,7 +489,10 @@ export declare interface GeoVideoManifest {
379
489
  export declare interface GeoVideoOptions {
380
490
  autoplay?: boolean;
381
491
  loop?: boolean;
492
+ /** Media rate multiplier; prefer `stepsPerSecond`, which means the same on every artifact. */
382
493
  playbackRate?: number;
494
+ /** Time steps shown per second; takes precedence over `playbackRate`. */
495
+ stepsPerSecond?: number;
383
496
  }
384
497
 
385
498
  export declare interface GeoVideoRangeTimeline {
@@ -389,11 +502,34 @@ export declare interface GeoVideoRangeTimeline {
389
502
  interpolation: "linear" | "nearest";
390
503
  }
391
504
 
505
+ /**
506
+ * Playback state for an artifact swapped in with `replaceManifest`, or
507
+ * loaded ahead with `preloadManifest`.
508
+ */
509
+ declare interface GeoVideoReplaceOptions {
510
+ time?: number;
511
+ timeRange?: [number, number];
512
+ loop?: boolean;
513
+ playbackRate?: number;
514
+ }
515
+
392
516
  export declare interface GeoVideoSampleSequenceTimeline {
393
517
  kind: "sample-sequence";
394
518
  values: string[];
395
519
  }
396
520
 
521
+ declare interface GeoVideoScalarEncoding {
522
+ kind: "scalar-luma";
523
+ bits: 8;
524
+ codeMin: number;
525
+ codeMax: number;
526
+ valueMin: number;
527
+ valueMax: number;
528
+ transfer: "linear";
529
+ colorSpace: "bt709";
530
+ colorRange: "limited" | "full";
531
+ }
532
+
397
533
  export declare function geoVideoSecondsForTime(manifest: GeoVideoManifest, timeMs: number): number;
398
534
 
399
535
  export declare interface GeoVideoSnapshotLoopTimeline {
@@ -405,10 +541,46 @@ export declare function geoVideoTimeForSeconds(manifest: GeoVideoManifest, secon
405
541
 
406
542
  export declare function geoVideoTimelineValues(manifest: GeoVideoManifest): number[];
407
543
 
544
+ /**
545
+ * u and v stacked vertically in luma (u rows on top). Each component maps
546
+ * [-valueDomain, valueDomain] to [codeMin, codeMax]; "sqrt" stores
547
+ * sign(x)·sqrt(|x|/valueDomain) to keep precision on slow flows. The static
548
+ * mask covers one component (media height / 2).
549
+ */
550
+ declare interface GeoVideoVectorEncoding {
551
+ kind: "vector-luma";
552
+ bits: 8;
553
+ codeMin: number;
554
+ codeMax: number;
555
+ valueDomain: number;
556
+ transfer: "linear" | "sqrt";
557
+ layout: "stacked-uv";
558
+ colorSpace: "bt709";
559
+ colorRange: "limited" | "full";
560
+ }
561
+
408
562
  export declare function getPalettes(): PaletteMeta[];
409
563
 
410
564
  export declare function getVectorDerivationVariables(derivation: VectorDerivation): [string, string];
411
565
 
566
+ /** Color grade of a style `particles.grade`; an object tunes the vibrant one. */
567
+ export declare function gradeToColorGrade(grade: ParticleGrade | undefined): ColorGrade;
568
+
569
+ /** Vibrant grade values; each one left out takes the vibrant preset's. */
570
+ declare interface GradeTuning {
571
+ /** Mid-tone lift [0, 4]. */
572
+ exposure?: number;
573
+ /** S-curve strength [0, 1]. */
574
+ contrast?: number;
575
+ /** Vibrance-weighted saturation [-1, 1]. */
576
+ saturation?: number;
577
+ /** Push of the bright half toward white [0, 1]. */
578
+ highlights?: number;
579
+ }
580
+
581
+ /** A `#rgb` or `#rrggbb` color. */
582
+ export declare const isHexColor: (value: unknown) => value is string;
583
+
412
584
  declare type LayerEventMap = {
413
585
  loading: () => void;
414
586
  loaded: (meta: FieldMeta) => void;
@@ -429,6 +601,25 @@ declare type LayerEventMap_2 = {
429
601
  cacheInvalidated: () => void;
430
602
  };
431
603
 
604
+ declare interface LayerStyle {
605
+ /** Palette id; undefined lets the engine pick its own. */
606
+ palette?: string;
607
+ renderMode: StyleRenderMode;
608
+ particles: ParticleStyle;
609
+ raster: RasterStyle;
610
+ }
611
+
612
+ /** A partial style: any key left out takes the value below it. */
613
+ declare interface LayerStyleInput {
614
+ palette?: string;
615
+ renderMode?: StyleRenderMode;
616
+ particles?: ParticleStyleInput;
617
+ raster?: Partial<RasterStyle>;
618
+ }
619
+
620
+ /** Where each deprecated flat style setting moved in the style. */
621
+ export declare const LEGACY_STYLE_SETTINGS: Readonly<Partial<Record<keyof ZartiglSettings, string>>>;
622
+
432
623
  export declare type Legend = {
433
624
  type: "gradient";
434
625
  palette: string;
@@ -450,12 +641,20 @@ declare type LocalizedText = Record<string, string>;
450
641
  export declare interface MapxWidgetSnippetOptions {
451
642
  layerId: string;
452
643
  layerKind: CatalogEntry["kind"];
644
+ /**
645
+ * The layer's own style, the catalog `defaults`: only override keys that
646
+ * differ from it are written. Unknown, the whole override is written.
647
+ */
648
+ layerDefaults?: LayerStyleInput;
453
649
  source?: string;
454
650
  sourceType?: SnippetSourceType;
455
651
  time?: string | number | Date;
456
652
  timeRange?: TimeRange;
457
653
  geoVideo?: GeoVideoOptions;
458
654
  depth?: number;
655
+ /** Style override; only keys that differ from the layer's own are written. */
656
+ style?: LayerStyleInput;
657
+ /** Engine tuning; flat style keys are ignored. */
459
658
  settings?: Partial<ZartiglSettings>;
460
659
  }
461
660
 
@@ -466,12 +665,16 @@ export declare interface PaletteMeta {
466
665
  colors: string[];
467
666
  }
468
667
 
668
+ /** Particle trail color grade: a preset, or the vibrant one tuned. */
669
+ declare type ParticleGrade = "off" | "vibrant" | GradeTuning;
670
+
469
671
  export declare class ParticleSimulation {
470
672
  private gl;
471
673
  private params;
472
674
  private updateProgram;
473
675
  private drawProgram;
474
676
  private fadeProgram;
677
+ private compositeProgram;
475
678
  private gridProgram;
476
679
  private gridGlobeProgram;
477
680
  private particleStateTextures;
@@ -482,12 +685,9 @@ export declare class ParticleSimulation {
482
685
  private numParticles;
483
686
  private prevBounds;
484
687
  private redistributionBoost;
485
- private cameraMoving;
486
- private currentFadeOpacity;
487
- private fadeTransitionStartOpacity;
488
- private fadeTransitionTargetOpacity;
489
- private fadeTransitionStartMs;
490
- private fadeTransitionDurationMs;
688
+ /** Advection counter seeding the update shader random streams. */
689
+ private frame;
690
+ private readonly trailFade;
491
691
  private webglInfo;
492
692
  private stateFormat;
493
693
  private lastRgba8ParticlesSuppressed;
@@ -504,10 +704,11 @@ export declare class ParticleSimulation {
504
704
  private updateLocs;
505
705
  private drawLocs;
506
706
  private fadeLocs;
707
+ private compositeLocs;
507
708
  private gridLocs;
508
709
  private gridGlobeLocs;
509
710
  constructor(params?: Partial<SimulationParams>);
510
- init(gl: WebGLRenderingContext): void;
711
+ init(gl: WebGL2RenderingContext): void;
511
712
  private getUniforms;
512
713
  /** Random initial particle positions, matching the active state format. */
513
714
  private makeStateData;
@@ -539,13 +740,13 @@ export declare class ParticleSimulation {
539
740
  south: number;
540
741
  east: number;
541
742
  north: number;
542
- }, opacityScale?: number): void;
743
+ }, opacityScale?: number, solid?: Vec4): void;
543
744
  renderGridGlobe(velocityTexUnit: number, velocityMin: [number, number], velocityMax: [number, number], matrix: Iterable<number>, geoBounds: {
544
745
  west: number;
545
746
  south: number;
546
747
  east: number;
547
748
  north: number;
548
- }, clippingPlane: [number, number, number, number], opacityScale?: number): void;
749
+ }, clippingPlane: [number, number, number, number], opacityScale?: number, solid?: Vec4): void;
549
750
  private setGridFieldUniforms;
550
751
  private bindRasterGrid;
551
752
  private unbindRasterGrid;
@@ -564,6 +765,10 @@ export declare class ParticleSimulation {
564
765
  setOpacity(v: number): void;
565
766
  setLogScale(v: boolean): void;
566
767
  setVibrance(v: number): void;
768
+ /** Partial values take the vibrant preset; see resolveColorGrade. */
769
+ setColorGrade(grade: Partial<ColorGrade>): void;
770
+ /** Missing values take the defaults; see resolveBackdrop. */
771
+ setBackdrop(backdrop: Partial<BackdropStyle>): void;
567
772
  setScalarMode(v: boolean): void;
568
773
  setRgba8MaxParticleZoom(v: number): void;
569
774
  /**
@@ -599,6 +804,8 @@ export declare interface ParticleSimulationDebugInfo {
599
804
  opacity: number;
600
805
  logScale: boolean;
601
806
  vibrance: number;
807
+ colorGrade: ColorGrade;
808
+ backdrop: BackdropStyle;
602
809
  scalarMode: boolean;
603
810
  devicePixelRatio?: number;
604
811
  webgl: WebGLRendererInfo | null;
@@ -608,6 +815,35 @@ export declare type ParticleStateKind = StateTextureFormat["kind"];
608
815
 
609
816
  export declare type ParticleStateMode = "auto" | "rgba8";
610
817
 
818
+ declare interface ParticleStyle {
819
+ /** Particles per device pixel, (0, 0.2]. */
820
+ density: number;
821
+ /** Css px per frame for the fastest value of the field, (0, 10]. */
822
+ speed: number;
823
+ /** Trail length, [0, 1] (longer when higher). */
824
+ fade: number;
825
+ /** Trail color grade; replaced as a whole, not merged. */
826
+ grade: ParticleGrade;
827
+ /** Solid color under the trails; replaced as a whole, not merged. */
828
+ backdrop: BackdropStyle;
829
+ }
830
+
831
+ /** Partial particle style; a partial backdrop takes the defaults' keys. */
832
+ declare type ParticleStyleInput = Partial<Omit<ParticleStyle, "backdrop">> & {
833
+ backdrop?: Partial<BackdropStyle>;
834
+ };
835
+
836
+ export declare interface PlaybackState {
837
+ /** Whether the active source plays automatically: GeoVideo only. */
838
+ available: boolean;
839
+ playing: boolean;
840
+ loop: boolean;
841
+ /** Speed applied by the active source; vector stays within `speeds`. */
842
+ stepsPerSecond: number;
843
+ /** Speeds offered for the active layer kind. */
844
+ speeds: readonly number[];
845
+ }
846
+
611
847
  export declare interface QueryDepthProfileOptions {
612
848
  longitude: number;
613
849
  latitude: number;
@@ -622,7 +858,30 @@ export declare interface QueryPointOptions {
622
858
  maxPoints?: number;
623
859
  }
624
860
 
625
- export declare type RenderMode = 'raster' | 'particles' | 'raster+particles';
861
+ declare interface RasterStyle {
862
+ opacity: number;
863
+ logScale: boolean;
864
+ /** Vibrance adjustment, [-1, 1]. */
865
+ vibrance: number;
866
+ /** Fixed physical-value domain; null follows the data. */
867
+ colorDomain: [number, number] | null;
868
+ }
869
+
870
+ export declare type RenderMode = "raster" | "particles" | "raster+particles";
871
+
872
+ /**
873
+ * Complete backdrop from partial input. Missing or invalid values take the
874
+ * defaults, opacity is clamped; `enabled` defaults to true when any input is
875
+ * given, so `{ opacity: 0.2 }` means "on".
876
+ */
877
+ export declare function resolveBackdrop(input?: Partial<BackdropStyle>): BackdropStyle;
878
+
879
+ /**
880
+ * Complete grade from partial input. Missing or non-finite values take the
881
+ * vibrant preset, others are clamped to their range; `enabled` defaults to
882
+ * true when any input is given, so `{}` means "vibrant".
883
+ */
884
+ export declare function resolveColorGrade(input?: Partial<ColorGrade>): ColorGrade;
626
885
 
627
886
  /** Resolve a native date/time control value against actual available times. */
628
887
  export declare function resolveTimeInputSelection(values: readonly number[], value: string, granularity: TimeGranularity): number | undefined;
@@ -656,10 +915,10 @@ export declare class ScalarLayer implements CustomLayerInterface {
656
915
  private colorDomain;
657
916
  private textureUnit;
658
917
  constructor(options: ScalarLayerOptions);
659
- onAdd(map: Map_2, gl: WebGLRenderingContext): Promise<void>;
918
+ onAdd(map: Map_2, context: WebGLRenderingContext | WebGL2RenderingContext): Promise<void>;
660
919
  private moveEndHandler;
661
920
  private initAsync;
662
- render(gl: WebGLRenderingContext, options: CustomRenderMethodInput): void;
921
+ render(context: WebGLRenderingContext | WebGL2RenderingContext, options: CustomRenderMethodInput): void;
663
922
  onRemove(): void;
664
923
  setTime(time: string | number): void;
665
924
  setTimeAndDepth(time: string | number, depth: number): void;
@@ -744,6 +1003,10 @@ declare interface SimulationParams {
744
1003
  opacity: number;
745
1004
  logScale: boolean;
746
1005
  vibrance: number;
1006
+ /** Grade of the trail composite; off by default. */
1007
+ colorGrade: ColorGrade;
1008
+ /** Solid color under the trails, on valid cells; off by default. */
1009
+ backdrop: BackdropStyle;
747
1010
  scalarMode: boolean;
748
1011
  particleState: ParticleStateMode;
749
1012
  rgba8MaxParticleZoom: number;
@@ -751,6 +1014,17 @@ declare interface SimulationParams {
751
1014
 
752
1015
  declare type SnippetSourceType = "zarr" | "geovideo" | "wmts";
753
1016
 
1017
+ /**
1018
+ * Split flat settings into the style their style keys make and the engine
1019
+ * tuning left. A custom palette ramp, not a palette id, stays in
1020
+ * `settings`. `legacyKeys` lists the style keys found.
1021
+ */
1022
+ export declare function splitLegacySettings(settings?: Partial<ZartiglSettings>): {
1023
+ style: LayerStyleInput;
1024
+ settings: Partial<ZartiglSettings>;
1025
+ legacyKeys: (keyof ZartiglSettings)[];
1026
+ };
1027
+
754
1028
  export declare interface StandaloneDemoSnippetOptions extends MapxWidgetSnippetOptions {
755
1029
  moduleBaseUrl?: string;
756
1030
  center?: [number, number];
@@ -758,7 +1032,8 @@ export declare interface StandaloneDemoSnippetOptions extends MapxWidgetSnippetO
758
1032
  bearing?: number;
759
1033
  pitch?: number;
760
1034
  projection?: "mercator" | "globe";
761
- style?: string;
1035
+ /** MapLibre map style URL; a cloudless imagery style when omitted. */
1036
+ mapStyle?: string;
762
1037
  }
763
1038
 
764
1039
  /**
@@ -798,6 +1073,13 @@ export declare function stitchVelocityChunks(uChunks: Array<{
798
1073
  north: number;
799
1074
  }, latDescending?: boolean, scalarMode?: boolean): VelocityData;
800
1075
 
1076
+ /**
1077
+ * Layer style: how a layer looks, with the same keys in the catalog
1078
+ * `defaults`, the engines and story views. Playback (how time moves) is not
1079
+ * part of it, so `particles.speed` is always the particle speed.
1080
+ */
1081
+ declare type StyleRenderMode = "particles" | "raster" | "raster+particles";
1082
+
801
1083
  export declare type TimeGranularity = "year" | "month" | "day" | "hour" | "minute" | "second";
802
1084
 
803
1085
  export declare interface TimeMeta {
@@ -829,8 +1111,28 @@ export declare interface VariableMeta {
829
1111
  units?: string;
830
1112
  }
831
1113
 
1114
+ declare type Vec4 = [number, number, number, number];
1115
+
1116
+ /**
1117
+ * Vector GeoVideo speeds: each step re-seeds the particles, so trails need
1118
+ * time to settle (the projection demo plays 0.2 to 2 steps per second).
1119
+ */
1120
+ export declare const VECTOR_GEOVIDEO_SPEEDS: readonly number[];
1121
+
832
1122
  export declare type VectorDerivation = DirectionMagnitudeVectorDerivation;
833
1123
 
1124
+ /**
1125
+ * Field source contract used by VectorLayer. ZarrSource satisfies it; frame
1126
+ * sources such as GeoVideoVectorSource implement it to feed decoded frames.
1127
+ */
1128
+ declare type VectorFieldSource = Pick<ZarrSource, "init" | "cancelAll" | "getCoords" | "getDimensions" | "getChunkShape" | "findTimeIndex" | "findDepthIndex" | "getChunksForBounds" | "fetchSpatialChunkResult"> & {
1129
+ /**
1130
+ * Fixed symmetric component domain [-d, d]. When provided, it replaces the
1131
+ * per-frame min/max so speed and color normalization stay stable in time.
1132
+ */
1133
+ getValueDomain?(): number;
1134
+ };
1135
+
834
1136
  export declare class VectorLayer implements CustomLayerInterface {
835
1137
  readonly id: string;
836
1138
  readonly type: "custom";
@@ -864,7 +1166,7 @@ export declare class VectorLayer implements CustomLayerInterface {
864
1166
  private inflight;
865
1167
  private listeners;
866
1168
  constructor(options: VectorLayerOptions);
867
- onAdd(map: Map_2, gl: WebGLRenderingContext): Promise<void>;
1169
+ onAdd(map: Map_2, context: WebGLRenderingContext | WebGL2RenderingContext): Promise<void>;
868
1170
  private initAsync;
869
1171
  private timeToMs;
870
1172
  private computeFieldMeta;
@@ -876,14 +1178,15 @@ export declare class VectorLayer implements CustomLayerInterface {
876
1178
  */
877
1179
  private fetchVelocityData;
878
1180
  private loadViewportVelocity;
879
- render(gl: WebGLRenderingContext, options: CustomRenderMethodInput): void;
1181
+ render(context: WebGLRenderingContext | WebGL2RenderingContext, options: CustomRenderMethodInput): void;
880
1182
  onRemove(): void;
881
1183
  setTime(time: string | number): void;
882
1184
  setTimeAndDepth(time: string | number, depth: number): void;
883
1185
  /**
884
1186
  * Pre-fetch velocity data for a future time step without swapping the active
885
1187
  * field. Fires `frameBuffered(ms)` when ready so playback can advance
886
- * immediately without a loading gap.
1188
+ * immediately without a loading gap. Rejects when the frame cannot be
1189
+ * loaded, so playback can stop instead of waiting for it.
887
1190
  */
888
1191
  prefetchTime(ms: number): Promise<void>;
889
1192
  /** True when the frame for the given time (ms) is already in the cache. */
@@ -902,6 +1205,8 @@ export declare class VectorLayer implements CustomLayerInterface {
902
1205
  setOpacity(v: number): void;
903
1206
  setLogScale(v: boolean): void;
904
1207
  setVibrance(v: number): void;
1208
+ setColorGrade(grade: Partial<ColorGrade>): void;
1209
+ setBackdrop(backdrop: Partial<BackdropStyle>): void;
905
1210
  getDebugInfo(): VectorLayerDebugInfo;
906
1211
  on<K extends keyof LayerEventMap_2>(event: K, handler: LayerEventMap_2[K]): this;
907
1212
  off<K extends keyof LayerEventMap_2>(event: K, handler: LayerEventMap_2[K]): this;
@@ -925,7 +1230,7 @@ export declare interface VectorLayerOptions {
925
1230
  id: string;
926
1231
  source: string;
927
1232
  /** Optional initialized/shared source. When omitted, the layer creates one from `source`. */
928
- zarrSource?: ZarrSource;
1233
+ zarrSource?: VectorFieldSource;
929
1234
  variableU?: string;
930
1235
  variableV?: string;
931
1236
  vectorDerivation?: VectorDerivation;
@@ -950,6 +1255,13 @@ export declare interface VectorLayerOptions {
950
1255
  logScale?: boolean;
951
1256
  /** Vibrance adjustment [-1, 1]. Default 0.0. */
952
1257
  vibrance?: number;
1258
+ /**
1259
+ * Particle trail color grade. Omitted: off; missing values take the
1260
+ * vibrant preset (see resolveColorGrade).
1261
+ */
1262
+ colorGrade?: Partial<ColorGrade>;
1263
+ /** Solid color under the trails, on valid cells. Omitted: off. */
1264
+ backdrop?: Partial<BackdropStyle>;
953
1265
  unit?: string;
954
1266
  }
955
1267
 
@@ -995,7 +1307,7 @@ export declare class VelocityField {
995
1307
  };
996
1308
  scalarMode: boolean;
997
1309
  private linearFilter;
998
- init(gl: WebGLRenderingContext): void;
1310
+ init(gl: WebGL2RenderingContext): void;
999
1311
  /**
1000
1312
  * Upload velocity data as a 2-channel (RG) texture.
1001
1313
  * Input: u/v Float32Arrays with real physical values.
@@ -1201,8 +1513,12 @@ export declare class Zartigl {
1201
1513
  private timeRange?;
1202
1514
  private fullTimeMeta;
1203
1515
  private autoplay;
1516
+ /** Autoplay applies to the first layer only: later sources start paused. */
1517
+ private autoplayPending;
1518
+ private playing;
1204
1519
  private loop;
1205
1520
  private playbackRate;
1521
+ private stepsPerSecond;
1206
1522
  private readonly metadata?;
1207
1523
  private readonly before?;
1208
1524
  private visible;
@@ -1211,8 +1527,22 @@ export declare class Zartigl {
1211
1527
  private layer;
1212
1528
  private time;
1213
1529
  private pendingTime;
1530
+ /** Latest time the caller asked for, ahead of the queued update applying it. */
1531
+ private requestedTime;
1532
+ /** Bumped by caller pauses and time requests, which cancel a chunk handoff. */
1533
+ private playbackIntent;
1534
+ /** Order of caller requests: updates and play() calls. */
1535
+ private requestSeq;
1536
+ /** Request that started the current playback; 0 once paused. */
1537
+ private playSeq;
1214
1538
  private depth;
1539
+ /** Depth the caller selected, kept across sources; null shows the surface. */
1540
+ private depthRequest;
1541
+ /** GeoVideo manifests that failed to load; "auto" falls back to Zarr. */
1542
+ private failedGeoVideo;
1215
1543
  private settings;
1544
+ /** Style override over the layer's own, kept across layers. */
1545
+ private style;
1216
1546
  private colorDomainOverridden;
1217
1547
  private paletteOverridden;
1218
1548
  private lastMeta;
@@ -1224,6 +1554,9 @@ export declare class Zartigl {
1224
1554
  private geoVideoManifest;
1225
1555
  private wmtsMetadata;
1226
1556
  private fieldSources;
1557
+ private archiveChunks;
1558
+ /** Manifest of the archive chunk expected next, requested ahead of it. */
1559
+ private nextChunkManifest;
1227
1560
  private activeFieldSource;
1228
1561
  private switchGeneration;
1229
1562
  private destroyed;
@@ -1239,10 +1572,24 @@ export declare class Zartigl {
1239
1572
  private readonly onMapIdle;
1240
1573
  constructor(options: ZartiglOptions);
1241
1574
  init(): Promise<void>;
1242
- update(change: ZartiglUpdate): Promise<void>;
1575
+ update(input: ZartiglUpdate): Promise<void>;
1576
+ /**
1577
+ * Queue a change after earlier ones. Internal seeks, such as an archive
1578
+ * chunk handoff, come here directly and keep playback going.
1579
+ */
1580
+ private enqueue;
1243
1581
  private applyUpdate;
1244
1582
  private loadLayer;
1583
+ private loadSource;
1245
1584
  private changeSource;
1585
+ /**
1586
+ * Source switch after init: leave the current GeoVideo when it cannot show
1587
+ * the requested time, window, or depth and another source can. A time
1588
+ * change alone never switches back, so scrubbing a Zarr timeline does not
1589
+ * flip sources; in "auto", selecting the surface depth, alone or with a
1590
+ * date GeoVideo covers, returns to GeoVideo.
1591
+ */
1592
+ private needsSourceSwitch;
1246
1593
  private applyVisible;
1247
1594
  /** Pause rendering and abort field requests without discarding layer state. */
1248
1595
  suspend(): void;
@@ -1250,10 +1597,31 @@ export declare class Zartigl {
1250
1597
  resume(): void;
1251
1598
  destroy(): void;
1252
1599
  private applyTime;
1600
+ /** Start GeoVideo playback; does nothing where playback is unavailable. */
1253
1601
  play(): Promise<void>;
1602
+ private startPlayback;
1603
+ /**
1604
+ * Show the previous or next time step, stopping playback. Stays at the
1605
+ * first and last steps: resolves false when there is no step to move to.
1606
+ */
1607
+ stepTime(offset: -1 | 1): Promise<boolean>;
1608
+ /**
1609
+ * Time step before or after the latest requested time, so quick successive
1610
+ * steps add up; undefined past the first or last step.
1611
+ */
1612
+ getSteppedTime(offset: -1 | 1): number | undefined;
1254
1613
  pause(): void;
1255
1614
  private applyLoop;
1256
1615
  private applyPlaybackRate;
1616
+ private applyPlaybackSpeed;
1617
+ /** Media rate for the loaded artifact: steps per second when set, else the raw multiplier. */
1618
+ private effectivePlaybackRate;
1619
+ private playbackSpeeds;
1620
+ /**
1621
+ * Steps per second the active source shows. Vector GeoVideo stays inside
1622
+ * its speeds; scalar media follows the request, or the raw multiplier.
1623
+ */
1624
+ private appliedStepsPerSecond;
1257
1625
  /** Apply or clear a time window without rebuilding source metadata or the map layer. */
1258
1626
  private applyTimeRange;
1259
1627
  private applyDepth;
@@ -1275,20 +1643,93 @@ export declare class Zartigl {
1275
1643
  render: true;
1276
1644
  time: boolean;
1277
1645
  depth: boolean;
1646
+ /** Automatic playback (play, stop, loop, speed): GeoVideo only. */
1647
+ playback: boolean;
1278
1648
  pointQuery: boolean;
1279
1649
  sourceTypes: CatalogSource["type"][];
1280
1650
  };
1651
+ getPlaybackState(): PlaybackState;
1652
+ /** GeoVideo with more than one step, or a scalar looping snapshot, plays. */
1653
+ private hasPlayback;
1281
1654
  /** Whether palette/domain styling is applied to values in the active renderer. */
1282
1655
  supportsDynamicStyle(): boolean;
1656
+ /**
1657
+ * The style drawn: the layer's own, then the override, with what the
1658
+ * source chose, such as a GeoVideo archive's palette.
1659
+ */
1660
+ getStyle(): LayerStyle;
1661
+ /** The style override over the layer's own. */
1662
+ getStyleOverride(): LayerStyleInput;
1283
1663
  getDebugInfo(): ZartiglDebugInfo;
1664
+ /**
1665
+ * Every style key of the shown layer with the override, the palette and
1666
+ * color domain of a GeoVideo archive included, as a load sets them.
1667
+ */
1668
+ private styledSettings;
1284
1669
  private applySettings;
1285
1670
  queryTimeSeries(options: QueryPointOptions): Promise<ZarrPointSeriesResult>;
1286
1671
  queryDepthProfile(options: QueryDepthProfileOptions): Promise<ZarrPointSeriesResult>;
1287
1672
  private attachWhenReady;
1673
+ /** GeoVideo playback options shared by a new layer and an in-place swap. */
1674
+ private geoVideoPlayback;
1675
+ /**
1676
+ * Show another artifact of the same scalar GeoVideo entry, such as the next
1677
+ * archive chunk, in the attached layer. The last frame stays on screen until
1678
+ * the new media is ready, instead of a blank map while a new layer loads.
1679
+ */
1680
+ private replaceGeoVideoInPlace;
1288
1681
  private detach;
1682
+ /**
1683
+ * In "auto", a GeoVideo that fails while it renders (media, mask) yields
1684
+ * to another source, paused, keeping the date and depth.
1685
+ */
1686
+ private fallBackFromGeoVideo;
1289
1687
  private applyMutableSettings;
1290
1688
  private getQuerySource;
1291
1689
  private getFieldSource;
1690
+ /**
1691
+ * Time the caller asked for, so selection skips fixed-period sources (such
1692
+ * as a GeoVideo artifact) that cannot show it. A trailing window targets now.
1693
+ */
1694
+ private sourceTimeRequest;
1695
+ /**
1696
+ * Source for a preference and target. In "auto", a GeoVideo that cannot show
1697
+ * the requested depth (it renders the surface level) yields to Zarr.
1698
+ */
1699
+ private selectSource;
1700
+ private isFailedGeoVideo;
1701
+ /** The entry without GeoVideo artifacts that failed to load. */
1702
+ private withoutFailedGeoVideo;
1703
+ /** Whether `depth` resolves to the surface level that GeoVideo renders. */
1704
+ private geoVideoServesDepth;
1705
+ private isArchiveChunk;
1706
+ /**
1707
+ * The archive chunk that follows the current one inside the active time
1708
+ * range, or with loop on, the first one, with the time playback resumes at.
1709
+ */
1710
+ private nextArchiveChunk;
1711
+ /** A chunk manifest, reusing the one requested ahead for the next chunk. */
1712
+ private loadChunkManifest;
1713
+ /**
1714
+ * While a scalar archive chunk plays, load the next chunk into the layer so
1715
+ * playback moves on without waiting for its manifest and media.
1716
+ */
1717
+ private preloadNextArchiveChunk;
1718
+ /**
1719
+ * Keep playing across an archive: when a chunk ends, play the next chunk
1720
+ * inside the active time range, or with loop on, the first one.
1721
+ */
1722
+ private continueArchivePlayback;
1723
+ /** The entry's initialised field Zarr, or null when it has none. */
1724
+ private entryFieldZarr;
1725
+ /** Zarr time and vertical axes; unavailable metadata leaves the artifact's own timeline. */
1726
+ private zarrAxes;
1727
+ /**
1728
+ * Entry whose archive sources are replaced by one fixed-period source per
1729
+ * published chunk. An unreachable index drops the archive, so selection
1730
+ * falls back to the entry's other sources.
1731
+ */
1732
+ private resolveArchives;
1292
1733
  private resolveSource;
1293
1734
  private querySource;
1294
1735
  private isMapReady;
@@ -1329,7 +1770,9 @@ export declare interface ZartiglDebugInfo {
1329
1770
  depth: number;
1330
1771
  settings: Partial<ZartiglSettings>;
1331
1772
  timeRange?: TimeRange;
1332
- geoVideo: Required<GeoVideoOptions>;
1773
+ geoVideo: Required<Omit<GeoVideoOptions, "stepsPerSecond">> & {
1774
+ stepsPerSecond: number | null;
1775
+ };
1333
1776
  layer: CatalogRenderLayerDebugInfo | null;
1334
1777
  }
1335
1778
 
@@ -1342,6 +1785,11 @@ declare type ZartiglEventMap = {
1342
1785
  cacheInvalidated: () => void;
1343
1786
  timeChange: (time: number) => void;
1344
1787
  playbackChange: (playing: boolean) => void;
1788
+ /** The active source changed, including an automatic fallback. */
1789
+ sourceChange: (source: {
1790
+ id: string;
1791
+ type: CatalogSource["type"];
1792
+ }) => void;
1345
1793
  };
1346
1794
 
1347
1795
  export declare class ZartiglFrameUnavailableError extends Error {
@@ -1362,6 +1810,12 @@ export declare interface ZartiglOptions {
1362
1810
  depth?: number;
1363
1811
  geoVideo?: GeoVideoOptions;
1364
1812
  visible?: boolean;
1813
+ /** Style override over the layer's own. */
1814
+ style?: LayerStyleInput;
1815
+ /**
1816
+ * Engine tuning (`particleState`, `rgba8MaxParticleZoom`). Flat style
1817
+ * keys are deprecated: they move to `style`, with a warning.
1818
+ */
1365
1819
  settings?: Partial<ZartiglSettings>;
1366
1820
  metadata?: Record<string, unknown>;
1367
1821
  before?: string;
@@ -1376,6 +1830,13 @@ export declare interface ZartiglSettings {
1376
1830
  opacity: number;
1377
1831
  logScale: boolean;
1378
1832
  vibrance: number;
1833
+ /**
1834
+ * Particle trail color grade; off by default. Missing values take the
1835
+ * vibrant preset (see resolveColorGrade).
1836
+ */
1837
+ colorGrade: Partial<ColorGrade>;
1838
+ /** Solid color under particle trails, on valid cells; off by default. */
1839
+ backdrop: Partial<BackdropStyle>;
1379
1840
  /** Fixed physical-value domain for scalar colors. Null uses frame extrema. */
1380
1841
  colorDomain: [number, number] | null;
1381
1842
  particleState: ParticleStateMode;
@@ -1412,6 +1873,17 @@ export declare interface ZartiglUpdate {
1412
1873
  timeRange?: TimeRange | null;
1413
1874
  time?: Date | string | number;
1414
1875
  depth?: number;
1876
+ /**
1877
+ * Replaces the style override: every style key returns to the layer's own,
1878
+ * then this one applies (`{}` clears it). Omitted, the override stays,
1879
+ * across layers too, without the keys a new layer kind cannot draw. Kept
1880
+ * once a layer switch loads.
1881
+ */
1882
+ style?: LayerStyleInput;
1883
+ /**
1884
+ * Engine tuning. Flat style keys are deprecated: they patch the style
1885
+ * override, with a warning.
1886
+ */
1415
1887
  settings?: Partial<ZartiglSettings>;
1416
1888
  geoVideo?: GeoVideoOptions;
1417
1889
  visible?: boolean;