bruce-cesium 7.2.2 → 7.2.4
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/bruce-cesium.es5.js +437 -377
- package/dist/bruce-cesium.es5.js.map +1 -1
- package/dist/bruce-cesium.umd.js +441 -382
- package/dist/bruce-cesium.umd.js.map +1 -1
- package/dist/lib/bruce-cesium.js +2 -1
- package/dist/lib/bruce-cesium.js.map +1 -1
- package/dist/lib/rendering/displaced-surface-primitive.js +51 -43
- package/dist/lib/rendering/displaced-surface-primitive.js.map +1 -1
- package/dist/lib/rendering/entity-render-engine-polygon.js +30 -39
- package/dist/lib/rendering/entity-render-engine-polygon.js.map +1 -1
- package/dist/lib/rendering/texture-frame-series-animator.js +279 -267
- package/dist/lib/rendering/texture-frame-series-animator.js.map +1 -1
- package/dist/lib/rendering/texture-value-labels.js +71 -25
- package/dist/lib/rendering/texture-value-labels.js.map +1 -1
- package/dist/types/bruce-cesium.d.ts +2 -1
- package/dist/types/rendering/displaced-surface-primitive.d.ts +2 -10
- package/dist/types/rendering/texture-frame-series-animator.d.ts +166 -66
- package/dist/types/rendering/texture-value-labels.d.ts +22 -0
- package/package.json +2 -2
package/dist/bruce-cesium.umd.js
CHANGED
|
@@ -40286,40 +40286,230 @@
|
|
|
40286
40286
|
});
|
|
40287
40287
|
}
|
|
40288
40288
|
|
|
40289
|
-
var TextureFrameSeriesAnimator;
|
|
40290
40289
|
(function (TextureFrameSeriesAnimator) {
|
|
40291
|
-
// Values are measured up from the ground beneath them.
|
|
40292
|
-
TextureFrameSeriesAnimator.QUANTITY_THICKNESS = "thickness";
|
|
40293
|
-
// Values are heights against a vertical datum, so they need that datum to be placed.
|
|
40294
|
-
TextureFrameSeriesAnimator.QUANTITY_ELEVATION = "elevation";
|
|
40295
40290
|
TextureFrameSeriesAnimator.LAYOUT_TILES = "tiles";
|
|
40291
|
+
// The only generation this renderer draws. Anything else is refused rather than half read: the
|
|
40292
|
+
// shapes differ enough that a partial reading produces a plausible wrong surface, which is worse
|
|
40293
|
+
// than an empty one.
|
|
40294
|
+
TextureFrameSeriesAnimator.SUPPORTED_VERSION = 3;
|
|
40295
|
+
// Value in R at 8 bits, or high in R and low in G at 16.
|
|
40296
|
+
TextureFrameSeriesAnimator.FORMAT_U8 = "u8";
|
|
40297
|
+
TextureFrameSeriesAnimator.FORMAT_RG16 = "rg16";
|
|
40298
|
+
// Declared as a field with one value, so a second mode can be added later without an absent
|
|
40299
|
+
// Encoding having to mean anything.
|
|
40300
|
+
TextureFrameSeriesAnimator.ENCODING_ABSOLUTE = "absolute";
|
|
40301
|
+
TextureFrameSeriesAnimator.LAYER_VALUE = "Value";
|
|
40302
|
+
TextureFrameSeriesAnimator.LAYER_VALUE_MIN = "ValueMin";
|
|
40303
|
+
TextureFrameSeriesAnimator.LAYER_VALUE_MAX = "ValueMax";
|
|
40304
|
+
TextureFrameSeriesAnimator.LAYER_BASELINE = "Baseline";
|
|
40305
|
+
// The per-texel correction that was applied, published only on an archive that was transformed.
|
|
40306
|
+
TextureFrameSeriesAnimator.LAYER_VALUE_SHIFT = "ValueShift";
|
|
40296
40307
|
/**
|
|
40297
|
-
*
|
|
40308
|
+
* Turns a published value map into the flat shape this renderer works in, or null when it cannot be drawn.
|
|
40309
|
+
* @param published the archive's Data.generation
|
|
40310
|
+
*/
|
|
40311
|
+
function Adapt(published) {
|
|
40312
|
+
if (!published || published.Version !== TextureFrameSeriesAnimator.SUPPORTED_VERSION) {
|
|
40313
|
+
const found = published && published.Version != null ? published.Version : "none";
|
|
40314
|
+
const at = published && published.Identity ? published.Identity.Generated : undefined;
|
|
40315
|
+
// In theory should be no files.
|
|
40316
|
+
console.warn("bruce-cesium: value map version " + found + " is not supported (this build reads "
|
|
40317
|
+
+ TextureFrameSeriesAnimator.SUPPORTED_VERSION + "), so nothing will be drawn for it."
|
|
40318
|
+
+ (at ? " The archive was generated at " + at + " and needs regenerating." : ""));
|
|
40319
|
+
return null;
|
|
40320
|
+
}
|
|
40321
|
+
const geometry = published.Geometry || {};
|
|
40322
|
+
const tiles = published.Tiles || [];
|
|
40323
|
+
if (geometry.Layout !== TextureFrameSeriesAnimator.LAYOUT_TILES || tiles.length === 0) {
|
|
40324
|
+
const at = published.Identity ? published.Identity.Generated : undefined;
|
|
40325
|
+
console.warn("bruce-cesium: value map declares no tiles, so nothing will be drawn for it."
|
|
40326
|
+
+ (at ? " The archive was generated at " + at + " and needs regenerating." : ""));
|
|
40327
|
+
return null;
|
|
40328
|
+
}
|
|
40329
|
+
const value = published.Value || {};
|
|
40330
|
+
if (value.Encoding && value.Encoding !== TextureFrameSeriesAnimator.ENCODING_ABSOLUTE) {
|
|
40331
|
+
const at = published.Identity ? published.Identity.Generated : undefined;
|
|
40332
|
+
console.warn("bruce-cesium: value map encoding " + value.Encoding + " is not supported"
|
|
40333
|
+
+ " (this build reads " + TextureFrameSeriesAnimator.ENCODING_ABSOLUTE + "), so nothing will be drawn for it."
|
|
40334
|
+
+ (at ? " The archive was generated at " + at + " and needs regenerating." : ""));
|
|
40335
|
+
return null;
|
|
40336
|
+
}
|
|
40337
|
+
const series = published.Series || {};
|
|
40338
|
+
const layerRange = declaredRange(published, TextureFrameSeriesAnimator.LAYER_VALUE_MIN);
|
|
40339
|
+
const flat = {
|
|
40340
|
+
Frames: [],
|
|
40341
|
+
ValueMin: value.Min,
|
|
40342
|
+
ValueMax: value.Max,
|
|
40343
|
+
Format: value.Format || TextureFrameSeriesAnimator.FORMAT_U8,
|
|
40344
|
+
Encoding: value.Encoding || TextureFrameSeriesAnimator.ENCODING_ABSOLUTE,
|
|
40345
|
+
Units: value.Units,
|
|
40346
|
+
West: geometry.West,
|
|
40347
|
+
East: geometry.East,
|
|
40348
|
+
South: geometry.South,
|
|
40349
|
+
North: geometry.North,
|
|
40350
|
+
ResolutionX: geometry.ResolutionX,
|
|
40351
|
+
ResolutionY: geometry.ResolutionY,
|
|
40352
|
+
PixelPlacement: geometry.PixelPlacement,
|
|
40353
|
+
Times: series.Times,
|
|
40354
|
+
ExtremesValueMin: layerRange ? layerRange.min : undefined,
|
|
40355
|
+
ExtremesValueMax: layerRange ? layerRange.max : undefined,
|
|
40356
|
+
// The delivered values are what gets drawn. What the source called them, and any shift
|
|
40357
|
+
// applied on the way, travel for labels and analysis rather than for placement.
|
|
40358
|
+
SourceVerticalDatum: published.ValueSource ? published.ValueSource.VerticalDatum : undefined,
|
|
40359
|
+
Transform: published.ValueSource ? published.ValueSource.Transform : undefined,
|
|
40360
|
+
SourceUnits: published.ValueSource ? published.ValueSource.Units : undefined,
|
|
40361
|
+
SourceValueMin: published.ValueSource ? published.ValueSource.Min : undefined,
|
|
40362
|
+
SourceValueMax: published.ValueSource ? published.ValueSource.Max : undefined
|
|
40363
|
+
};
|
|
40364
|
+
flat.Tiles = tiles.map((tile) => adaptTile(tile, series.Times));
|
|
40365
|
+
// The coarsest tile covers the whole extent, so it is what a caller wanting one time list reads.
|
|
40366
|
+
flat.Frames = flat.Tiles[0].Frames;
|
|
40367
|
+
warnIfUnshifted(published, flat);
|
|
40368
|
+
return flat;
|
|
40369
|
+
}
|
|
40370
|
+
TextureFrameSeriesAnimator.Adapt = Adapt;
|
|
40371
|
+
/*
|
|
40372
|
+
* Says so when an archive's heights are not on the ellipsoid they will be drawn against.
|
|
40373
|
+
*/
|
|
40374
|
+
function warnIfUnshifted(published, flat) {
|
|
40375
|
+
const datum = flat.SourceVerticalDatum;
|
|
40376
|
+
if (!datum || datum === TextureFrameSeriesAnimator.DATUM_ELLIPSOIDAL || flat.Transform) {
|
|
40377
|
+
return;
|
|
40378
|
+
}
|
|
40379
|
+
const at = published.Identity ? published.Identity.Generated : undefined;
|
|
40380
|
+
console.warn("bruce-cesium: value map heights are in " + datum + " and report no transform, so"
|
|
40381
|
+
+ " they are not ellipsoidal and will draw at the wrong height when placed absolutely."
|
|
40382
|
+
+ (at ? " The archive was generated at " + at + " and needs regenerating with the shift"
|
|
40383
|
+
+ " applied." : ""));
|
|
40384
|
+
}
|
|
40385
|
+
/*
|
|
40386
|
+
* The Min and Max a named layer declares, since a statistics layer is encoded over the true data
|
|
40387
|
+
* range rather than over the frames' own.
|
|
40388
|
+
*/
|
|
40389
|
+
function declaredRange(published, name) {
|
|
40390
|
+
const layers = published.Layers || [];
|
|
40391
|
+
for (const layer of layers) {
|
|
40392
|
+
if (layer.Name === name && typeof layer.Min === "number" && typeof layer.Max === "number") {
|
|
40393
|
+
return { min: layer.Min, max: layer.Max };
|
|
40394
|
+
}
|
|
40395
|
+
}
|
|
40396
|
+
return null;
|
|
40397
|
+
}
|
|
40398
|
+
/*
|
|
40399
|
+
* One tile, with its per-time value rasters lined up against the shared time list.
|
|
40400
|
+
*/
|
|
40401
|
+
function adaptTile(tile, times) {
|
|
40402
|
+
const layers = tile.Layers || {};
|
|
40403
|
+
const perTime = layers[TextureFrameSeriesAnimator.LAYER_VALUE] || [];
|
|
40404
|
+
const frames = perTime.map((entry, index) => ({
|
|
40405
|
+
Timestamp: times && times[index] ? times[index] : "",
|
|
40406
|
+
ByteOffset: entry.Bytes.Offset,
|
|
40407
|
+
ByteLength: entry.Bytes.Length
|
|
40408
|
+
}));
|
|
40409
|
+
return {
|
|
40410
|
+
Level: tile.Level,
|
|
40411
|
+
X: tile.X,
|
|
40412
|
+
Y: tile.Y,
|
|
40413
|
+
West: tile.West,
|
|
40414
|
+
East: tile.East,
|
|
40415
|
+
South: tile.South,
|
|
40416
|
+
North: tile.North,
|
|
40417
|
+
TexelMetres: tile.TexelMetres,
|
|
40418
|
+
ResolutionX: tile.ResolutionX,
|
|
40419
|
+
ResolutionY: tile.ResolutionY,
|
|
40420
|
+
Frames: frames,
|
|
40421
|
+
Floor: single(layers[TextureFrameSeriesAnimator.LAYER_VALUE_MIN]),
|
|
40422
|
+
Ceiling: single(layers[TextureFrameSeriesAnimator.LAYER_VALUE_MAX]),
|
|
40423
|
+
BaselineMask: single(layers[TextureFrameSeriesAnimator.LAYER_BASELINE])
|
|
40424
|
+
};
|
|
40425
|
+
}
|
|
40426
|
+
function single(layer) {
|
|
40427
|
+
if (!layer || !layer.Bytes) {
|
|
40428
|
+
return undefined;
|
|
40429
|
+
}
|
|
40430
|
+
return { ByteOffset: layer.Bytes.Offset, ByteLength: layer.Bytes.Length };
|
|
40431
|
+
}
|
|
40432
|
+
/**
|
|
40433
|
+
* How to turn a delivered value into the number the source published, or null when the archive
|
|
40434
|
+
* does not say enough to do it.
|
|
40435
|
+
* @param metadata the archive's Data.generation
|
|
40436
|
+
*/
|
|
40437
|
+
function SourceMap(metadata) {
|
|
40438
|
+
if (!metadata) {
|
|
40439
|
+
return null;
|
|
40440
|
+
}
|
|
40441
|
+
const { ValueMin, ValueMax, SourceValueMin, SourceValueMax } = metadata;
|
|
40442
|
+
if (typeof ValueMin !== "number" || typeof ValueMax !== "number"
|
|
40443
|
+
|| typeof SourceValueMin !== "number" || typeof SourceValueMax !== "number") {
|
|
40444
|
+
return null;
|
|
40445
|
+
}
|
|
40446
|
+
const delivered = ValueMax - ValueMin;
|
|
40447
|
+
// A single delivered value describes no line, so all that can be recovered is where it sits.
|
|
40448
|
+
const scale = delivered !== 0 ? (SourceValueMax - SourceValueMin) / delivered : 1;
|
|
40449
|
+
return { scale, offset: SourceValueMin - ValueMin * scale };
|
|
40450
|
+
}
|
|
40451
|
+
TextureFrameSeriesAnimator.SourceMap = SourceMap;
|
|
40452
|
+
/**
|
|
40453
|
+
* Whether anything was applied to the values on the way out of the source.
|
|
40454
|
+
* @param metadata the archive's Data.generation
|
|
40455
|
+
*/
|
|
40456
|
+
function IsTransformed(metadata) {
|
|
40457
|
+
return Boolean(metadata && metadata.Transform);
|
|
40458
|
+
}
|
|
40459
|
+
TextureFrameSeriesAnimator.IsTransformed = IsTransformed;
|
|
40460
|
+
/**
|
|
40461
|
+
* A delivered value in the source's own terms, or unchanged when it cannot be turned back.
|
|
40462
|
+
* @param map from SourceMap(), null when the archive does not say enough
|
|
40463
|
+
*/
|
|
40464
|
+
function ToSource(map, value) {
|
|
40465
|
+
return map ? value * map.scale + map.offset : value;
|
|
40466
|
+
}
|
|
40467
|
+
TextureFrameSeriesAnimator.ToSource = ToSource;
|
|
40468
|
+
/**
|
|
40469
|
+
* Whether the archive's frames carry a 16 bit value packed across R and G.
|
|
40298
40470
|
*
|
|
40299
|
-
*
|
|
40300
|
-
*
|
|
40471
|
+
* Every consumer that reads a texel has to ask: taking R alone from a packed frame yields a
|
|
40472
|
+
* plausible surface quantised to 255 steps of the full range rather than an obvious failure.
|
|
40473
|
+
* @param metadata the archive's Data.generation
|
|
40474
|
+
*/
|
|
40475
|
+
function IsPackedValue(metadata) {
|
|
40476
|
+
return Boolean(metadata && metadata.Format === TextureFrameSeriesAnimator.FORMAT_RG16);
|
|
40477
|
+
}
|
|
40478
|
+
TextureFrameSeriesAnimator.IsPackedValue = IsPackedValue;
|
|
40479
|
+
/**
|
|
40480
|
+
* The normalised 0 to 1 value at a texel, from whichever format the archive packed it in.
|
|
40301
40481
|
* @param metadata the archive's Data.generation
|
|
40482
|
+
* @param pixels untinted RGBA value pixels
|
|
40483
|
+
* @param at index of the texel's red channel
|
|
40302
40484
|
*/
|
|
40303
|
-
function
|
|
40304
|
-
if (
|
|
40305
|
-
return 1;
|
|
40485
|
+
function NormalisedAt(metadata, pixels, at) {
|
|
40486
|
+
if (IsPackedValue(metadata)) {
|
|
40487
|
+
return (pixels[at] * 256 + pixels[at + 1]) / 65535;
|
|
40306
40488
|
}
|
|
40307
|
-
return
|
|
40489
|
+
return pixels[at] / 255;
|
|
40308
40490
|
}
|
|
40309
|
-
TextureFrameSeriesAnimator.
|
|
40491
|
+
TextureFrameSeriesAnimator.NormalisedAt = NormalisedAt;
|
|
40492
|
+
// Values already sit on the ellipsoid Cesium measures against, so nothing is added to place them.
|
|
40493
|
+
TextureFrameSeriesAnimator.DATUM_ELLIPSOIDAL = "WGS84";
|
|
40310
40494
|
/**
|
|
40311
|
-
* Whether
|
|
40495
|
+
* Whether the values are heights against a vertical datum rather than a thickness.
|
|
40496
|
+
*
|
|
40497
|
+
* A thickness rises from wherever the polygon sits and a height already knows where it belongs, so
|
|
40498
|
+
* this is what decides whether the polygon's own altitude may be added underneath it.
|
|
40312
40499
|
* @param metadata the archive's Data.generation
|
|
40313
40500
|
*/
|
|
40314
|
-
function
|
|
40315
|
-
return
|
|
40316
|
-
|
|
40317
|
-
|
|
40318
|
-
|
|
40319
|
-
|
|
40320
|
-
|
|
40321
|
-
|
|
40322
|
-
|
|
40501
|
+
function IsDatumHeight(metadata) {
|
|
40502
|
+
return Boolean(metadata && metadata.SourceVerticalDatum);
|
|
40503
|
+
}
|
|
40504
|
+
TextureFrameSeriesAnimator.IsDatumHeight = IsDatumHeight;
|
|
40505
|
+
/**
|
|
40506
|
+
* The archive's tiles, which Adapt() guarantees are present.
|
|
40507
|
+
* @param metadata the archive's Data.generation
|
|
40508
|
+
*/
|
|
40509
|
+
function TilesOf(metadata) {
|
|
40510
|
+
return (metadata && metadata.Tiles) || [];
|
|
40511
|
+
}
|
|
40512
|
+
TextureFrameSeriesAnimator.TilesOf = TilesOf;
|
|
40323
40513
|
/**
|
|
40324
40514
|
* Turns a value in the attribute's own units into the 0 to 1 position the ramp is indexed by.
|
|
40325
40515
|
*
|
|
@@ -40338,44 +40528,18 @@
|
|
|
40338
40528
|
return Math.min(1, Math.max(0, (value - lo) / span));
|
|
40339
40529
|
}
|
|
40340
40530
|
TextureFrameSeriesAnimator.NormalisePosition = NormalisePosition;
|
|
40341
|
-
/**
|
|
40342
|
-
* Whether the frames are a departure from each cell's own normal rather than a raw reading.
|
|
40343
|
-
*
|
|
40344
|
-
* Worth asking before labelling anything: the two measures need different words for the same
|
|
40345
|
-
* number, and calling an anomaly "water depth" is a worse error than the coarse ramp it replaced.
|
|
40346
|
-
* @param metadata the archive's Data.generation
|
|
40347
|
-
*/
|
|
40348
|
-
function IsAnomalyMeasure(metadata) {
|
|
40349
|
-
return Boolean(metadata && metadata.Measure === TextureFrameSeriesAnimator.MEASURE_ANOMALY);
|
|
40350
|
-
}
|
|
40351
|
-
TextureFrameSeriesAnimator.IsAnomalyMeasure = IsAnomalyMeasure;
|
|
40352
|
-
/**
|
|
40353
|
-
* How much the value moves anywhere in the series, in source units.
|
|
40354
|
-
*
|
|
40355
|
-
* Falls back to the published range, which is the right answer for an anomaly archive (its range
|
|
40356
|
-
* IS the movement) and the only available one for an archive predating AnomalyMax.
|
|
40357
|
-
* @param metadata the archive's Data.generation
|
|
40358
|
-
*/
|
|
40359
|
-
function MovingRange(metadata) {
|
|
40360
|
-
if (!metadata) {
|
|
40361
|
-
return undefined;
|
|
40362
|
-
}
|
|
40363
|
-
if (typeof metadata.AnomalyMax === "number" && metadata.AnomalyMax > 0) {
|
|
40364
|
-
return metadata.AnomalyMax;
|
|
40365
|
-
}
|
|
40366
|
-
if (typeof metadata.ValueMin === "number" && typeof metadata.ValueMax === "number") {
|
|
40367
|
-
return Math.abs(metadata.ValueMax - metadata.ValueMin);
|
|
40368
|
-
}
|
|
40369
|
-
return undefined;
|
|
40370
|
-
}
|
|
40371
|
-
TextureFrameSeriesAnimator.MovingRange = MovingRange;
|
|
40372
40531
|
/**
|
|
40373
40532
|
* Detects whether a ClientFile's `Data.generation` metadata describes a frame archive rather than a single static image,
|
|
40374
40533
|
* so a caller can decide whether to construct an Animator or fall back to the existing static-texture path.
|
|
40375
|
-
* @param data ClientFile.Data
|
|
40534
|
+
* @param data ClientFile.Data
|
|
40376
40535
|
*/
|
|
40377
40536
|
function IsFrameArchiveMetadata(data) {
|
|
40378
|
-
|
|
40537
|
+
const generation = data && data.generation;
|
|
40538
|
+
if (!generation) {
|
|
40539
|
+
return false;
|
|
40540
|
+
}
|
|
40541
|
+
return (typeof generation.Version === "number" ||
|
|
40542
|
+
(Array.isArray(generation.Frames) && generation.Frames.length > 0));
|
|
40379
40543
|
}
|
|
40380
40544
|
TextureFrameSeriesAnimator.IsFrameArchiveMetadata = IsFrameArchiveMetadata;
|
|
40381
40545
|
// First resolvable static number out of a calculator field list, since that is all a border needs.
|
|
@@ -40421,10 +40585,27 @@
|
|
|
40421
40585
|
TextureFrameSeriesAnimator.AppearanceSignature = AppearanceSignature;
|
|
40422
40586
|
const MAX_COMPOSITE_TEXELS = 2048;
|
|
40423
40587
|
const VALUE_DILATE_PASSES = 2;
|
|
40588
|
+
/*
|
|
40589
|
+
* Rewrites a packed 16 bit value as the 8 bit grey the colour ramps index on.
|
|
40590
|
+
*
|
|
40591
|
+
* All three channels, since a ramp is free to read any of them and a leftover low byte in G would
|
|
40592
|
+
* show up as a green cast on the one that reads them all.
|
|
40593
|
+
*/
|
|
40594
|
+
function flattenPacked(pixels, packed) {
|
|
40595
|
+
if (!packed) {
|
|
40596
|
+
return;
|
|
40597
|
+
}
|
|
40598
|
+
for (let i = 0; i < pixels.length; i += 4) {
|
|
40599
|
+
const grey = Math.round((pixels[i] * 256 + pixels[i + 1]) / 65535 * 255);
|
|
40600
|
+
pixels[i] = grey;
|
|
40601
|
+
pixels[i + 1] = grey;
|
|
40602
|
+
pixels[i + 2] = grey;
|
|
40603
|
+
}
|
|
40604
|
+
}
|
|
40424
40605
|
/*
|
|
40425
40606
|
* Bleeds covered values outward into uncovered texels, leaving alpha untouched.
|
|
40426
40607
|
*/
|
|
40427
|
-
function dilateValues(value, width, height, passes) {
|
|
40608
|
+
function dilateValues(value, width, height, passes, packed) {
|
|
40428
40609
|
const texels = width * height;
|
|
40429
40610
|
const filled = new Uint8Array(texels);
|
|
40430
40611
|
for (let p = 0; p < texels; p++) {
|
|
@@ -40448,15 +40629,25 @@
|
|
|
40448
40629
|
if (nx < 0 || ny < 0 || nx >= width || ny >= height || !wasFilled[ny * width + nx]) {
|
|
40449
40630
|
continue;
|
|
40450
40631
|
}
|
|
40451
|
-
|
|
40632
|
+
const at = (ny * width + nx) * 4;
|
|
40633
|
+
// Averaged as a value rather than per byte: averaging a packed low byte
|
|
40634
|
+
// on its own wraps at every 256th step and speckles the bled edge.
|
|
40635
|
+
sum += packed ? source[at] * 256 + source[at + 1] : source[at];
|
|
40452
40636
|
hits++;
|
|
40453
40637
|
}
|
|
40454
40638
|
}
|
|
40455
40639
|
if (hits > 0) {
|
|
40456
40640
|
const v = Math.round(sum / hits);
|
|
40457
|
-
|
|
40458
|
-
|
|
40459
|
-
|
|
40641
|
+
if (packed) {
|
|
40642
|
+
value[p * 4] = (v >> 8) & 255;
|
|
40643
|
+
value[p * 4 + 1] = v & 255;
|
|
40644
|
+
value[p * 4 + 2] = 0;
|
|
40645
|
+
}
|
|
40646
|
+
else {
|
|
40647
|
+
value[p * 4] = v;
|
|
40648
|
+
value[p * 4 + 1] = v;
|
|
40649
|
+
value[p * 4 + 2] = v;
|
|
40650
|
+
}
|
|
40460
40651
|
filled[p] = 1;
|
|
40461
40652
|
}
|
|
40462
40653
|
}
|
|
@@ -40494,7 +40685,6 @@
|
|
|
40494
40685
|
// forcing Cesium to re-upload the texture only when something changed.
|
|
40495
40686
|
this.pool = [document.createElement("canvas"), document.createElement("canvas")];
|
|
40496
40687
|
this.poolIdx = 0;
|
|
40497
|
-
this.scratch = null;
|
|
40498
40688
|
this.valueDims = null;
|
|
40499
40689
|
this.presentVersion = 0;
|
|
40500
40690
|
this.extremesLoad = null;
|
|
@@ -40517,7 +40707,7 @@
|
|
|
40517
40707
|
this.highColor = (mask && BModels.Color.ColorFromStr(mask.maxColor)) || DEFAULT_HIGH_COLOR;
|
|
40518
40708
|
// Positions are authored in the attribute's units and normalised once here, so the
|
|
40519
40709
|
// per-pixel loop stays a comparison against 0 to 1 like the two-colour path.
|
|
40520
|
-
const points = mask
|
|
40710
|
+
const points = mask && mask.points;
|
|
40521
40711
|
this.rampStops = (points && points.length > 0)
|
|
40522
40712
|
? points.map((p) => ({
|
|
40523
40713
|
position: NormalisePosition(options.metadata, p.position),
|
|
@@ -40543,9 +40733,8 @@
|
|
|
40543
40733
|
this.produceValueCanvas = Boolean(options.produceValueCanvas);
|
|
40544
40734
|
this.metadata = options.metadata;
|
|
40545
40735
|
this.drapeExtent = options.drapeExtent;
|
|
40546
|
-
this.tiles =
|
|
40547
|
-
this.
|
|
40548
|
-
this.maskBaseline = (_c = options.maskBaseline) !== null && _c !== void 0 ? _c : Boolean(options.baselineMask);
|
|
40736
|
+
this.tiles = TilesOf(options.metadata);
|
|
40737
|
+
this.maskBaseline = (_c = options.maskBaseline) !== null && _c !== void 0 ? _c : this.tiles.some((tile) => Boolean(tile.BaselineMask));
|
|
40549
40738
|
this.valueCanvas = this.produceValueCanvas ? document.createElement("canvas") : null;
|
|
40550
40739
|
this.driveMaterial = options.driveMaterial !== false;
|
|
40551
40740
|
this.originalMaterial = options.entity.polygon.material;
|
|
@@ -40721,32 +40910,20 @@
|
|
|
40721
40910
|
}
|
|
40722
40911
|
async fetchAndTint(idx) {
|
|
40723
40912
|
const tiles = this.tiles;
|
|
40724
|
-
|
|
40725
|
-
|
|
40726
|
-
|
|
40727
|
-
|
|
40728
|
-
|
|
40729
|
-
|
|
40730
|
-
|
|
40731
|
-
|
|
40732
|
-
const at = tile.Frames[idx];
|
|
40733
|
-
if (!at) {
|
|
40734
|
-
continue;
|
|
40735
|
-
}
|
|
40736
|
-
lo = Math.min(lo, at.ByteOffset);
|
|
40737
|
-
hi = Math.max(hi, at.ByteOffset + at.ByteLength);
|
|
40913
|
+
// Every tile's frame for one timestep sits contiguously, because the generator writes
|
|
40914
|
+
// frame major. So a tiled frame is still ONE range request, not one per tile.
|
|
40915
|
+
let lo = Number.MAX_SAFE_INTEGER;
|
|
40916
|
+
let hi = 0;
|
|
40917
|
+
for (const tile of tiles) {
|
|
40918
|
+
const at = tile.Frames[idx];
|
|
40919
|
+
if (!at) {
|
|
40920
|
+
continue;
|
|
40738
40921
|
}
|
|
40739
|
-
|
|
40740
|
-
|
|
40741
|
-
buffer = await response.arrayBuffer();
|
|
40742
|
-
}
|
|
40743
|
-
else {
|
|
40744
|
-
const entry = this.frames[idx];
|
|
40745
|
-
const start = entry.ByteOffset;
|
|
40746
|
-
const end = entry.ByteOffset + entry.ByteLength - 1;
|
|
40747
|
-
const response = await fetch(this.archiveUrl, { headers: { Range: `bytes=${start}-${end}` } });
|
|
40748
|
-
buffer = await response.arrayBuffer();
|
|
40922
|
+
lo = Math.min(lo, at.ByteOffset);
|
|
40923
|
+
hi = Math.max(hi, at.ByteOffset + at.ByteLength);
|
|
40749
40924
|
}
|
|
40925
|
+
const response = await fetch(this.archiveUrl, { headers: { Range: `bytes=${lo}-${hi - 1}` } });
|
|
40926
|
+
const buffer = await response.arrayBuffer();
|
|
40750
40927
|
if (this.disposed) {
|
|
40751
40928
|
return;
|
|
40752
40929
|
}
|
|
@@ -40756,9 +40933,7 @@
|
|
|
40756
40933
|
if (this.disposed) {
|
|
40757
40934
|
return;
|
|
40758
40935
|
}
|
|
40759
|
-
const decoded = tiles
|
|
40760
|
-
? await this.composeTiles(tiles, idx, buffer, span)
|
|
40761
|
-
: await this.decodeAndTint(buffer);
|
|
40936
|
+
const decoded = await this.composeTiles(tiles, idx, buffer, lo);
|
|
40762
40937
|
if (this.disposed) {
|
|
40763
40938
|
return;
|
|
40764
40939
|
}
|
|
@@ -40769,26 +40944,6 @@
|
|
|
40769
40944
|
this.beginCrossfadeTo(idx);
|
|
40770
40945
|
}
|
|
40771
40946
|
}
|
|
40772
|
-
/**
|
|
40773
|
-
* Decodes one frame's raw PNG bytes (as returned by a Range GET) and applies the grayscale color mask.
|
|
40774
|
-
*/
|
|
40775
|
-
async decodeAndTint(buffer) {
|
|
40776
|
-
const blob = new Blob([buffer], { type: "image/png" });
|
|
40777
|
-
const objectUrl = URL.createObjectURL(blob);
|
|
40778
|
-
let image;
|
|
40779
|
-
try {
|
|
40780
|
-
image = await loadImage(objectUrl);
|
|
40781
|
-
}
|
|
40782
|
-
finally {
|
|
40783
|
-
URL.revokeObjectURL(objectUrl);
|
|
40784
|
-
}
|
|
40785
|
-
const canvas = document.createElement("canvas");
|
|
40786
|
-
canvas.width = image.width;
|
|
40787
|
-
canvas.height = image.height;
|
|
40788
|
-
const ctx = canvas.getContext("2d");
|
|
40789
|
-
ctx.drawImage(image, 0, 0);
|
|
40790
|
-
return this.tint(ctx.getImageData(0, 0, canvas.width, canvas.height));
|
|
40791
|
-
}
|
|
40792
40947
|
/*
|
|
40793
40948
|
* Draws every tile of one frame into a single raster covering the drape extent.
|
|
40794
40949
|
*/
|
|
@@ -40861,6 +41016,10 @@
|
|
|
40861
41016
|
// Copied before tinting: the ramp is a lerp between two colours, so the value cannot be
|
|
40862
41017
|
// recovered from the tinted pixels afterwards.
|
|
40863
41018
|
const valuePixels = this.produceValueCanvas ? new Uint8ClampedArray(imageData.data) : undefined;
|
|
41019
|
+
// The ramp indexes on R as an 8 bit grey, so a packed frame is flattened to one here
|
|
41020
|
+
// rather than teaching every ramp function a second format.
|
|
41021
|
+
// The copy above keeps the packed pair, which is what displacement and labels read.
|
|
41022
|
+
flattenPacked(imageData.data, IsPackedValue(this.metadata));
|
|
40864
41023
|
// Stops win when supplied: they can express a band and a hidden floor, which a two
|
|
40865
41024
|
// colour ramp cannot. The pair stays the fallback so an older style still draws.
|
|
40866
41025
|
if (this.rampStops && this.rampStops.length > 0) {
|
|
@@ -40874,72 +41033,20 @@
|
|
|
40874
41033
|
}
|
|
40875
41034
|
this.applyBaselineMask(imageData.data, valuePixels, canvas.width, canvas.height);
|
|
40876
41035
|
if (valuePixels) {
|
|
40877
|
-
dilateValues(valuePixels, canvas.width, canvas.height, VALUE_DILATE_PASSES);
|
|
41036
|
+
dilateValues(valuePixels, canvas.width, canvas.height, VALUE_DILATE_PASSES, IsPackedValue(this.metadata));
|
|
40878
41037
|
}
|
|
40879
41038
|
return { pixels: imageData.data, width: canvas.width, height: canvas.height, valuePixels };
|
|
40880
41039
|
}
|
|
40881
41040
|
/*
|
|
40882
|
-
* Paints one frame's pixels onto a canvas
|
|
41041
|
+
* Paints one frame's composited pixels onto a canvas.
|
|
40883
41042
|
*/
|
|
40884
41043
|
writeInto(canvas, pixels, dims) {
|
|
40885
|
-
const rect = this.sourceRect(dims);
|
|
40886
|
-
if (!rect) {
|
|
40887
|
-
canvas.width = dims.width;
|
|
40888
|
-
canvas.height = dims.height;
|
|
40889
|
-
const direct = canvas.getContext("2d");
|
|
40890
|
-
const image = direct.createImageData(dims.width, dims.height);
|
|
40891
|
-
image.data.set(pixels);
|
|
40892
|
-
direct.putImageData(image, 0, 0);
|
|
40893
|
-
return;
|
|
40894
|
-
}
|
|
40895
|
-
if (!this.scratch) {
|
|
40896
|
-
this.scratch = document.createElement("canvas");
|
|
40897
|
-
}
|
|
40898
|
-
this.scratch.width = dims.width;
|
|
40899
|
-
this.scratch.height = dims.height;
|
|
40900
|
-
const scratchCtx = this.scratch.getContext("2d");
|
|
40901
|
-
const image = scratchCtx.createImageData(dims.width, dims.height);
|
|
40902
|
-
image.data.set(pixels);
|
|
40903
|
-
scratchCtx.putImageData(image, 0, 0);
|
|
40904
41044
|
canvas.width = dims.width;
|
|
40905
41045
|
canvas.height = dims.height;
|
|
40906
41046
|
const ctx = canvas.getContext("2d");
|
|
40907
|
-
|
|
40908
|
-
|
|
40909
|
-
ctx.
|
|
40910
|
-
ctx.clearRect(0, 0, dims.width, dims.height);
|
|
40911
|
-
ctx.drawImage(this.scratch, rect.x, rect.y, rect.width, rect.height, 0, 0, dims.width, dims.height);
|
|
40912
|
-
}
|
|
40913
|
-
/*
|
|
40914
|
-
* The part of the archive raster that the drape extent covers, in source pixels.
|
|
40915
|
-
*/
|
|
40916
|
-
sourceRect(dims) {
|
|
40917
|
-
const drape = this.drapeExtent;
|
|
40918
|
-
const m = this.metadata;
|
|
40919
|
-
// A composited frame was drawn into the drape extent already, so resampling it again
|
|
40920
|
-
// would apply the same correction twice.
|
|
40921
|
-
if (this.tiles) {
|
|
40922
|
-
return null;
|
|
40923
|
-
}
|
|
40924
|
-
if (!drape || !m || m.West == null || m.East == null || m.South == null || m.North == null) {
|
|
40925
|
-
return null;
|
|
40926
|
-
}
|
|
40927
|
-
const spanLon = m.East - m.West;
|
|
40928
|
-
const spanLat = m.North - m.South;
|
|
40929
|
-
if (!(spanLon > 0) || !(spanLat > 0)) {
|
|
40930
|
-
return null;
|
|
40931
|
-
}
|
|
40932
|
-
const x = (drape.West - m.West) / spanLon * dims.width;
|
|
40933
|
-
const width = (drape.East - drape.West) / spanLon * dims.width;
|
|
40934
|
-
const y = (m.North - drape.North) / spanLat * dims.height;
|
|
40935
|
-
const height = (drape.North - drape.South) / spanLat * dims.height;
|
|
40936
|
-
// A drape that already matches the archive is the common case and must not pay for a
|
|
40937
|
-
// resample, nor lose a half pixel to rounding.
|
|
40938
|
-
if (Math.abs(x) < 0.01 && Math.abs(y) < 0.01
|
|
40939
|
-
&& Math.abs(width - dims.width) < 0.01 && Math.abs(height - dims.height) < 0.01) {
|
|
40940
|
-
return null;
|
|
40941
|
-
}
|
|
40942
|
-
return { x, y, width, height };
|
|
41047
|
+
const image = ctx.createImageData(dims.width, dims.height);
|
|
41048
|
+
image.data.set(pixels);
|
|
41049
|
+
ctx.putImageData(image, 0, 0);
|
|
40943
41050
|
}
|
|
40944
41051
|
/**
|
|
40945
41052
|
* The archive's per-cell floor and ceiling rasters, once EnsureExtremes has resolved.
|
|
@@ -40951,45 +41058,16 @@
|
|
|
40951
41058
|
* Fetches and decodes the floor and ceiling rasters, at most once.
|
|
40952
41059
|
*/
|
|
40953
41060
|
EnsureExtremes() {
|
|
40954
|
-
if (this.extremesLoad) {
|
|
40955
|
-
return this.extremesLoad;
|
|
40956
|
-
}
|
|
40957
|
-
if (this.tiles) {
|
|
41061
|
+
if (!this.extremesLoad) {
|
|
40958
41062
|
this.extremesLoad = this.loadTiledExtremes();
|
|
40959
|
-
|
|
40960
|
-
}
|
|
40961
|
-
const floor = this.metadata && this.metadata.Floor;
|
|
40962
|
-
const ceiling = this.metadata && this.metadata.Ceiling;
|
|
40963
|
-
if (!floor && !ceiling) {
|
|
40964
|
-
this.extremesLoad = Promise.resolve();
|
|
40965
|
-
return this.extremesLoad;
|
|
40966
|
-
}
|
|
40967
|
-
this.extremesLoad = Promise.all([
|
|
40968
|
-
floor ? this.loadImageDataAt(floor) : Promise.resolve(null),
|
|
40969
|
-
ceiling ? this.loadImageDataAt(ceiling) : Promise.resolve(null)
|
|
40970
|
-
]).then(([f, c]) => {
|
|
40971
|
-
if (this.disposed) {
|
|
40972
|
-
return;
|
|
40973
|
-
}
|
|
40974
|
-
this.floorPixels = f;
|
|
40975
|
-
this.ceilingPixels = c;
|
|
40976
|
-
}).catch((e) => {
|
|
40977
|
-
// Missing extremes cost a label its range, and must not take the animation with them.
|
|
40978
|
-
console.warn("TextureFrameSeriesAnimator: could not load the extremes rasters.", e);
|
|
40979
|
-
});
|
|
41063
|
+
}
|
|
40980
41064
|
return this.extremesLoad;
|
|
40981
41065
|
}
|
|
40982
41066
|
/*
|
|
40983
41067
|
* Composites each tile's baseline mask into one covering the composited frames.
|
|
40984
|
-
*
|
|
40985
|
-
* A tiled archive publishes the mask per tile, so reading the archive level BaselineMask
|
|
40986
|
-
* finds nothing and hiding dry cells silently does nothing at all.
|
|
40987
41068
|
*/
|
|
40988
41069
|
async loadTiledBaselineMask() {
|
|
40989
41070
|
const tiles = this.tiles;
|
|
40990
|
-
if (!tiles) {
|
|
40991
|
-
return;
|
|
40992
|
-
}
|
|
40993
41071
|
const entries = tiles.map((tile) => tile.BaselineMask);
|
|
40994
41072
|
let lo = Number.MAX_SAFE_INTEGER;
|
|
40995
41073
|
let hi = 0;
|
|
@@ -41043,14 +41121,11 @@
|
|
|
41043
41121
|
/*
|
|
41044
41122
|
* Composites each tile's floor and ceiling into rasters matching the composited frames.
|
|
41045
41123
|
*
|
|
41046
|
-
* A
|
|
41047
|
-
*
|
|
41124
|
+
* A label indexes them by the same texel as the value canvas, so they have to be laid out the
|
|
41125
|
+
* same way rather than fetched as whole-extent rasters.
|
|
41048
41126
|
*/
|
|
41049
41127
|
async loadTiledExtremes() {
|
|
41050
41128
|
const tiles = this.tiles;
|
|
41051
|
-
if (!tiles) {
|
|
41052
|
-
return;
|
|
41053
|
-
}
|
|
41054
41129
|
const target = this.compositeExtent();
|
|
41055
41130
|
const size = this.compositeSize(tiles, target);
|
|
41056
41131
|
// One request for the whole extremes block. They sit contiguously after the frames, so
|
|
@@ -41145,70 +41220,6 @@
|
|
|
41145
41220
|
}
|
|
41146
41221
|
}));
|
|
41147
41222
|
}
|
|
41148
|
-
/*
|
|
41149
|
-
* Range GETs one entry and decodes it to an image.
|
|
41150
|
-
*/
|
|
41151
|
-
async loadImageAt(entry) {
|
|
41152
|
-
const start = entry.ByteOffset;
|
|
41153
|
-
const end = entry.ByteOffset + entry.ByteLength - 1;
|
|
41154
|
-
const response = await fetch(this.archiveUrl, { headers: { Range: `bytes=${start}-${end}` } });
|
|
41155
|
-
const buffer = await response.arrayBuffer();
|
|
41156
|
-
const objectUrl = URL.createObjectURL(new Blob([buffer], { type: "image/png" }));
|
|
41157
|
-
try {
|
|
41158
|
-
return await loadImage(objectUrl);
|
|
41159
|
-
}
|
|
41160
|
-
finally {
|
|
41161
|
-
URL.revokeObjectURL(objectUrl);
|
|
41162
|
-
}
|
|
41163
|
-
}
|
|
41164
|
-
/*
|
|
41165
|
-
* Puts a static raster through the same resample the frames get.
|
|
41166
|
-
*/
|
|
41167
|
-
remap(image) {
|
|
41168
|
-
if (!image) {
|
|
41169
|
-
return null;
|
|
41170
|
-
}
|
|
41171
|
-
const rect = this.sourceRect({ width: image.width, height: image.height });
|
|
41172
|
-
if (!rect) {
|
|
41173
|
-
return image;
|
|
41174
|
-
}
|
|
41175
|
-
const from = document.createElement("canvas");
|
|
41176
|
-
from.width = image.width;
|
|
41177
|
-
from.height = image.height;
|
|
41178
|
-
from.getContext("2d").putImageData(image, 0, 0);
|
|
41179
|
-
const to = document.createElement("canvas");
|
|
41180
|
-
to.width = image.width;
|
|
41181
|
-
to.height = image.height;
|
|
41182
|
-
const ctx = to.getContext("2d");
|
|
41183
|
-
ctx.imageSmoothingEnabled = false;
|
|
41184
|
-
ctx.clearRect(0, 0, image.width, image.height);
|
|
41185
|
-
ctx.drawImage(from, rect.x, rect.y, rect.width, rect.height, 0, 0, image.width, image.height);
|
|
41186
|
-
return ctx.getImageData(0, 0, image.width, image.height);
|
|
41187
|
-
}
|
|
41188
|
-
/*
|
|
41189
|
-
* Range GETs one entry out of the blob and decodes it to pixels.
|
|
41190
|
-
*/
|
|
41191
|
-
async loadImageDataAt(entry) {
|
|
41192
|
-
const start = entry.ByteOffset;
|
|
41193
|
-
const end = entry.ByteOffset + entry.ByteLength - 1;
|
|
41194
|
-
const response = await fetch(this.archiveUrl, { headers: { Range: `bytes=${start}-${end}` } });
|
|
41195
|
-
const buffer = await response.arrayBuffer();
|
|
41196
|
-
const blob = new Blob([buffer], { type: "image/png" });
|
|
41197
|
-
const objectUrl = URL.createObjectURL(blob);
|
|
41198
|
-
let image;
|
|
41199
|
-
try {
|
|
41200
|
-
image = await loadImage(objectUrl);
|
|
41201
|
-
}
|
|
41202
|
-
finally {
|
|
41203
|
-
URL.revokeObjectURL(objectUrl);
|
|
41204
|
-
}
|
|
41205
|
-
const canvas = document.createElement("canvas");
|
|
41206
|
-
canvas.width = image.width;
|
|
41207
|
-
canvas.height = image.height;
|
|
41208
|
-
const ctx = canvas.getContext("2d");
|
|
41209
|
-
ctx.drawImage(image, 0, 0);
|
|
41210
|
-
return ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
41211
|
-
}
|
|
41212
41223
|
/**
|
|
41213
41224
|
* Fetches and decodes the archive's static baseline mask, at most once.
|
|
41214
41225
|
*/
|
|
@@ -41351,7 +41362,7 @@
|
|
|
41351
41362
|
function Now() {
|
|
41352
41363
|
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
41353
41364
|
}
|
|
41354
|
-
})(TextureFrameSeriesAnimator || (TextureFrameSeriesAnimator = {}));
|
|
41365
|
+
})(exports.TextureFrameSeriesAnimator || (exports.TextureFrameSeriesAnimator = {}));
|
|
41355
41366
|
|
|
41356
41367
|
(function (TextureValueLabels) {
|
|
41357
41368
|
// Off by default: a caller can still set a floor, but thinning is the mechanism that keeps a
|
|
@@ -41399,6 +41410,52 @@
|
|
|
41399
41410
|
return out;
|
|
41400
41411
|
}
|
|
41401
41412
|
TextureValueLabels.ClipOutline = ClipOutline;
|
|
41413
|
+
/**
|
|
41414
|
+
* One cell's numbers as a label should print them, or null where the texture has no data there.
|
|
41415
|
+
* @param params at is the index of the cell's red channel. sourceMap is derived from the metadata
|
|
41416
|
+
* when omitted, which a caller reading many cells should hoist rather than repeat.
|
|
41417
|
+
*/
|
|
41418
|
+
function ReadingAt(params) {
|
|
41419
|
+
const { metadata, source, floor, ceiling, at } = params;
|
|
41420
|
+
if (source.data[at + 3] < 128) {
|
|
41421
|
+
return null;
|
|
41422
|
+
}
|
|
41423
|
+
const lo = metadata && typeof metadata.ValueMin === "number" ? metadata.ValueMin : 0;
|
|
41424
|
+
const hi = metadata && typeof metadata.ValueMax === "number" ? metadata.ValueMax : 1;
|
|
41425
|
+
const normalised = exports.TextureFrameSeriesAnimator.NormalisedAt(metadata, source.data, at);
|
|
41426
|
+
const toSource = params.sourceMap !== undefined
|
|
41427
|
+
? params.sourceMap
|
|
41428
|
+
: exports.TextureFrameSeriesAnimator.SourceMap(metadata);
|
|
41429
|
+
const inSource = (value) => exports.TextureFrameSeriesAnimator.ToSource(toSource, value);
|
|
41430
|
+
const reading = { value: inSource(lo + normalised * (hi - lo)) };
|
|
41431
|
+
if (exports.TextureFrameSeriesAnimator.IsTransformed(metadata)) {
|
|
41432
|
+
return reading;
|
|
41433
|
+
}
|
|
41434
|
+
const eLo = metadata && metadata.ExtremesValueMin;
|
|
41435
|
+
const eHi = metadata && metadata.ExtremesValueMax;
|
|
41436
|
+
if (typeof eLo === "number" && typeof eHi === "number") {
|
|
41437
|
+
if (floor) {
|
|
41438
|
+
reading.floor = inSource(eLo + decode16(floor.data, at) * (eHi - eLo));
|
|
41439
|
+
}
|
|
41440
|
+
if (ceiling) {
|
|
41441
|
+
reading.ceiling = inSource(eLo + decode16(ceiling.data, at) * (eHi - eLo));
|
|
41442
|
+
}
|
|
41443
|
+
}
|
|
41444
|
+
return reading;
|
|
41445
|
+
}
|
|
41446
|
+
TextureValueLabels.ReadingAt = ReadingAt;
|
|
41447
|
+
/**
|
|
41448
|
+
* The unit a label's value line should name, or undefined when the archive states none.
|
|
41449
|
+
* @param metadata the archive's Data.generation
|
|
41450
|
+
*/
|
|
41451
|
+
function LabelUnits(metadata) {
|
|
41452
|
+
if (!metadata) {
|
|
41453
|
+
return undefined;
|
|
41454
|
+
}
|
|
41455
|
+
const recovered = exports.TextureFrameSeriesAnimator.SourceMap(metadata);
|
|
41456
|
+
return (recovered && metadata.SourceUnits) || metadata.Units;
|
|
41457
|
+
}
|
|
41458
|
+
TextureValueLabels.LabelUnits = LabelUnits;
|
|
41402
41459
|
/**
|
|
41403
41460
|
* How many cells apart labels should sit, as a power of two.
|
|
41404
41461
|
* @param options cellPixels is one cell's size on screen, held is the stride already in use
|
|
@@ -41425,6 +41482,7 @@
|
|
|
41425
41482
|
TextureValueLabels.ChooseLabelStride = ChooseLabelStride;
|
|
41426
41483
|
class Labels {
|
|
41427
41484
|
constructor(options) {
|
|
41485
|
+
this.sourceMap = null;
|
|
41428
41486
|
this.source = null;
|
|
41429
41487
|
this.painted = null;
|
|
41430
41488
|
this.floor = null;
|
|
@@ -41436,6 +41494,9 @@
|
|
|
41436
41494
|
this.extent = options.extent;
|
|
41437
41495
|
this.settings = options.settings || {};
|
|
41438
41496
|
this.metadata = options.metadata;
|
|
41497
|
+
// Once per overlay rather than once per cell: a label pass probes thousands of texels
|
|
41498
|
+
// looking for one worth labelling, and the map cannot change while the metadata does not.
|
|
41499
|
+
this.sourceMap = exports.TextureFrameSeriesAnimator.SourceMap(options.metadata);
|
|
41439
41500
|
this.tiles = options.tiles;
|
|
41440
41501
|
this.clipRing = options.clipRing;
|
|
41441
41502
|
this.canvas = document.createElement("canvas");
|
|
@@ -41851,13 +41912,14 @@
|
|
|
41851
41912
|
widestLabel(parts) {
|
|
41852
41913
|
const sample = [];
|
|
41853
41914
|
if (parts.indexOf("value") >= 0) {
|
|
41854
|
-
sample.push("-000.00");
|
|
41915
|
+
sample.push(valueLine("-000.00", LabelUnits(this.metadata)));
|
|
41855
41916
|
}
|
|
41856
41917
|
const range = [];
|
|
41857
|
-
|
|
41918
|
+
const showsRange = !exports.TextureFrameSeriesAnimator.IsTransformed(this.metadata);
|
|
41919
|
+
if (showsRange && parts.indexOf("floor") >= 0) {
|
|
41858
41920
|
range.push("min: -000.00");
|
|
41859
41921
|
}
|
|
41860
|
-
if (parts.indexOf("ceiling") >= 0) {
|
|
41922
|
+
if (showsRange && parts.indexOf("ceiling") >= 0) {
|
|
41861
41923
|
range.push("max: -000.00");
|
|
41862
41924
|
}
|
|
41863
41925
|
if (range.length) {
|
|
@@ -41891,7 +41953,7 @@
|
|
|
41891
41953
|
const dp = (_a = this.settings.decimals) !== null && _a !== void 0 ? _a : (Math.abs(reading.value) < 10 ? 2 : 1);
|
|
41892
41954
|
const lines = [];
|
|
41893
41955
|
if (parts.indexOf("value") >= 0) {
|
|
41894
|
-
lines.push(reading.value.toFixed(dp));
|
|
41956
|
+
lines.push(valueLine(reading.value.toFixed(dp), LabelUnits(this.metadata)));
|
|
41895
41957
|
}
|
|
41896
41958
|
// Named rather than a bare pair: two numbers under a third say nothing about which is
|
|
41897
41959
|
// the series low and which is its high.
|
|
@@ -41965,29 +42027,17 @@
|
|
|
41965
42027
|
}
|
|
41966
42028
|
// Decodes one cell, skipping anything the texture marks as having no data.
|
|
41967
42029
|
readAt(col, row, cols) {
|
|
41968
|
-
|
|
41969
|
-
const i = (row * cols + col) * 4;
|
|
41970
|
-
if (!this.source || this.source.data[i + 3] < 128) {
|
|
42030
|
+
if (!this.source) {
|
|
41971
42031
|
return null;
|
|
41972
42032
|
}
|
|
41973
|
-
|
|
41974
|
-
|
|
41975
|
-
|
|
41976
|
-
|
|
41977
|
-
|
|
41978
|
-
|
|
41979
|
-
|
|
41980
|
-
|
|
41981
|
-
// An anomaly archive's frames are already floor-relative, so the reading a user
|
|
41982
|
-
// wants is the absolute one. This is the whole reason the floor is published.
|
|
41983
|
-
if (TextureFrameSeriesAnimator.IsAnomalyMeasure(this.metadata)) {
|
|
41984
|
-
reading.value += reading.floor;
|
|
41985
|
-
}
|
|
41986
|
-
}
|
|
41987
|
-
if (this.ceiling && typeof eLo === "number" && typeof eHi === "number") {
|
|
41988
|
-
reading.ceiling = eLo + decode16(this.ceiling.data, i) * (eHi - eLo);
|
|
41989
|
-
}
|
|
41990
|
-
return reading;
|
|
42033
|
+
return ReadingAt({
|
|
42034
|
+
metadata: this.metadata,
|
|
42035
|
+
source: this.source,
|
|
42036
|
+
floor: this.floor,
|
|
42037
|
+
ceiling: this.ceiling,
|
|
42038
|
+
at: (row * cols + col) * 4,
|
|
42039
|
+
sourceMap: this.sourceMap
|
|
42040
|
+
});
|
|
41991
42041
|
}
|
|
41992
42042
|
lonAt(col, cols) {
|
|
41993
42043
|
return this.extent.West + ((col + 0.5) / cols) * (this.extent.East - this.extent.West);
|
|
@@ -42026,6 +42076,13 @@
|
|
|
42026
42076
|
}
|
|
42027
42077
|
}
|
|
42028
42078
|
TextureValueLabels.Labels = Labels;
|
|
42079
|
+
/*
|
|
42080
|
+
* The value line. Units go on it alone: repeating them on the range underneath doubles the widest
|
|
42081
|
+
* label to say nothing, and the two lines are plainly the same quantity.
|
|
42082
|
+
*/
|
|
42083
|
+
function valueLine(value, units) {
|
|
42084
|
+
return units ? `${value} (${units})` : value;
|
|
42085
|
+
}
|
|
42029
42086
|
function decode16(data, i) {
|
|
42030
42087
|
return (data[i] * 256 + data[i + 1]) / 65535;
|
|
42031
42088
|
}
|
|
@@ -42046,11 +42103,6 @@
|
|
|
42046
42103
|
const SKIRT_FRACTION = 0.015;
|
|
42047
42104
|
// Alpha below which a texel counts as no-data and is not drawn at all.
|
|
42048
42105
|
const COVERAGE_CUTOFF = 0.35;
|
|
42049
|
-
// With no exaggeration stated, the full value range is drawn as this fraction of the extent's shorter side.
|
|
42050
|
-
const AUTO_RELIEF_FRACTION = 0.02;
|
|
42051
|
-
// Ceiling on the derived relief. Sizing purely off the extent is fine for a parcel and absurd for
|
|
42052
|
-
// a whole estuary: 4 m of water over an 82 km extent came out 1.6 km tall.
|
|
42053
|
-
const AUTO_MAX_RELIEF_METRES = 250.0;
|
|
42054
42106
|
// Terrain is sampled on this grid and interpolated between samples.
|
|
42055
42107
|
// Per-texel sampling would be hundreds of thousands of lookups for a surface whose own data is far coarser than that.
|
|
42056
42108
|
const GROUND_SAMPLES_PER_SIDE = 64;
|
|
@@ -42071,6 +42123,7 @@ in float skirt;
|
|
|
42071
42123
|
|
|
42072
42124
|
uniform sampler2D u_valueTexture;
|
|
42073
42125
|
uniform sampler2D u_groundTexture;
|
|
42126
|
+
uniform float u_valuePacked;
|
|
42074
42127
|
uniform float u_valueMin;
|
|
42075
42128
|
uniform float u_valueRange;
|
|
42076
42129
|
uniform float u_exaggeration;
|
|
@@ -42108,18 +42161,37 @@ float sampleGround(vec2 uv) {
|
|
|
42108
42161
|
return mix(mix(h00, h10, f.x), mix(h01, h11, f.x), f.y);
|
|
42109
42162
|
}
|
|
42110
42163
|
|
|
42164
|
+
/*
|
|
42165
|
+
* The texel's normalised value, whichever way the archive packed it.
|
|
42166
|
+
*
|
|
42167
|
+
* A packed frame keeps the high byte in R, so reading R alone would quantise the whole range to 255
|
|
42168
|
+
* steps and draw a plausible wrong surface rather than an obviously broken one.
|
|
42169
|
+
*
|
|
42170
|
+
* Each channel is scaled and then added, rather than reassembled to a 0..65535 integer and divided.
|
|
42171
|
+
* That integer is outside the range mediump guarantees, and a fragment shader only gets highp where
|
|
42172
|
+
* the device offers it, so the obvious form loses the low byte on exactly the older mobile hardware
|
|
42173
|
+
* that Cesium falls back to mediump for.
|
|
42174
|
+
*/
|
|
42175
|
+
float decodeValue(vec4 texel) {
|
|
42176
|
+
float packed16 = texel.r * (255.0 * 256.0 / 65535.0) + texel.g * (255.0 / 65535.0);
|
|
42177
|
+
return mix(texel.r, packed16, u_valuePacked);
|
|
42178
|
+
}
|
|
42179
|
+
|
|
42111
42180
|
void main() {
|
|
42112
42181
|
vec4 texel = texture(u_valueTexture, st);
|
|
42113
42182
|
v_st = st;
|
|
42114
|
-
v_value = texel
|
|
42183
|
+
v_value = decodeValue(texel);
|
|
42115
42184
|
v_coverage = texel.a;
|
|
42116
42185
|
|
|
42117
|
-
|
|
42186
|
+
// Exaggeration stretches the span above the archive's minimum rather than the height itself. On an
|
|
42187
|
+
// elevation the height is measured from the ellipsoid, tens of metres from any of the data, so
|
|
42188
|
+
// scaling that instead translates the whole sheet underground rather than spreading it out.
|
|
42189
|
+
float metres = u_valueMin + v_value * u_valueRange * u_exaggeration;
|
|
42118
42190
|
|
|
42119
42191
|
// Value zero sits at the polygon's own altitude, plus the terrain underneath when the polygon
|
|
42120
42192
|
// is following the ground rather than absolutely positioned.
|
|
42121
42193
|
float ground = sampleGround(st) * u_groundWeight;
|
|
42122
|
-
float displacement = u_baseHeight + ground + metres *
|
|
42194
|
+
float displacement = u_baseHeight + ground + metres * texel.a - skirt * u_skirtDepth;
|
|
42123
42195
|
|
|
42124
42196
|
// Added to the LOW half of the encoded position: a metre-scale offset added to the high half
|
|
42125
42197
|
// would be lost to float32 rounding at an earth radius.
|
|
@@ -42132,6 +42204,7 @@ uniform sampler2D u_valueTexture;
|
|
|
42132
42204
|
uniform sampler2D u_rampTexture;
|
|
42133
42205
|
uniform float u_coverageCutoff;
|
|
42134
42206
|
uniform vec2 u_texelSize;
|
|
42207
|
+
uniform float u_valuePacked;
|
|
42135
42208
|
uniform float u_valueRange;
|
|
42136
42209
|
uniform float u_exaggeration;
|
|
42137
42210
|
uniform float u_metresPerTexel;
|
|
@@ -42140,6 +42213,16 @@ in vec2 v_st;
|
|
|
42140
42213
|
in float v_value;
|
|
42141
42214
|
in float v_coverage;
|
|
42142
42215
|
|
|
42216
|
+
/*
|
|
42217
|
+
* Scaled per channel for the same reason the vertex shader's decode is: reassembling the pair reaches
|
|
42218
|
+
* 65535, which mediump does not have to represent, and this is the shader that may not get highp.
|
|
42219
|
+
*/
|
|
42220
|
+
float valueAt(vec2 uv) {
|
|
42221
|
+
vec4 texel = texture(u_valueTexture, uv);
|
|
42222
|
+
float packed16 = texel.r * (255.0 * 256.0 / 65535.0) + texel.g * (255.0 / 65535.0);
|
|
42223
|
+
return mix(texel.r, packed16, u_valuePacked);
|
|
42224
|
+
}
|
|
42225
|
+
|
|
42143
42226
|
void main() {
|
|
42144
42227
|
if (v_coverage < u_coverageCutoff) {
|
|
42145
42228
|
discard;
|
|
@@ -42154,10 +42237,10 @@ void main() {
|
|
|
42154
42237
|
|
|
42155
42238
|
// Relief shading from the value gradient, so the displacement reads at a distance without
|
|
42156
42239
|
// recomputing vertex normals every time the texture changes.
|
|
42157
|
-
float left =
|
|
42158
|
-
float right =
|
|
42159
|
-
float down =
|
|
42160
|
-
float up =
|
|
42240
|
+
float left = valueAt(v_st - vec2(u_texelSize.x, 0.0));
|
|
42241
|
+
float right = valueAt(v_st + vec2(u_texelSize.x, 0.0));
|
|
42242
|
+
float down = valueAt(v_st - vec2(0.0, u_texelSize.y));
|
|
42243
|
+
float up = valueAt(v_st + vec2(0.0, u_texelSize.y));
|
|
42161
42244
|
float scale = u_valueRange * u_exaggeration;
|
|
42162
42245
|
vec3 n = normalize(vec3((left - right) * scale, (down - up) * scale, 2.0 * u_metresPerTexel));
|
|
42163
42246
|
float lambert = clamp(dot(n, normalize(vec3(-0.5, -0.6, 0.62))), 0.0, 1.0);
|
|
@@ -42367,8 +42450,9 @@ void main() {
|
|
|
42367
42450
|
this.source = options.source || null;
|
|
42368
42451
|
this.valueMin = (_a = options.valueMin) !== null && _a !== void 0 ? _a : 0;
|
|
42369
42452
|
this.valueMax = (_b = options.valueMax) !== null && _b !== void 0 ? _b : 1;
|
|
42370
|
-
this.exaggeration = (_c = options.exaggeration) !== null && _c !== void 0 ? _c :
|
|
42453
|
+
this.exaggeration = (_c = options.exaggeration) !== null && _c !== void 0 ? _c : 1;
|
|
42371
42454
|
this.baseHeight = (_d = options.baseHeight) !== null && _d !== void 0 ? _d : 0;
|
|
42455
|
+
this.packedValue = Boolean(options.packedValue);
|
|
42372
42456
|
this.pixelPlacement = options.pixelPlacement;
|
|
42373
42457
|
this.tileSkirts = Boolean(options.tileSkirts);
|
|
42374
42458
|
this.rampPixels = RampLookup(options.rampStops || null, options.lowColor || DEFAULT_LOW_COLOR, options.highColor || DEFAULT_HIGH_COLOR);
|
|
@@ -42592,6 +42676,14 @@ void main() {
|
|
|
42592
42676
|
if (this.texture) {
|
|
42593
42677
|
this.texture.destroy();
|
|
42594
42678
|
}
|
|
42679
|
+
// Nearest for a packed value, because filtering would blend the two bytes of the
|
|
42680
|
+
// packing rather than the values they encode, the same trap sampleGround avoids.
|
|
42681
|
+
const minification = this.packedValue
|
|
42682
|
+
? Cesium.TextureMinificationFilter.NEAREST
|
|
42683
|
+
: Cesium.TextureMinificationFilter.LINEAR;
|
|
42684
|
+
const magnification = this.packedValue
|
|
42685
|
+
? Cesium.TextureMagnificationFilter.NEAREST
|
|
42686
|
+
: Cesium.TextureMagnificationFilter.LINEAR;
|
|
42595
42687
|
this.texture = new Cesium.Texture({
|
|
42596
42688
|
context,
|
|
42597
42689
|
source: this.source,
|
|
@@ -42601,8 +42693,8 @@ void main() {
|
|
|
42601
42693
|
sampler: new Cesium.Sampler({
|
|
42602
42694
|
wrapS: Cesium.TextureWrap.CLAMP_TO_EDGE,
|
|
42603
42695
|
wrapT: Cesium.TextureWrap.CLAMP_TO_EDGE,
|
|
42604
|
-
minificationFilter:
|
|
42605
|
-
magnificationFilter:
|
|
42696
|
+
minificationFilter: minification,
|
|
42697
|
+
magnificationFilter: magnification
|
|
42606
42698
|
})
|
|
42607
42699
|
});
|
|
42608
42700
|
this.textureDirty = false;
|
|
@@ -42703,6 +42795,7 @@ void main() {
|
|
|
42703
42795
|
uniformMap: {
|
|
42704
42796
|
u_valueTexture: () => self.texture,
|
|
42705
42797
|
u_groundTexture: () => self.groundTexture,
|
|
42798
|
+
u_valuePacked: () => (self.packedValue ? 1 : 0),
|
|
42706
42799
|
u_groundWeight: () => (self.followGround && self.groundPixels ? 1 : 0),
|
|
42707
42800
|
u_groundRange: () => new Cesium.Cartesian2(GROUND_MIN_METRES, GROUND_MAX_METRES),
|
|
42708
42801
|
u_groundSize: () => new Cesium.Cartesian2(self.groundPixels ? GROUND_SAMPLES_PER_SIDE : 1, self.groundPixels ? GROUND_SAMPLES_PER_SIDE : 1),
|
|
@@ -42755,31 +42848,6 @@ void main() {
|
|
|
42755
42848
|
}
|
|
42756
42849
|
return tiles;
|
|
42757
42850
|
}
|
|
42758
|
-
/**
|
|
42759
|
-
* Exaggeration that makes the full value range stand AUTO_RELIEF_FRACTION of the extent tall.
|
|
42760
|
-
*
|
|
42761
|
-
* Derived from the whole extent rather than per tile: scaling each tile to its own size would
|
|
42762
|
-
* make neighbours disagree along their shared edge and tear the surface apart.
|
|
42763
|
-
* @param extent
|
|
42764
|
-
* @param valueMin
|
|
42765
|
-
* @param valueMax
|
|
42766
|
-
*/
|
|
42767
|
-
function autoExaggeration(extent, valueMin, valueMax) {
|
|
42768
|
-
const range = Math.abs(valueMax - valueMin);
|
|
42769
|
-
if (!(range > 0)) {
|
|
42770
|
-
return 1;
|
|
42771
|
-
}
|
|
42772
|
-
const southWest = Cesium.Cartesian3.fromDegrees(extent.West, extent.South, 0);
|
|
42773
|
-
const southEast = Cesium.Cartesian3.fromDegrees(extent.East, extent.South, 0);
|
|
42774
|
-
const northWest = Cesium.Cartesian3.fromDegrees(extent.West, extent.North, 0);
|
|
42775
|
-
const shorterSide = Math.min(Cesium.Cartesian3.distance(southWest, southEast), Cesium.Cartesian3.distance(southWest, northWest));
|
|
42776
|
-
if (!(shorterSide > 0)) {
|
|
42777
|
-
return 1;
|
|
42778
|
-
}
|
|
42779
|
-
const relief = Math.min(shorterSide * AUTO_RELIEF_FRACTION, AUTO_MAX_RELIEF_METRES);
|
|
42780
|
-
return relief / range;
|
|
42781
|
-
}
|
|
42782
|
-
DisplacedSurfacePrimitive.autoExaggeration = autoExaggeration;
|
|
42783
42851
|
function now() {
|
|
42784
42852
|
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
42785
42853
|
}
|
|
@@ -43517,12 +43585,12 @@ void main() {
|
|
|
43517
43585
|
&& existingAnimator.GetArchiveUrl() === extrusionArchive.url
|
|
43518
43586
|
&& existingSurface
|
|
43519
43587
|
&& !existingSurface.isDestroyed();
|
|
43520
|
-
const placement = surfacePlacement(entity, heightRef);
|
|
43588
|
+
const placement = surfacePlacement(entity, heightRef, extrusionArchive && exports.TextureFrameSeriesAnimator.IsDatumHeight(extrusionArchive.metadata));
|
|
43521
43589
|
// Whether the surface follows terrain is baked into its ground sampling, so a change there has
|
|
43522
43590
|
// to rebuild rather than update in place.
|
|
43523
43591
|
if (reusable && existingSurface.GetFollowsGround() === placement.followGround) {
|
|
43524
43592
|
existingSurface.SetBaseHeight(placement.baseHeight);
|
|
43525
|
-
existingSurface.SetExaggeration(exaggerationFor(style
|
|
43593
|
+
existingSurface.SetExaggeration(exaggerationFor(style));
|
|
43526
43594
|
return;
|
|
43527
43595
|
}
|
|
43528
43596
|
disposeDisplacedSurface(cEntity, viewer);
|
|
@@ -43536,7 +43604,7 @@ void main() {
|
|
|
43536
43604
|
}
|
|
43537
43605
|
// Drives the value canvas only: the surface paints the colour, so the polygon's own material is
|
|
43538
43606
|
// left transparent rather than animated underneath it.
|
|
43539
|
-
const animator = new TextureFrameSeriesAnimator.Animator({
|
|
43607
|
+
const animator = new exports.TextureFrameSeriesAnimator.Animator({
|
|
43540
43608
|
viewer,
|
|
43541
43609
|
entity: cEntity,
|
|
43542
43610
|
archiveUrl: extrusionArchive.url,
|
|
@@ -43546,8 +43614,6 @@ void main() {
|
|
|
43546
43614
|
produceValueCanvas: true,
|
|
43547
43615
|
metadata: extrusionArchive.metadata,
|
|
43548
43616
|
cellTexels: 1,
|
|
43549
|
-
baselineMask: extrusionArchive.metadata.BaselineMask,
|
|
43550
|
-
// Off-type for the same reason as the fill path: the pinned bruce-models predates the field.
|
|
43551
43617
|
maskBaseline: style.maskTextureBaseline
|
|
43552
43618
|
});
|
|
43553
43619
|
cEntity[EXTRUSION_ANIMATOR_KEY] = animator;
|
|
@@ -43562,13 +43628,14 @@ void main() {
|
|
|
43562
43628
|
terrainProvider: viewer && viewer.terrainProvider,
|
|
43563
43629
|
valueMin: extrusionArchive.metadata.ValueMin,
|
|
43564
43630
|
valueMax: extrusionArchive.metadata.ValueMax,
|
|
43565
|
-
|
|
43631
|
+
packedValue: exports.TextureFrameSeriesAnimator.IsPackedValue(extrusionArchive.metadata),
|
|
43632
|
+
exaggeration: exaggerationFor(style),
|
|
43566
43633
|
lowColor: mask ? BModels.Color.ColorFromStr(mask.minColor) : null,
|
|
43567
43634
|
highColor: mask ? BModels.Color.ColorFromStr(mask.maxColor) : null,
|
|
43568
43635
|
// Normalised the same way the animator normalises them, so the surface and the flat drape
|
|
43569
43636
|
// agree about which values the style hides.
|
|
43570
|
-
rampStops: (mask && mask.points || []).map((stop) => ({
|
|
43571
|
-
position: TextureFrameSeriesAnimator.NormalisePosition(extrusionArchive.metadata, stop.position),
|
|
43637
|
+
rampStops: ((mask && mask.points) || []).map((stop) => ({
|
|
43638
|
+
position: exports.TextureFrameSeriesAnimator.NormalisePosition(extrusionArchive.metadata, stop.position),
|
|
43572
43639
|
color: BModels.Color.ColorFromStr(stop.color)
|
|
43573
43640
|
})).filter((stop) => Boolean(stop.color))
|
|
43574
43641
|
});
|
|
@@ -43583,7 +43650,7 @@ void main() {
|
|
|
43583
43650
|
* @param entity
|
|
43584
43651
|
* @param heightRef
|
|
43585
43652
|
*/
|
|
43586
|
-
function surfacePlacement(entity, heightRef) {
|
|
43653
|
+
function surfacePlacement(entity, heightRef, datumHeight) {
|
|
43587
43654
|
const rawAltitude = BModels.Entity.GetValue({
|
|
43588
43655
|
entity,
|
|
43589
43656
|
path: ["Bruce", "Location", "altitude"]
|
|
@@ -43595,32 +43662,18 @@ void main() {
|
|
|
43595
43662
|
if (heightRef === Cesium.HeightReference.RELATIVE_TO_GROUND) {
|
|
43596
43663
|
return { baseHeight: altitude, followGround: true };
|
|
43597
43664
|
}
|
|
43598
|
-
|
|
43665
|
+
// Values that ARE heights against a datum already say where they belong, so adding the entity's
|
|
43666
|
+
// altitude on top would move a measured surface off the datum it was measured against.
|
|
43667
|
+
return { baseHeight: datumHeight ? 0 : altitude, followGround: false };
|
|
43599
43668
|
}
|
|
43600
|
-
/**
|
|
43601
|
-
* The archive's own extent when it recorded one, otherwise the polygon's own bounds so archives
|
|
43602
|
-
* generated before the extent was written still render somewhere sensible.
|
|
43603
|
-
* @param metadata
|
|
43604
|
-
* @param posses
|
|
43605
|
-
*/
|
|
43606
43669
|
/*
|
|
43607
|
-
* The factor a displaced surface should use,
|
|
43608
|
-
*
|
|
43609
|
-
* An elevation is a real height against a datum, so scaling it is not a display choice: it lifts the
|
|
43610
|
-
* surface off the datum it belongs to. Only a thickness is safe to exaggerate, which is why an
|
|
43611
|
-
* elevation archive defaults to 1 rather than to a derived factor.
|
|
43670
|
+
* The factor a displaced surface should use, which is the style's or nothing.
|
|
43612
43671
|
*/
|
|
43613
|
-
function exaggerationFor(style
|
|
43672
|
+
function exaggerationFor(style) {
|
|
43614
43673
|
if (style.extrusionExaggeration != null) {
|
|
43615
43674
|
return style.extrusionExaggeration;
|
|
43616
43675
|
}
|
|
43617
|
-
|
|
43618
|
-
return 1;
|
|
43619
|
-
}
|
|
43620
|
-
if (!extent) {
|
|
43621
|
-
return undefined;
|
|
43622
|
-
}
|
|
43623
|
-
return exports.DisplacedSurfacePrimitive.autoExaggeration(extent, metadata.ValueMin, metadata.ValueMax);
|
|
43676
|
+
return 1;
|
|
43624
43677
|
}
|
|
43625
43678
|
/*
|
|
43626
43679
|
* The lon/lat bounding rectangle of a ring, which is the frame Cesium drapes an image material in.
|
|
@@ -43696,7 +43749,7 @@ void main() {
|
|
|
43696
43749
|
// Appearance is part of the reuse test, not just the archive URL. Recolouring a texture-driven
|
|
43697
43750
|
// polygon keeps the same archive, so matching on the URL alone kept the old Animator and the
|
|
43698
43751
|
// style edit appeared to do nothing at all.
|
|
43699
|
-
const appearance = TextureFrameSeriesAnimator.AppearanceSignature({
|
|
43752
|
+
const appearance = exports.TextureFrameSeriesAnimator.AppearanceSignature({
|
|
43700
43753
|
textureColorMask, cellBorder, cellTexels, maskBaseline: maskTextureBaseline,
|
|
43701
43754
|
valueCanvas: produceValueCanvas, drape: drapeExtent
|
|
43702
43755
|
});
|
|
@@ -43712,7 +43765,7 @@ void main() {
|
|
|
43712
43765
|
if (!frameArchive) {
|
|
43713
43766
|
return null;
|
|
43714
43767
|
}
|
|
43715
|
-
const animator = new TextureFrameSeriesAnimator.Animator({
|
|
43768
|
+
const animator = new exports.TextureFrameSeriesAnimator.Animator({
|
|
43716
43769
|
viewer,
|
|
43717
43770
|
entity: cEntity,
|
|
43718
43771
|
archiveUrl: frameArchive.url,
|
|
@@ -43721,7 +43774,6 @@ void main() {
|
|
|
43721
43774
|
metadata: frameArchive.metadata,
|
|
43722
43775
|
cellBorder,
|
|
43723
43776
|
cellTexels,
|
|
43724
|
-
baselineMask: frameArchive.metadata.BaselineMask,
|
|
43725
43777
|
maskBaseline: maskTextureBaseline,
|
|
43726
43778
|
produceValueCanvas,
|
|
43727
43779
|
drapeExtent
|
|
@@ -43782,20 +43834,19 @@ void main() {
|
|
|
43782
43834
|
}
|
|
43783
43835
|
const labels = new exports.TextureValueLabels.Labels({
|
|
43784
43836
|
viewer, extent, settings, metadata,
|
|
43785
|
-
tiles: TextureFrameSeriesAnimator.
|
|
43786
|
-
|
|
43787
|
-
|
|
43788
|
-
|
|
43789
|
-
}))
|
|
43790
|
-
: undefined,
|
|
43837
|
+
tiles: exports.TextureFrameSeriesAnimator.TilesOf(metadata).map((tile) => ({
|
|
43838
|
+
West: tile.West, East: tile.East, South: tile.South, North: tile.North,
|
|
43839
|
+
ResolutionX: tile.ResolutionX, ResolutionY: tile.ResolutionY
|
|
43840
|
+
})),
|
|
43791
43841
|
clipRing: params.clipRing
|
|
43792
43842
|
});
|
|
43793
43843
|
let lastVersion = -1;
|
|
43794
43844
|
let appliedFloor = null;
|
|
43795
43845
|
let appliedCeiling = null;
|
|
43796
|
-
//
|
|
43797
|
-
|
|
43798
|
-
|
|
43846
|
+
// Two extra Range GETs, and worth it only because a label draws the cell's own range from them.
|
|
43847
|
+
if (!exports.TextureFrameSeriesAnimator.IsTransformed(metadata)) {
|
|
43848
|
+
animator.EnsureExtremes();
|
|
43849
|
+
}
|
|
43799
43850
|
const remove = viewer.scene.preRender.addEventListener(() => {
|
|
43800
43851
|
if (animator.IsDisposed()) {
|
|
43801
43852
|
return;
|
|
@@ -43920,11 +43971,15 @@ void main() {
|
|
|
43920
43971
|
const { clientFile } = await BModels.ClientFile.Get({ api, fileId: clientFileId });
|
|
43921
43972
|
url = clientFile.URL;
|
|
43922
43973
|
const clientFileData = clientFile.Data;
|
|
43923
|
-
if (TextureFrameSeriesAnimator.IsFrameArchiveMetadata(clientFileData)) {
|
|
43974
|
+
if (exports.TextureFrameSeriesAnimator.IsFrameArchiveMetadata(clientFileData)) {
|
|
43975
|
+
const adapted = exports.TextureFrameSeriesAnimator.Adapt(clientFileData.generation);
|
|
43976
|
+
if (!adapted) {
|
|
43977
|
+
return { dataUri: null, effective: textureTrace.effective };
|
|
43978
|
+
}
|
|
43924
43979
|
return {
|
|
43925
43980
|
dataUri: null,
|
|
43926
43981
|
effective: textureTrace.effective,
|
|
43927
|
-
frameArchive: { url, metadata:
|
|
43982
|
+
frameArchive: { url, metadata: adapted }
|
|
43928
43983
|
};
|
|
43929
43984
|
}
|
|
43930
43985
|
}
|
|
@@ -43952,8 +44007,12 @@ void main() {
|
|
|
43952
44007
|
}
|
|
43953
44008
|
const url = clientFile.URL;
|
|
43954
44009
|
const clientFileData = clientFile.Data;
|
|
43955
|
-
if (TextureFrameSeriesAnimator.IsFrameArchiveMetadata(clientFileData)) {
|
|
43956
|
-
|
|
44010
|
+
if (exports.TextureFrameSeriesAnimator.IsFrameArchiveMetadata(clientFileData)) {
|
|
44011
|
+
const adapted = exports.TextureFrameSeriesAnimator.Adapt(clientFileData.generation);
|
|
44012
|
+
if (adapted) {
|
|
44013
|
+
return { dataUri: null, effective: null, frameArchive: { url, metadata: adapted } };
|
|
44014
|
+
}
|
|
44015
|
+
return { dataUri: null, effective: null };
|
|
43957
44016
|
}
|
|
43958
44017
|
const cacheKey = "texture-" + url + "-none";
|
|
43959
44018
|
let prom = _textureCache.Get(cacheKey);
|
|
@@ -45422,7 +45481,7 @@ void main() {
|
|
|
45422
45481
|
StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
|
|
45423
45482
|
})(exports.StyleUtils || (exports.StyleUtils = {}));
|
|
45424
45483
|
|
|
45425
|
-
const VERSION = "7.2.
|
|
45484
|
+
const VERSION = "7.2.4";
|
|
45426
45485
|
/**
|
|
45427
45486
|
* Updates the environment instance used by bruce-cesium to one specified.
|
|
45428
45487
|
* This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.
|