bruce-cesium 7.5.8 → 7.5.9
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 +938 -274
- package/dist/bruce-cesium.es5.js.map +1 -1
- package/dist/bruce-cesium.umd.js +936 -272
- package/dist/bruce-cesium.umd.js.map +1 -1
- package/dist/lib/bruce-cesium.js +1 -1
- package/dist/lib/rendering/displaced-surface-primitive.js +187 -36
- package/dist/lib/rendering/displaced-surface-primitive.js.map +1 -1
- package/dist/lib/rendering/entity-render-engine-polygon.js +20 -9
- package/dist/lib/rendering/entity-render-engine-polygon.js.map +1 -1
- package/dist/lib/rendering/render-managers/common/entity-label.js +2 -1
- package/dist/lib/rendering/render-managers/common/entity-label.js.map +1 -1
- package/dist/lib/rendering/texture-frame-series-animator.js +220 -20
- package/dist/lib/rendering/texture-frame-series-animator.js.map +1 -1
- package/dist/lib/rendering/texture-value-labels.js +478 -194
- package/dist/lib/rendering/texture-value-labels.js.map +1 -1
- package/dist/lib/rendering/visuals-register.js +27 -9
- package/dist/lib/rendering/visuals-register.js.map +1 -1
- package/dist/types/bruce-cesium.d.ts +1 -1
- package/dist/types/rendering/displaced-surface-primitive.d.ts +32 -0
- package/dist/types/rendering/entity-render-engine-polygon.d.ts +1 -0
- package/dist/types/rendering/texture-frame-series-animator.d.ts +42 -0
- package/dist/types/rendering/texture-value-labels.d.ts +59 -11
- package/dist/types/rendering/visuals-register.d.ts +6 -0
- package/package.json +1 -1
package/dist/bruce-cesium.umd.js
CHANGED
|
@@ -7295,7 +7295,8 @@
|
|
|
7295
7295
|
this.groupId = groupId;
|
|
7296
7296
|
this.additionalTexts = this.options.text != null ? [] : null;
|
|
7297
7297
|
this.photo = null;
|
|
7298
|
-
|
|
7298
|
+
// A label given its position keeps it, or the rebuild goes looking for the entity's own.
|
|
7299
|
+
this.createLabel(this.prePositioned ? this._pos3d : undefined);
|
|
7299
7300
|
}
|
|
7300
7301
|
/**
|
|
7301
7302
|
* Creates cesium entity for the label if one does not exist.
|
|
@@ -13789,7 +13790,9 @@
|
|
|
13789
13790
|
entityId: params.entityId,
|
|
13790
13791
|
menuItemId: params.menuItemId,
|
|
13791
13792
|
position: params.position ? { ...params.position } : undefined,
|
|
13792
|
-
createIfMissing: params.createIfMissing !== false
|
|
13793
|
+
createIfMissing: params.createIfMissing !== false,
|
|
13794
|
+
text: params.text,
|
|
13795
|
+
pos3d: params.pos3d ? Cesium.Cartesian3.clone(params.pos3d) : undefined
|
|
13793
13796
|
};
|
|
13794
13797
|
const doc = this.viewer.container.ownerDocument;
|
|
13795
13798
|
// Only a label following the cursor should go when the cursor leaves the canvas.
|
|
@@ -13805,6 +13808,13 @@
|
|
|
13805
13808
|
this.SyncLabelHover();
|
|
13806
13809
|
this.viewer.scene.requestRender();
|
|
13807
13810
|
}
|
|
13811
|
+
/*
|
|
13812
|
+
* Which Entity holds the label hover, if any, so a caller can leave another's hover alone.
|
|
13813
|
+
*/
|
|
13814
|
+
GetLabelHoverTarget() {
|
|
13815
|
+
const hover = this.labelHover;
|
|
13816
|
+
return hover ? { entityId: hover.entityId, menuItemId: hover.menuItemId } : undefined;
|
|
13817
|
+
}
|
|
13808
13818
|
/*
|
|
13809
13819
|
* Whether the label hover is on this Entity, with or without a cursor position.
|
|
13810
13820
|
*/
|
|
@@ -13817,7 +13827,7 @@
|
|
|
13817
13827
|
return (hover === null || hover === void 0 ? void 0 : hover.entityId) === entityId && (hover.menuItemId == null || hover.menuItemId === menuItemId) ? hover.position : undefined;
|
|
13818
13828
|
}
|
|
13819
13829
|
SyncLabelHover() {
|
|
13820
|
-
var _a, _b, _c;
|
|
13830
|
+
var _a, _b, _c, _d;
|
|
13821
13831
|
const hover = this.labelHover;
|
|
13822
13832
|
if (!hover || this.disposed) {
|
|
13823
13833
|
return;
|
|
@@ -13849,12 +13859,24 @@
|
|
|
13849
13859
|
this.ReleaseHoverLabel(true);
|
|
13850
13860
|
return;
|
|
13851
13861
|
}
|
|
13862
|
+
const hoverOptions = {
|
|
13863
|
+
presentation: "text",
|
|
13864
|
+
text: (_b = hover.text) !== null && _b !== void 0 ? _b : group === null || group === void 0 ? void 0 : group.options.text,
|
|
13865
|
+
selectOnClick: false
|
|
13866
|
+
};
|
|
13852
13867
|
if (this.hoverLabel && !this.hoverLabel.Disposed) {
|
|
13868
|
+
// Rebuilds only when the text changed, as it does when the pointer crosses a cell.
|
|
13869
|
+
if (hover.pos3d && !Cesium.Cartesian3.equals(hover.pos3d, this.hoverLabel.pos3d)) {
|
|
13870
|
+
this.hoverLabel.pos3d = hover.pos3d;
|
|
13871
|
+
}
|
|
13872
|
+
if (hover.text != null) {
|
|
13873
|
+
this.hoverLabel.Configure(hoverOptions);
|
|
13874
|
+
}
|
|
13853
13875
|
this.hoverLabel.SetHoverVisible(true);
|
|
13854
13876
|
return;
|
|
13855
13877
|
}
|
|
13856
13878
|
// Query-only icon groups have coordinates but no registered scene visual to attach to.
|
|
13857
|
-
if (!rego && group && [group.item.longitude, group.item.latitude, (
|
|
13879
|
+
if (!rego && group && [group.item.longitude, group.item.latitude, (_c = group.item.altitude) !== null && _c !== void 0 ? _c : 0].every(Number.isFinite)) {
|
|
13858
13880
|
rego = {
|
|
13859
13881
|
entityId: hover.entityId,
|
|
13860
13882
|
menuItemId: hover.menuItemId,
|
|
@@ -13876,15 +13898,12 @@
|
|
|
13876
13898
|
rego,
|
|
13877
13899
|
viewer: this.viewer,
|
|
13878
13900
|
visualRegister: this,
|
|
13879
|
-
api: (
|
|
13901
|
+
api: (_d = this.ApiGetters) === null || _d === void 0 ? void 0 : _d.GetBruceApi(),
|
|
13880
13902
|
persistent: false,
|
|
13881
13903
|
transient: true,
|
|
13882
13904
|
hoverRingState: this.hoverRingState,
|
|
13883
|
-
|
|
13884
|
-
|
|
13885
|
-
text: group === null || group === void 0 ? void 0 : group.options.text,
|
|
13886
|
-
selectOnClick: false
|
|
13887
|
-
},
|
|
13905
|
+
pos3d: hover.pos3d,
|
|
13906
|
+
options: hoverOptions,
|
|
13888
13907
|
visible: () => {
|
|
13889
13908
|
var _a;
|
|
13890
13909
|
const current = this.GetState(hover);
|
|
@@ -46708,6 +46727,10 @@
|
|
|
46708
46727
|
TextureFrameSeriesAnimator.AppearanceSignature = AppearanceSignature;
|
|
46709
46728
|
const MAX_COMPOSITE_TEXELS = 2048;
|
|
46710
46729
|
const VALUE_DILATE_PASSES = 2;
|
|
46730
|
+
// Decoded frame bytes kept at once. A 2048 square composite is 16 MB, so a long series cannot all stay.
|
|
46731
|
+
const MAX_CACHED_FRAME_BYTES = 256 * 1024 * 1024;
|
|
46732
|
+
// Fetched bytes kept for decoding again, a small fraction of what they decode to.
|
|
46733
|
+
const MAX_RAW_FRAME_BYTES = 96 * 1024 * 1024;
|
|
46711
46734
|
/*
|
|
46712
46735
|
* Rewrites a packed 16 bit value as the 8 bit grey the colour ramps index on.
|
|
46713
46736
|
*
|
|
@@ -46728,19 +46751,33 @@
|
|
|
46728
46751
|
/*
|
|
46729
46752
|
* Bleeds covered values outward into uncovered texels, leaving alpha untouched.
|
|
46730
46753
|
*/
|
|
46731
|
-
function
|
|
46754
|
+
function DilateValues(value, width, height, passes, packed) {
|
|
46732
46755
|
const texels = width * height;
|
|
46733
|
-
|
|
46756
|
+
// 255 for a texel that had data to begin with, otherwise the 1 based pass that filled it, so a pass
|
|
46757
|
+
// reads only texels filled before it and can work in place.
|
|
46758
|
+
const stamp = new Uint8Array(texels);
|
|
46734
46759
|
for (let p = 0; p < texels; p++) {
|
|
46735
|
-
|
|
46760
|
+
stamp[p] = value[p * 4 + 3] !== 0 ? 255 : 0;
|
|
46736
46761
|
}
|
|
46762
|
+
// Whether a texel or a horizontal neighbour had data, so empty space is skipped in three reads.
|
|
46763
|
+
const across = new Uint8Array(texels);
|
|
46737
46764
|
for (let pass = 0; pass < passes; pass++) {
|
|
46738
|
-
const
|
|
46739
|
-
const
|
|
46765
|
+
const current = pass + 1;
|
|
46766
|
+
const had = (p) => stamp[p] !== 0 && stamp[p] !== current;
|
|
46767
|
+
for (let y = 0; y < height; y++) {
|
|
46768
|
+
const row = y * width;
|
|
46769
|
+
for (let x = 0; x < width; x++) {
|
|
46770
|
+
const p = row + x;
|
|
46771
|
+
across[p] = (had(p) || (x > 0 && had(p - 1)) || (x + 1 < width && had(p + 1))) ? 1 : 0;
|
|
46772
|
+
}
|
|
46773
|
+
}
|
|
46740
46774
|
for (let y = 0; y < height; y++) {
|
|
46741
46775
|
for (let x = 0; x < width; x++) {
|
|
46742
46776
|
const p = y * width + x;
|
|
46743
|
-
if (
|
|
46777
|
+
if (stamp[p] !== 0) {
|
|
46778
|
+
continue;
|
|
46779
|
+
}
|
|
46780
|
+
if (!across[p] && !(y > 0 && across[p - width]) && !(y + 1 < height && across[p + width])) {
|
|
46744
46781
|
continue;
|
|
46745
46782
|
}
|
|
46746
46783
|
let sum = 0;
|
|
@@ -46749,13 +46786,13 @@
|
|
|
46749
46786
|
for (let dx = -1; dx <= 1; dx++) {
|
|
46750
46787
|
const nx = x + dx;
|
|
46751
46788
|
const ny = y + dy;
|
|
46752
|
-
if (nx < 0 || ny < 0 || nx >= width || ny >= height || !
|
|
46789
|
+
if (nx < 0 || ny < 0 || nx >= width || ny >= height || !had(ny * width + nx)) {
|
|
46753
46790
|
continue;
|
|
46754
46791
|
}
|
|
46755
46792
|
const at = (ny * width + nx) * 4;
|
|
46756
46793
|
// Averaged as a value rather than per byte: averaging a packed low byte
|
|
46757
46794
|
// on its own wraps at every 256th step and speckles the bled edge.
|
|
46758
|
-
sum += packed ?
|
|
46795
|
+
sum += packed ? value[at] * 256 + value[at + 1] : value[at];
|
|
46759
46796
|
hits++;
|
|
46760
46797
|
}
|
|
46761
46798
|
}
|
|
@@ -46771,18 +46808,25 @@
|
|
|
46771
46808
|
value[p * 4 + 1] = v;
|
|
46772
46809
|
value[p * 4 + 2] = v;
|
|
46773
46810
|
}
|
|
46774
|
-
|
|
46811
|
+
stamp[p] = current;
|
|
46775
46812
|
}
|
|
46776
46813
|
}
|
|
46777
46814
|
}
|
|
46778
46815
|
}
|
|
46779
46816
|
}
|
|
46817
|
+
TextureFrameSeriesAnimator.DilateValues = DilateValues;
|
|
46818
|
+
/*
|
|
46819
|
+
* Bytes one cached frame holds, counting a buffer shared between colour and value once.
|
|
46820
|
+
*/
|
|
46821
|
+
function frameBytes(pixels, value) {
|
|
46822
|
+
return (pixels ? pixels.byteLength : 0) + (value && value !== pixels ? value.byteLength : 0);
|
|
46823
|
+
}
|
|
46780
46824
|
const DEFAULT_CROSSFADE_MS = 900;
|
|
46781
46825
|
const DEFAULT_LOW_COLOR = { red: 255, green: 255, blue: 255, alpha: 0.12 };
|
|
46782
46826
|
const DEFAULT_HIGH_COLOR = { red: 21, green: 96, blue: 196, alpha: 0.92 };
|
|
46783
46827
|
class Animator {
|
|
46784
46828
|
constructor(options) {
|
|
46785
|
-
var _a, _b, _c;
|
|
46829
|
+
var _a, _b, _c, _d;
|
|
46786
46830
|
this.removeOnTick = null;
|
|
46787
46831
|
this.removeCrossfadeTick = null;
|
|
46788
46832
|
this.disposed = false;
|
|
@@ -46813,6 +46857,15 @@
|
|
|
46813
46857
|
this.extremesLoad = null;
|
|
46814
46858
|
this.floorPixels = null;
|
|
46815
46859
|
this.ceilingPixels = null;
|
|
46860
|
+
// How far through the fade from the previous value frame to the presented one, 1 when settled.
|
|
46861
|
+
this.fadeMix = 1;
|
|
46862
|
+
// Where the current fade started from, above zero when it took over from one still running.
|
|
46863
|
+
this.fadeMixStart = 0;
|
|
46864
|
+
// Version the value canvas was last written at, since in value only mode it is written on demand.
|
|
46865
|
+
this.valueCanvasVersion = -1;
|
|
46866
|
+
this.rawBytes = 0;
|
|
46867
|
+
this.cachedBytes = 0;
|
|
46868
|
+
this.lastFrameBytes = 0;
|
|
46816
46869
|
if (!options.entity.polygon) {
|
|
46817
46870
|
throw new Error("TextureFrameSeriesAnimator requires an entity with polygon graphics.");
|
|
46818
46871
|
}
|
|
@@ -46860,6 +46913,9 @@
|
|
|
46860
46913
|
this.maskBaseline = (_c = options.maskBaseline) !== null && _c !== void 0 ? _c : this.tiles.some((tile) => Boolean(tile.BaselineMask));
|
|
46861
46914
|
this.valueCanvas = this.produceValueCanvas ? document.createElement("canvas") : null;
|
|
46862
46915
|
this.driveMaterial = options.driveMaterial !== false;
|
|
46916
|
+
this.valueOnly = this.produceValueCanvas && !this.driveMaterial;
|
|
46917
|
+
this.rawCache = new Array(this.frames.length).fill(null);
|
|
46918
|
+
this.cacheBudget = (_d = options.cacheBytes) !== null && _d !== void 0 ? _d : MAX_CACHED_FRAME_BYTES;
|
|
46863
46919
|
this.originalMaterial = options.entity.polygon.material;
|
|
46864
46920
|
this.imageProperty = new Cesium.CallbackProperty(() => this.pool[this.poolIdx], false);
|
|
46865
46921
|
if (this.driveMaterial) {
|
|
@@ -46925,7 +46981,40 @@
|
|
|
46925
46981
|
* or null unless the instance was constructed with produceValueCanvas.
|
|
46926
46982
|
*/
|
|
46927
46983
|
GetValueCanvas() {
|
|
46928
|
-
|
|
46984
|
+
if (!this.valueDims) {
|
|
46985
|
+
return null;
|
|
46986
|
+
}
|
|
46987
|
+
if (this.valueOnly && this.valueCanvasVersion !== this.presentVersion && this.displayedValuePixels) {
|
|
46988
|
+
this.writeInto(this.valueCanvas, this.displayedValuePixels, this.valueDims);
|
|
46989
|
+
this.valueCanvasVersion = this.presentVersion;
|
|
46990
|
+
}
|
|
46991
|
+
return this.valueCanvas;
|
|
46992
|
+
}
|
|
46993
|
+
/**
|
|
46994
|
+
* The presented frame's value pixels, or null before one. Shares the cached array, so never write to it.
|
|
46995
|
+
*/
|
|
46996
|
+
GetValueRaster() {
|
|
46997
|
+
if (!this.valueDims || !this.displayedValuePixels) {
|
|
46998
|
+
return null;
|
|
46999
|
+
}
|
|
47000
|
+
return { data: this.displayedValuePixels, width: this.valueDims.width, height: this.valueDims.height };
|
|
47001
|
+
}
|
|
47002
|
+
/**
|
|
47003
|
+
* The two value frames being faded between and the mix, or null unless the material is not driven.
|
|
47004
|
+
*/
|
|
47005
|
+
GetValueFade() {
|
|
47006
|
+
if (!this.valueOnly || !this.valueDims || !this.displayedValuePixels) {
|
|
47007
|
+
return null;
|
|
47008
|
+
}
|
|
47009
|
+
const previous = this.fadeFromValuePixels;
|
|
47010
|
+
const from = previous && previous.length === this.displayedValuePixels.length ? previous : null;
|
|
47011
|
+
return {
|
|
47012
|
+
from,
|
|
47013
|
+
to: this.displayedValuePixels,
|
|
47014
|
+
width: this.valueDims.width,
|
|
47015
|
+
height: this.valueDims.height,
|
|
47016
|
+
mix: from ? this.fadeMix : 1
|
|
47017
|
+
};
|
|
46929
47018
|
}
|
|
46930
47019
|
/**
|
|
46931
47020
|
* Increments every time a frame is presented, so a consumer holding the value canvas can tell
|
|
@@ -46992,7 +47081,11 @@
|
|
|
46992
47081
|
if (this.currentFrameIndex < 0) {
|
|
46993
47082
|
return this.frameCache[0] ? -1 : 0;
|
|
46994
47083
|
}
|
|
46995
|
-
|
|
47084
|
+
// Full once another frame will not fit, so a prefetch only displaces a frame further from the
|
|
47085
|
+
// playhead than itself and never trades the two ends of the window back and forth.
|
|
47086
|
+
const full = this.cachedBytes + this.lastFrameBytes > this.cacheBudget;
|
|
47087
|
+
const reach = full ? this.farthestCachedDistance() : n;
|
|
47088
|
+
for (let d = 0; d < Math.min(n, reach); d++) {
|
|
46996
47089
|
const forward = this.currentFrameIndex + d;
|
|
46997
47090
|
if (forward < n && !this.frameCache[forward]) {
|
|
46998
47091
|
return forward;
|
|
@@ -47004,6 +47097,68 @@
|
|
|
47004
47097
|
}
|
|
47005
47098
|
return -1;
|
|
47006
47099
|
}
|
|
47100
|
+
farthestCachedDistance() {
|
|
47101
|
+
let farthest = 0;
|
|
47102
|
+
for (let i = 0; i < this.frameCache.length; i++) {
|
|
47103
|
+
if (this.frameCache[i]) {
|
|
47104
|
+
farthest = Math.max(farthest, Math.abs(i - this.currentFrameIndex));
|
|
47105
|
+
}
|
|
47106
|
+
}
|
|
47107
|
+
return farthest;
|
|
47108
|
+
}
|
|
47109
|
+
/*
|
|
47110
|
+
* Drops decoded frames furthest from the playhead until the cache fits its budget again.
|
|
47111
|
+
*/
|
|
47112
|
+
evictDecoded(keep) {
|
|
47113
|
+
// A tinted fade reads its target from the cache each tick, where a value fade holds the arrays.
|
|
47114
|
+
const fading = !this.valueOnly && this.fadeStartMs !== null ? this.fadeToIndex : -1;
|
|
47115
|
+
while (this.cachedBytes > this.cacheBudget) {
|
|
47116
|
+
let victim = -1;
|
|
47117
|
+
let distance = -1;
|
|
47118
|
+
for (let i = 0; i < this.frameCache.length; i++) {
|
|
47119
|
+
if (!this.frameCache[i] || i === keep || i === this.currentFrameIndex || i === fading) {
|
|
47120
|
+
continue;
|
|
47121
|
+
}
|
|
47122
|
+
const d = Math.abs(i - this.currentFrameIndex);
|
|
47123
|
+
if (d > distance) {
|
|
47124
|
+
distance = d;
|
|
47125
|
+
victim = i;
|
|
47126
|
+
}
|
|
47127
|
+
}
|
|
47128
|
+
if (victim < 0) {
|
|
47129
|
+
return;
|
|
47130
|
+
}
|
|
47131
|
+
this.cachedBytes -= frameBytes(this.frameCache[victim], this.valueCache[victim]);
|
|
47132
|
+
this.frameCache[victim] = null;
|
|
47133
|
+
this.valueCache[victim] = null;
|
|
47134
|
+
}
|
|
47135
|
+
}
|
|
47136
|
+
/*
|
|
47137
|
+
* Keeps a frame's fetched bytes unless they are large, as a tile major archive's are.
|
|
47138
|
+
*/
|
|
47139
|
+
keepRaw(idx, buffer, base) {
|
|
47140
|
+
if (buffer.byteLength > MAX_RAW_FRAME_BYTES / 16) {
|
|
47141
|
+
return;
|
|
47142
|
+
}
|
|
47143
|
+
this.rawCache[idx] = { buffer, base };
|
|
47144
|
+
this.rawBytes += buffer.byteLength;
|
|
47145
|
+
while (this.rawBytes > MAX_RAW_FRAME_BYTES) {
|
|
47146
|
+
let victim = -1;
|
|
47147
|
+
let distance = -1;
|
|
47148
|
+
for (let i = 0; i < this.rawCache.length; i++) {
|
|
47149
|
+
const d = Math.abs(i - this.currentFrameIndex);
|
|
47150
|
+
if (this.rawCache[i] && i !== idx && d > distance) {
|
|
47151
|
+
distance = d;
|
|
47152
|
+
victim = i;
|
|
47153
|
+
}
|
|
47154
|
+
}
|
|
47155
|
+
if (victim < 0) {
|
|
47156
|
+
return;
|
|
47157
|
+
}
|
|
47158
|
+
this.rawBytes -= this.rawCache[victim].buffer.byteLength;
|
|
47159
|
+
this.rawCache[victim] = null;
|
|
47160
|
+
}
|
|
47161
|
+
}
|
|
47007
47162
|
pumpFetchQueue() {
|
|
47008
47163
|
if (this.disposed || this.inFlightIndex !== -1) {
|
|
47009
47164
|
return;
|
|
@@ -47047,10 +47202,18 @@
|
|
|
47047
47202
|
lo = Math.min(lo, at.ByteOffset);
|
|
47048
47203
|
hi = Math.max(hi, at.ByteOffset + at.ByteLength);
|
|
47049
47204
|
}
|
|
47050
|
-
|
|
47051
|
-
const
|
|
47052
|
-
if (
|
|
47053
|
-
|
|
47205
|
+
let buffer;
|
|
47206
|
+
const raw = this.rawCache[idx];
|
|
47207
|
+
if (raw && raw.base === lo) {
|
|
47208
|
+
buffer = raw.buffer;
|
|
47209
|
+
}
|
|
47210
|
+
else {
|
|
47211
|
+
const response = await fetch(this.archiveUrl, { headers: { Range: `bytes=${lo}-${hi - 1}` } });
|
|
47212
|
+
buffer = await response.arrayBuffer();
|
|
47213
|
+
if (this.disposed) {
|
|
47214
|
+
return;
|
|
47215
|
+
}
|
|
47216
|
+
this.keepRaw(idx, buffer, lo);
|
|
47054
47217
|
}
|
|
47055
47218
|
// Awaited before decoding rather than applied later, so no frame is ever cached unmasked and
|
|
47056
47219
|
// then corrected once the mask lands.
|
|
@@ -47062,9 +47225,15 @@
|
|
|
47062
47225
|
if (this.disposed) {
|
|
47063
47226
|
return;
|
|
47064
47227
|
}
|
|
47228
|
+
if (this.frameCache[idx]) {
|
|
47229
|
+
this.cachedBytes -= frameBytes(this.frameCache[idx], this.valueCache[idx]);
|
|
47230
|
+
}
|
|
47065
47231
|
this.frameCache[idx] = decoded.pixels;
|
|
47066
47232
|
this.frameDims[idx] = { width: decoded.width, height: decoded.height };
|
|
47067
47233
|
this.valueCache[idx] = decoded.valuePixels || null;
|
|
47234
|
+
this.lastFrameBytes = frameBytes(decoded.pixels, decoded.valuePixels || null);
|
|
47235
|
+
this.cachedBytes += this.lastFrameBytes;
|
|
47236
|
+
this.evictDecoded(idx);
|
|
47068
47237
|
if (this.shouldPresent(idx, isTarget)) {
|
|
47069
47238
|
this.beginCrossfadeTo(idx);
|
|
47070
47239
|
}
|
|
@@ -47134,6 +47303,13 @@
|
|
|
47134
47303
|
*/
|
|
47135
47304
|
tint(imageData) {
|
|
47136
47305
|
const canvas = { width: imageData.width, height: imageData.height };
|
|
47306
|
+
if (this.valueOnly) {
|
|
47307
|
+
// One array serves as both, since nothing will ever display the colour.
|
|
47308
|
+
const values = imageData.data;
|
|
47309
|
+
this.applyBaselineMask(values, undefined, canvas.width, canvas.height);
|
|
47310
|
+
DilateValues(values, canvas.width, canvas.height, VALUE_DILATE_PASSES, IsPackedValue(this.metadata));
|
|
47311
|
+
return { pixels: values, width: canvas.width, height: canvas.height, valuePixels: values };
|
|
47312
|
+
}
|
|
47137
47313
|
// Copied before tinting: the ramp is a lerp between two colours, so the value cannot be
|
|
47138
47314
|
// recovered from the tinted pixels afterwards.
|
|
47139
47315
|
const valuePixels = this.produceValueCanvas ? new Uint8ClampedArray(imageData.data) : undefined;
|
|
@@ -47154,7 +47330,7 @@
|
|
|
47154
47330
|
}
|
|
47155
47331
|
this.applyBaselineMask(imageData.data, valuePixels, canvas.width, canvas.height);
|
|
47156
47332
|
if (valuePixels) {
|
|
47157
|
-
|
|
47333
|
+
DilateValues(valuePixels, canvas.width, canvas.height, VALUE_DILATE_PASSES, IsPackedValue(this.metadata));
|
|
47158
47334
|
}
|
|
47159
47335
|
return { pixels: imageData.data, width: canvas.width, height: canvas.height, valuePixels };
|
|
47160
47336
|
}
|
|
@@ -47412,6 +47588,10 @@
|
|
|
47412
47588
|
}
|
|
47413
47589
|
}
|
|
47414
47590
|
beginCrossfadeTo(idx) {
|
|
47591
|
+
if (this.valueOnly) {
|
|
47592
|
+
this.beginValueFadeTo(idx);
|
|
47593
|
+
return;
|
|
47594
|
+
}
|
|
47415
47595
|
const to = this.frameCache[idx];
|
|
47416
47596
|
this.fadeFromValuePixels = this.displayedValuePixels === null && this.valueCache[idx]
|
|
47417
47597
|
? new Uint8ClampedArray(this.valueCache[idx].length)
|
|
@@ -47421,12 +47601,53 @@
|
|
|
47421
47601
|
this.fadeToDims = this.frameDims[idx];
|
|
47422
47602
|
this.fadeStartMs = Now();
|
|
47423
47603
|
}
|
|
47604
|
+
/*
|
|
47605
|
+
* Presents a value frame at once and leaves the fade to the consumer, which blends on the GPU.
|
|
47606
|
+
*/
|
|
47607
|
+
beginValueFadeTo(idx) {
|
|
47608
|
+
const to = this.valueCache[idx];
|
|
47609
|
+
if (!to || to === this.displayedValuePixels) {
|
|
47610
|
+
return;
|
|
47611
|
+
}
|
|
47612
|
+
// A new target mid fade keeps whichever frame is mostly on screen and carries on from the current
|
|
47613
|
+
// mix, so frames landing in quick succession do not jump the sheet back.
|
|
47614
|
+
const midFade = this.fadeStartMs !== null && Boolean(this.fadeFromValuePixels);
|
|
47615
|
+
if (midFade && this.fadeMix < 0.5) {
|
|
47616
|
+
this.fadeMixStart = this.fadeMix;
|
|
47617
|
+
}
|
|
47618
|
+
else {
|
|
47619
|
+
this.fadeFromValuePixels = this.displayedValuePixels;
|
|
47620
|
+
this.fadeMixStart = 0;
|
|
47621
|
+
}
|
|
47622
|
+
this.displayedValuePixels = to;
|
|
47623
|
+
this.valueDims = this.frameDims[idx];
|
|
47624
|
+
this.fadeToIndex = idx;
|
|
47625
|
+
this.fadeMix = this.fadeFromValuePixels ? this.fadeMixStart : 1;
|
|
47626
|
+
this.fadeStartMs = this.fadeFromValuePixels ? Now() : null;
|
|
47627
|
+
this.presentVersion++;
|
|
47628
|
+
this.requestRender();
|
|
47629
|
+
}
|
|
47630
|
+
requestRender() {
|
|
47631
|
+
if (this.viewer && this.viewer.scene && this.viewer.scene.requestRenderMode) {
|
|
47632
|
+
this.viewer.scene.requestRender();
|
|
47633
|
+
}
|
|
47634
|
+
}
|
|
47424
47635
|
tickCrossfade() {
|
|
47425
47636
|
if (this.disposed || this.fadeStartMs === null) {
|
|
47426
47637
|
return;
|
|
47427
47638
|
}
|
|
47428
47639
|
const t = Math.min(1, (Now() - this.fadeStartMs) / this.crossfadeMs);
|
|
47429
47640
|
const eased = easeInOutQuad(t);
|
|
47641
|
+
if (this.valueOnly) {
|
|
47642
|
+
this.fadeMix = this.fadeMixStart + (1 - this.fadeMixStart) * eased;
|
|
47643
|
+
if (t >= 1) {
|
|
47644
|
+
this.fadeStartMs = null;
|
|
47645
|
+
this.fadeFromValuePixels = null;
|
|
47646
|
+
this.fadeMix = 1;
|
|
47647
|
+
}
|
|
47648
|
+
this.requestRender();
|
|
47649
|
+
return;
|
|
47650
|
+
}
|
|
47430
47651
|
const from = this.fadeFromPixels;
|
|
47431
47652
|
const to = this.frameCache[this.fadeToIndex];
|
|
47432
47653
|
const n = Math.min(from.length, to.length);
|
|
@@ -47466,9 +47687,7 @@
|
|
|
47466
47687
|
this.valueDims = dims;
|
|
47467
47688
|
}
|
|
47468
47689
|
this.presentVersion++;
|
|
47469
|
-
|
|
47470
|
-
this.viewer.scene.requestRender();
|
|
47471
|
-
}
|
|
47690
|
+
this.requestRender();
|
|
47472
47691
|
}
|
|
47473
47692
|
}
|
|
47474
47693
|
TextureFrameSeriesAnimator.Animator = Animator;
|
|
@@ -47487,13 +47706,15 @@
|
|
|
47487
47706
|
const MIN_VISIBLE_PIXELS = 60;
|
|
47488
47707
|
const MAX_STRIDE = 512;
|
|
47489
47708
|
const STRIDE_HYSTERESIS = 1.4;
|
|
47490
|
-
|
|
47491
|
-
const
|
|
47492
|
-
const
|
|
47493
|
-
|
|
47494
|
-
|
|
47495
|
-
|
|
47496
|
-
const
|
|
47709
|
+
// Scene labels carry a leader and are wider than a value, so they want room and not many of them.
|
|
47710
|
+
const DEFAULT_MIN_SPACING_PIXELS = 140;
|
|
47711
|
+
const DEFAULT_MAX_LABELS = 60;
|
|
47712
|
+
// How far past the edge of the view an anchor may sit before its label is dropped.
|
|
47713
|
+
const LABEL_EDGE_MARGIN_PIXELS = 120;
|
|
47714
|
+
// Distance discount for a label already up when the cap forces a choice.
|
|
47715
|
+
const LABEL_KEEP_BIAS = 0.6;
|
|
47716
|
+
// How long a label outlives its cell leaving the wanted set, so one back a frame later does not blink.
|
|
47717
|
+
const LABEL_LINGER_MS = 300;
|
|
47497
47718
|
// 8 rather than 5: below this a cell reads as texture rather than as a boundary, and it also
|
|
47498
47719
|
// bounds the worst case, since a full screen of 8 px cells fits inside MAX_GRID_CELLS.
|
|
47499
47720
|
const DEFAULT_MIN_GRID_CELL_PIXELS = 8;
|
|
@@ -47507,7 +47728,8 @@
|
|
|
47507
47728
|
const TILE_OUTLINE_ALPHA = 0.45;
|
|
47508
47729
|
// Cells per grid square at the coarsest. Beyond this a tile is outlined instead.
|
|
47509
47730
|
const MAX_GRID_STRIDE = 256;
|
|
47510
|
-
|
|
47731
|
+
// Times the pointer's ground hit is moved onto the sheet's own height before reading a cell.
|
|
47732
|
+
const HOVER_REFINE_STEPS = 3;
|
|
47511
47733
|
/**
|
|
47512
47734
|
* A clip outline from a projected ring, or null when the ring cannot be drawn.
|
|
47513
47735
|
* @param projected one entry per ring vertex, null where the vertex has no window coordinate
|
|
@@ -47601,11 +47823,25 @@
|
|
|
47601
47823
|
this.sourceMap = null;
|
|
47602
47824
|
this.source = null;
|
|
47603
47825
|
this.painted = null;
|
|
47826
|
+
this.paintedRamp = null;
|
|
47604
47827
|
this.floor = null;
|
|
47605
47828
|
this.ceiling = null;
|
|
47606
47829
|
this.disposed = false;
|
|
47607
|
-
this.drawn = 0;
|
|
47608
47830
|
this.stride = 0;
|
|
47831
|
+
this.cellLabels = new Map();
|
|
47832
|
+
this.lingerTimer = null;
|
|
47833
|
+
// What the last draw was made from, so a frame that changes none of it draws nothing.
|
|
47834
|
+
this.drawnView = null;
|
|
47835
|
+
this.drawnInputs = [];
|
|
47836
|
+
this.scratchWorld = new Cesium.Cartesian3();
|
|
47837
|
+
this.scratchWindow = new Cesium.Cartesian2();
|
|
47838
|
+
this.scratchClip = new Cesium.Cartesian4();
|
|
47839
|
+
// The camera's view and projection as one matrix, rebuilt per draw, or null outside 3D.
|
|
47840
|
+
this.viewProjection = null;
|
|
47841
|
+
this.viewProjectionScratch = new Cesium.Matrix4();
|
|
47842
|
+
this.hovering = false;
|
|
47843
|
+
this.hoverFrame = 0;
|
|
47844
|
+
this.hoverAt = null;
|
|
47609
47845
|
this.viewer = options.viewer;
|
|
47610
47846
|
this.extent = options.extent;
|
|
47611
47847
|
this.settings = options.settings || {};
|
|
@@ -47615,6 +47851,9 @@
|
|
|
47615
47851
|
this.sourceMap = exports.TextureFrameSeriesAnimator.SourceMap(options.metadata);
|
|
47616
47852
|
this.tiles = options.tiles;
|
|
47617
47853
|
this.clipRing = options.clipRing;
|
|
47854
|
+
this.heightAt = options.heightAt;
|
|
47855
|
+
this.heightKey = options.heightKey;
|
|
47856
|
+
this.hover = options.hover;
|
|
47618
47857
|
this.canvas = document.createElement("canvas");
|
|
47619
47858
|
this.canvas.style.position = "absolute";
|
|
47620
47859
|
this.canvas.style.inset = "0";
|
|
@@ -47624,12 +47863,31 @@
|
|
|
47624
47863
|
parent.appendChild(this.canvas);
|
|
47625
47864
|
}
|
|
47626
47865
|
this.ctx = this.canvas.getContext("2d");
|
|
47866
|
+
this.onPointerMove = (event) => {
|
|
47867
|
+
const bounds = this.viewer.canvas.getBoundingClientRect();
|
|
47868
|
+
this.hoverAt = { x: event.clientX - bounds.left, y: event.clientY - bounds.top };
|
|
47869
|
+
// At most once a frame, since a pointer reports far faster than anything is drawn.
|
|
47870
|
+
if (!this.hoverFrame) {
|
|
47871
|
+
this.hoverFrame = requestAnimationFrame(() => {
|
|
47872
|
+
this.hoverFrame = 0;
|
|
47873
|
+
this.updateHover();
|
|
47874
|
+
});
|
|
47875
|
+
}
|
|
47876
|
+
};
|
|
47877
|
+
this.onPointerLeave = () => {
|
|
47878
|
+
this.hoverAt = null;
|
|
47879
|
+
this.releaseHover();
|
|
47880
|
+
};
|
|
47881
|
+
if (this.hover && this.hover.register) {
|
|
47882
|
+
this.viewer.canvas.addEventListener("pointermove", this.onPointerMove, { passive: true });
|
|
47883
|
+
this.viewer.canvas.addEventListener("pointerleave", this.onPointerLeave);
|
|
47884
|
+
}
|
|
47627
47885
|
}
|
|
47628
47886
|
SetExtent(extent) {
|
|
47629
47887
|
this.extent = extent;
|
|
47630
47888
|
}
|
|
47631
47889
|
/**
|
|
47632
|
-
* The current frame's decoded values
|
|
47890
|
+
* The current frame's decoded values.
|
|
47633
47891
|
* @param source value pixels, one texel per cell
|
|
47634
47892
|
*/
|
|
47635
47893
|
SetSource(source) {
|
|
@@ -47637,11 +47895,17 @@
|
|
|
47637
47895
|
}
|
|
47638
47896
|
/**
|
|
47639
47897
|
* The tinted pixels of the same frame, so the grid can skip cells the ramp painted nothing in.
|
|
47640
|
-
* @param painted RGBA of the presented frame, or null to fall back to coverage alone
|
|
47898
|
+
* @param painted RGBA of the presented frame, or null to fall back to the ramp or coverage alone
|
|
47641
47899
|
*/
|
|
47642
47900
|
SetPainted(painted) {
|
|
47643
47901
|
this.painted = painted;
|
|
47644
47902
|
}
|
|
47903
|
+
/**
|
|
47904
|
+
* The surface's 256 step RGBA ramp, for telling whether a cell was painted without a tinted frame.
|
|
47905
|
+
*/
|
|
47906
|
+
SetPaintedRamp(ramp) {
|
|
47907
|
+
this.paintedRamp = ramp;
|
|
47908
|
+
}
|
|
47645
47909
|
/**
|
|
47646
47910
|
* The archive's per-cell extremes, when it publishes them. Without these a label can still
|
|
47647
47911
|
* print the current reading, just not the range behind it.
|
|
@@ -47651,21 +47915,28 @@
|
|
|
47651
47915
|
this.ceiling = ceiling;
|
|
47652
47916
|
}
|
|
47653
47917
|
GetDrawnCount() {
|
|
47654
|
-
return this.
|
|
47918
|
+
return this.cellLabels.size;
|
|
47655
47919
|
}
|
|
47656
47920
|
Clear() {
|
|
47657
47921
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
47658
|
-
this.
|
|
47922
|
+
this.drawnView = null;
|
|
47659
47923
|
}
|
|
47660
47924
|
Dispose() {
|
|
47661
47925
|
this.disposed = true;
|
|
47926
|
+
this.clearCellLabels();
|
|
47927
|
+
this.releaseHover();
|
|
47928
|
+
if (this.hoverFrame) {
|
|
47929
|
+
cancelAnimationFrame(this.hoverFrame);
|
|
47930
|
+
this.hoverFrame = 0;
|
|
47931
|
+
}
|
|
47932
|
+
this.viewer.canvas.removeEventListener("pointermove", this.onPointerMove);
|
|
47933
|
+
this.viewer.canvas.removeEventListener("pointerleave", this.onPointerLeave);
|
|
47662
47934
|
if (this.canvas.parentElement) {
|
|
47663
47935
|
this.canvas.parentElement.removeChild(this.canvas);
|
|
47664
47936
|
}
|
|
47665
47937
|
}
|
|
47666
47938
|
/**
|
|
47667
|
-
* Redraws
|
|
47668
|
-
* count it considers is bounded by the view rather than by the texture.
|
|
47939
|
+
* Redraws the grid and syncs the value labels, when anything they are drawn from has changed.
|
|
47669
47940
|
*/
|
|
47670
47941
|
Render() {
|
|
47671
47942
|
var _a, _b, _c;
|
|
@@ -47673,67 +47944,296 @@
|
|
|
47673
47944
|
return;
|
|
47674
47945
|
}
|
|
47675
47946
|
const view = this.viewer.canvas;
|
|
47947
|
+
const inputs = [this.source, this.painted, this.paintedRamp, this.floor, this.ceiling,
|
|
47948
|
+
view.clientWidth, view.clientHeight, this.heightKey ? this.heightKey() : null];
|
|
47949
|
+
const camera = this.viewer.camera.viewMatrix;
|
|
47950
|
+
if (this.drawnView &&
|
|
47951
|
+
Cesium.Matrix4.equals(this.drawnView, camera) &&
|
|
47952
|
+
inputs.every((input, i) => input === this.drawnInputs[i])) {
|
|
47953
|
+
return;
|
|
47954
|
+
}
|
|
47676
47955
|
if (this.canvas.width !== view.clientWidth || this.canvas.height !== view.clientHeight) {
|
|
47677
47956
|
this.canvas.width = view.clientWidth;
|
|
47678
47957
|
this.canvas.height = view.clientHeight;
|
|
47679
47958
|
}
|
|
47680
47959
|
this.Clear();
|
|
47960
|
+
this.drawnView = Cesium.Matrix4.clone(camera, this.drawnView || undefined);
|
|
47961
|
+
this.drawnInputs = inputs;
|
|
47962
|
+
const projection = this.viewer.camera.frustum.projectionMatrix;
|
|
47963
|
+
this.viewProjection = this.viewer.scene.mode === Cesium.SceneMode.SCENE3D
|
|
47964
|
+
? Cesium.Matrix4.multiply(projection, camera, this.viewProjectionScratch)
|
|
47965
|
+
: null;
|
|
47681
47966
|
const cols = this.source.width;
|
|
47682
47967
|
const rows = this.source.height;
|
|
47683
47968
|
const window = this.visibleTexels(cols, rows);
|
|
47684
47969
|
if (!window) {
|
|
47970
|
+
this.syncCellLabels([]);
|
|
47685
47971
|
return;
|
|
47686
47972
|
}
|
|
47687
|
-
|
|
47688
|
-
|
|
47689
|
-
const
|
|
47690
|
-
const
|
|
47973
|
+
// Measured where the view is looking, since in an oblique view up close the middle of the visible
|
|
47974
|
+
// texels sits out toward the horizon, where cells are a fraction of the size.
|
|
47975
|
+
const focus = this.groundAtScreenCentre();
|
|
47976
|
+
const at = focus ? this.texelAt(focus.lon, focus.lat) : null;
|
|
47977
|
+
const focused = (Boolean(at) &&
|
|
47978
|
+
at.col >= window.col0 &&
|
|
47979
|
+
at.col <= window.col1 &&
|
|
47980
|
+
at.row >= window.row0 &&
|
|
47981
|
+
at.row <= window.row1);
|
|
47982
|
+
const midCol = focused ? at.col : (window.col0 + window.col1) / 2;
|
|
47983
|
+
const midRow = focused ? at.row : (window.row0 + window.row1) / 2;
|
|
47984
|
+
const a = this.projectCell(midCol, midRow, cols, rows);
|
|
47985
|
+
const b = this.projectCell(midCol + 1, midRow + 1, cols, rows);
|
|
47691
47986
|
if (!a || !b) {
|
|
47987
|
+
// Left as they are, since one frame where the focus will not project is not the data leaving.
|
|
47692
47988
|
return;
|
|
47693
47989
|
}
|
|
47694
47990
|
const cellPixels = Math.min(Math.abs(b.x - a.x), Math.abs(b.y - a.y));
|
|
47695
47991
|
// Ahead of the label threshold on purpose: a grid still reads at a cell size where a
|
|
47696
47992
|
// number no longer fits.
|
|
47697
47993
|
this.drawGrid(window, cols, rows, cellPixels);
|
|
47698
|
-
|
|
47994
|
+
// By area rather than the shorter side, which an oblique view foreshortens to a fraction of the other.
|
|
47995
|
+
const across = this.projectCell(midCol + 1, midRow, cols, rows);
|
|
47996
|
+
const down = this.projectCell(midCol, midRow + 1, cols, rows);
|
|
47997
|
+
const cellSize = across && down
|
|
47998
|
+
? Math.sqrt(Math.abs((across.x - a.x) * (down.y - a.y) - (across.y - a.y) * (down.x - a.x)))
|
|
47999
|
+
: cellPixels;
|
|
48000
|
+
const visibleWidth = cellSize * (window.col1 - window.col0 + 1);
|
|
48001
|
+
const visibleHeight = cellSize * (window.row1 - window.row0 + 1);
|
|
48002
|
+
if (cellSize < ((_a = this.settings.minCellPixels) !== null && _a !== void 0 ? _a : DEFAULT_MIN_CELL_PIXELS) ||
|
|
48003
|
+
Math.max(visibleWidth, visibleHeight) < MIN_VISIBLE_PIXELS) {
|
|
48004
|
+
this.syncCellLabels([]);
|
|
47699
48005
|
return;
|
|
47700
48006
|
}
|
|
47701
|
-
const visibleWidth = cellPixels * (window.col1 - window.col0 + 1);
|
|
47702
|
-
const visibleHeight = cellPixels * (window.row1 - window.row0 + 1);
|
|
47703
|
-
if (Math.max(visibleWidth, visibleHeight) < MIN_VISIBLE_PIXELS) {
|
|
47704
|
-
return;
|
|
47705
|
-
}
|
|
47706
|
-
const parts = this.settings.parts || ["value", "floor", "ceiling"];
|
|
47707
|
-
this.ctx.font = LABEL_FONT;
|
|
47708
|
-
this.ctx.textAlign = "center";
|
|
47709
48007
|
const maxLabels = (_b = this.settings.maxLabels) !== null && _b !== void 0 ? _b : DEFAULT_MAX_LABELS;
|
|
47710
48008
|
this.stride = ChooseLabelStride({
|
|
47711
|
-
cellPixels,
|
|
48009
|
+
cellPixels: cellSize,
|
|
47712
48010
|
visibleCols: window.col1 - window.col0 + 1,
|
|
47713
48011
|
visibleRows: window.row1 - window.row0 + 1,
|
|
47714
48012
|
maxLabels,
|
|
47715
|
-
|
|
47716
|
-
// one is, so changing what a label says cannot quietly start overlapping them.
|
|
47717
|
-
minSpacingPixels: Math.max((_c = this.settings.minSpacingPixels) !== null && _c !== void 0 ? _c : DEFAULT_MIN_SPACING_PIXELS, this.widestLabel(parts)),
|
|
48013
|
+
minSpacingPixels: (_c = this.settings.minSpacingPixels) !== null && _c !== void 0 ? _c : DEFAULT_MIN_SPACING_PIXELS,
|
|
47718
48014
|
held: this.stride
|
|
47719
48015
|
});
|
|
47720
|
-
const
|
|
48016
|
+
const parts = this.settings.parts || ["value", "floor", "ceiling"];
|
|
48017
|
+
this.syncCellLabels(this.labelCells(window, cols, rows, this.stride, maxLabels, parts));
|
|
48018
|
+
}
|
|
48019
|
+
/*
|
|
48020
|
+
* One labelled cell per lattice block, keyed by the block's origin so it survives frame and stride changes.
|
|
48021
|
+
*/
|
|
48022
|
+
labelCells(window, cols, rows, stride, max, parts) {
|
|
48023
|
+
const width = this.canvas.width;
|
|
48024
|
+
const height = this.canvas.height;
|
|
47721
48025
|
const firstRow = Math.floor(window.row0 / stride) * stride;
|
|
47722
48026
|
const firstCol = Math.floor(window.col0 / stride) * stride;
|
|
47723
|
-
|
|
47724
|
-
|
|
48027
|
+
const candidates = [];
|
|
48028
|
+
for (let row = firstRow; row <= window.row1; row += stride) {
|
|
48029
|
+
for (let col = firstCol; col <= window.col1; col += stride) {
|
|
47725
48030
|
const found = this.readNear(col, row, cols, rows, stride);
|
|
47726
48031
|
if (!found) {
|
|
47727
48032
|
continue;
|
|
47728
48033
|
}
|
|
47729
|
-
const point = this.
|
|
47730
|
-
|
|
47731
|
-
|
|
48034
|
+
const point = this.projectCell(found.col, found.row, cols, rows);
|
|
48035
|
+
// A margin past the edge, so a label does not drop the moment its anchor does.
|
|
48036
|
+
const margin = LABEL_EDGE_MARGIN_PIXELS;
|
|
48037
|
+
if (!point ||
|
|
48038
|
+
point.x < -margin ||
|
|
48039
|
+
point.y < -margin ||
|
|
48040
|
+
point.x > width + margin ||
|
|
48041
|
+
point.y > height + margin) {
|
|
47732
48042
|
continue;
|
|
47733
48043
|
}
|
|
47734
|
-
this.
|
|
48044
|
+
const text = this.labelLines(found.reading, parts).join("\n");
|
|
48045
|
+
if (!text) {
|
|
48046
|
+
continue;
|
|
48047
|
+
}
|
|
48048
|
+
const key = col + "," + row;
|
|
48049
|
+
const distance = Math.hypot(point.x - width / 2, point.y - height / 2);
|
|
48050
|
+
candidates.push({
|
|
48051
|
+
key,
|
|
48052
|
+
lon: this.lonAt(found.col, cols),
|
|
48053
|
+
lat: this.latAt(found.row, rows),
|
|
48054
|
+
text,
|
|
48055
|
+
// Labels already up outrank new ones, so the capped set does not shuffle as the view pans.
|
|
48056
|
+
rank: this.cellLabels.has(key) ? distance * LABEL_KEEP_BIAS : distance
|
|
48057
|
+
});
|
|
48058
|
+
}
|
|
48059
|
+
}
|
|
48060
|
+
if (candidates.length > max) {
|
|
48061
|
+
candidates.sort((x, y) => x.rank - y.rank);
|
|
48062
|
+
candidates.length = max;
|
|
48063
|
+
}
|
|
48064
|
+
return candidates;
|
|
48065
|
+
}
|
|
48066
|
+
/*
|
|
48067
|
+
* Keeps one scene label per wanted cell, reusing those that are still wanted.
|
|
48068
|
+
*/
|
|
48069
|
+
syncCellLabels(wanted) {
|
|
48070
|
+
var _a;
|
|
48071
|
+
const keep = new Set(wanted.map((w) => w.key));
|
|
48072
|
+
const now = Date.now();
|
|
48073
|
+
let lingering = false;
|
|
48074
|
+
for (const [key, entry] of this.cellLabels) {
|
|
48075
|
+
if (keep.has(key)) {
|
|
48076
|
+
entry.missingSince = undefined;
|
|
48077
|
+
continue;
|
|
48078
|
+
}
|
|
48079
|
+
entry.missingSince = (_a = entry.missingSince) !== null && _a !== void 0 ? _a : now;
|
|
48080
|
+
if (now - entry.missingSince >= LABEL_LINGER_MS) {
|
|
48081
|
+
entry.label.Dispose();
|
|
48082
|
+
this.cellLabels.delete(key);
|
|
48083
|
+
}
|
|
48084
|
+
else {
|
|
48085
|
+
lingering = true;
|
|
48086
|
+
}
|
|
48087
|
+
}
|
|
48088
|
+
// A still camera draws nothing more, so a lingering label is given its own later look.
|
|
48089
|
+
if (lingering && !this.lingerTimer) {
|
|
48090
|
+
this.lingerTimer = setTimeout(() => {
|
|
48091
|
+
this.lingerTimer = null;
|
|
48092
|
+
if (!this.disposed) {
|
|
48093
|
+
this.drawnView = null;
|
|
48094
|
+
this.viewer.scene.requestRender();
|
|
48095
|
+
}
|
|
48096
|
+
}, LABEL_LINGER_MS);
|
|
48097
|
+
}
|
|
48098
|
+
for (const want of wanted) {
|
|
48099
|
+
const pos3d = Cesium.Cartesian3.fromDegrees(want.lon, want.lat, this.surfaceHeight(want.lon, want.lat));
|
|
48100
|
+
const options = {
|
|
48101
|
+
presentation: "text",
|
|
48102
|
+
text: want.text,
|
|
48103
|
+
leader: true,
|
|
48104
|
+
selectOnClick: false
|
|
48105
|
+
};
|
|
48106
|
+
const existing = this.cellLabels.get(want.key);
|
|
48107
|
+
if (existing) {
|
|
48108
|
+
if (!Cesium.Cartesian3.equalsEpsilon(existing.label.pos3d, pos3d, 0, 0.01)) {
|
|
48109
|
+
existing.label.pos3d = pos3d;
|
|
48110
|
+
}
|
|
48111
|
+
if (existing.text !== want.text) {
|
|
48112
|
+
existing.label.Configure(options);
|
|
48113
|
+
existing.text = want.text;
|
|
48114
|
+
}
|
|
48115
|
+
continue;
|
|
48116
|
+
}
|
|
48117
|
+
const id = (this.hover ? this.hover.entityId : "texture-value") + "#cell:" + want.key;
|
|
48118
|
+
const label = new exports.EntityLabel.Label({
|
|
48119
|
+
rego: {
|
|
48120
|
+
entityId: id,
|
|
48121
|
+
menuItemId: this.hover ? this.hover.menuItemId : undefined,
|
|
48122
|
+
name: want.text,
|
|
48123
|
+
priority: 0,
|
|
48124
|
+
// No graphic, since a point or billboard visual pulls a label back to itself on rebuild.
|
|
48125
|
+
visual: new Cesium.Entity({ position: pos3d })
|
|
48126
|
+
},
|
|
48127
|
+
viewer: this.viewer,
|
|
48128
|
+
pos3d,
|
|
48129
|
+
options,
|
|
48130
|
+
persistent: true,
|
|
48131
|
+
visible: () => !this.disposed
|
|
48132
|
+
});
|
|
48133
|
+
this.cellLabels.set(want.key, { label, text: want.text });
|
|
48134
|
+
}
|
|
48135
|
+
}
|
|
48136
|
+
clearCellLabels() {
|
|
48137
|
+
if (this.lingerTimer) {
|
|
48138
|
+
clearTimeout(this.lingerTimer);
|
|
48139
|
+
this.lingerTimer = null;
|
|
48140
|
+
}
|
|
48141
|
+
for (const entry of this.cellLabels.values()) {
|
|
48142
|
+
entry.label.Dispose();
|
|
48143
|
+
}
|
|
48144
|
+
this.cellLabels.clear();
|
|
48145
|
+
}
|
|
48146
|
+
/*
|
|
48147
|
+
* Shows the cell under the pointer through the register's hover label, unless something else holds it.
|
|
48148
|
+
*/
|
|
48149
|
+
updateHover() {
|
|
48150
|
+
const hover = this.hover;
|
|
48151
|
+
if (this.disposed || !hover || !hover.register || !this.source || !this.hoverAt) {
|
|
48152
|
+
return;
|
|
48153
|
+
}
|
|
48154
|
+
const register = hover.register;
|
|
48155
|
+
const target = register.GetLabelHoverTarget();
|
|
48156
|
+
const ours = Boolean(target) && register.IsLabelHovered(hover.entityId, hover.menuItemId);
|
|
48157
|
+
if (target && !ours) {
|
|
48158
|
+
this.hovering = false;
|
|
48159
|
+
return;
|
|
48160
|
+
}
|
|
48161
|
+
const under = this.readingUnder(this.hoverAt.x, this.hoverAt.y);
|
|
48162
|
+
if (!under) {
|
|
48163
|
+
this.releaseHover();
|
|
48164
|
+
return;
|
|
48165
|
+
}
|
|
48166
|
+
register.SetLabelHover({
|
|
48167
|
+
entityId: hover.entityId,
|
|
48168
|
+
menuItemId: hover.menuItemId,
|
|
48169
|
+
position: { x: this.hoverAt.x, y: this.hoverAt.y },
|
|
48170
|
+
text: under.text,
|
|
48171
|
+
pos3d: under.pos3d
|
|
48172
|
+
});
|
|
48173
|
+
this.hovering = true;
|
|
48174
|
+
}
|
|
48175
|
+
releaseHover() {
|
|
48176
|
+
const hover = this.hover;
|
|
48177
|
+
if (!this.hovering || !hover || !hover.register) {
|
|
48178
|
+
return;
|
|
48179
|
+
}
|
|
48180
|
+
this.hovering = false;
|
|
48181
|
+
if (hover.register.IsLabelHovered(hover.entityId, hover.menuItemId)) {
|
|
48182
|
+
hover.register.ClearLabelHover();
|
|
48183
|
+
}
|
|
48184
|
+
}
|
|
48185
|
+
/*
|
|
48186
|
+
* The text for the painted cell under a window position and its centre on the sheet, or null.
|
|
48187
|
+
*/
|
|
48188
|
+
readingUnder(x, y) {
|
|
48189
|
+
const at = this.cellUnder(x, y);
|
|
48190
|
+
if (!at) {
|
|
48191
|
+
return null;
|
|
48192
|
+
}
|
|
48193
|
+
const cols = this.source.width;
|
|
48194
|
+
const found = this.readAt(at.col, at.row, cols);
|
|
48195
|
+
if (!found || !this.paintedAt(at.col, at.row, cols)) {
|
|
48196
|
+
return null;
|
|
48197
|
+
}
|
|
48198
|
+
const text = this.labelLines(found, this.settings.parts || ["value", "floor", "ceiling"]).join("\n");
|
|
48199
|
+
if (!text) {
|
|
48200
|
+
return null;
|
|
48201
|
+
}
|
|
48202
|
+
const lon = this.lonAt(at.col, cols);
|
|
48203
|
+
const lat = this.latAt(at.row, this.source.height);
|
|
48204
|
+
return { text, pos3d: Cesium.Cartesian3.fromDegrees(lon, lat, this.surfaceHeight(lon, lat)) };
|
|
48205
|
+
}
|
|
48206
|
+
/*
|
|
48207
|
+
* The cell under a window position, found on the sheet rather than on the ground beneath it.
|
|
48208
|
+
*/
|
|
48209
|
+
cellUnder(x, y) {
|
|
48210
|
+
const scene = this.viewer.scene;
|
|
48211
|
+
const camera = this.viewer.camera;
|
|
48212
|
+
const screen = new Cesium.Cartesian2(x, y);
|
|
48213
|
+
const ray = camera.getPickRay(screen);
|
|
48214
|
+
if (!ray) {
|
|
48215
|
+
return null;
|
|
48216
|
+
}
|
|
48217
|
+
let hit = (scene.globe && scene.globe.pick(ray, scene)) || camera.pickEllipsoid(screen);
|
|
48218
|
+
if (!hit) {
|
|
48219
|
+
return null;
|
|
48220
|
+
}
|
|
48221
|
+
const radii = Cesium.Ellipsoid.WGS84.radii;
|
|
48222
|
+
for (let i = 0; i < HOVER_REFINE_STEPS && this.heightAt; i++) {
|
|
48223
|
+
const carto = Cesium.Cartographic.fromCartesian(hit);
|
|
48224
|
+
const height = this.heightAt(Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude));
|
|
48225
|
+
if (height == null) {
|
|
48226
|
+
break;
|
|
47735
48227
|
}
|
|
48228
|
+
const inflated = new Cesium.Ellipsoid(radii.x + height, radii.y + height, radii.z + height);
|
|
48229
|
+
const interval = Cesium.IntersectionTests.rayEllipsoid(ray, inflated);
|
|
48230
|
+
if (!interval) {
|
|
48231
|
+
break;
|
|
48232
|
+
}
|
|
48233
|
+
hit = Cesium.Ray.getPoint(ray, interval.start);
|
|
47736
48234
|
}
|
|
48235
|
+
const carto = Cesium.Cartographic.fromCartesian(hit);
|
|
48236
|
+
return this.texelAt(Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude));
|
|
47737
48237
|
}
|
|
47738
48238
|
/*
|
|
47739
48239
|
* Outlines each covered cell, from a projected lattice of its corners.
|
|
@@ -47761,44 +48261,25 @@
|
|
|
47761
48261
|
if (wide * high > MAX_GRID_CELLS) {
|
|
47762
48262
|
return;
|
|
47763
48263
|
}
|
|
47764
|
-
const lattice = new
|
|
47765
|
-
|
|
47766
|
-
|
|
47767
|
-
|
|
47768
|
-
|
|
47769
|
-
|
|
47770
|
-
|
|
47771
|
-
}
|
|
47772
|
-
}
|
|
47773
|
-
const path = new Path2D();
|
|
47774
|
-
let drew = false;
|
|
48264
|
+
const lattice = new Lattice(wide + 1, high + 1, (c, r) => {
|
|
48265
|
+
// Corners sit half a texel out from the centres lonAt/latAt return.
|
|
48266
|
+
const lon = this.lonAt(window.col0 + c - 0.5, cols);
|
|
48267
|
+
const lat = this.latAt(window.row0 + r - 0.5, rows);
|
|
48268
|
+
return this.projectInto(lon, lat);
|
|
48269
|
+
});
|
|
48270
|
+
const covered = new Uint8Array(wide * high);
|
|
47775
48271
|
for (let r = 0; r < high; r++) {
|
|
47776
48272
|
for (let c = 0; c < wide; c++) {
|
|
47777
|
-
|
|
47778
|
-
continue;
|
|
47779
|
-
}
|
|
47780
|
-
const tl = lattice[r * (wide + 1) + c];
|
|
47781
|
-
const tr = lattice[r * (wide + 1) + c + 1];
|
|
47782
|
-
const bl = lattice[(r + 1) * (wide + 1) + c];
|
|
47783
|
-
const br = lattice[(r + 1) * (wide + 1) + c + 1];
|
|
47784
|
-
if (!tl || !tr || !bl || !br) {
|
|
47785
|
-
continue;
|
|
47786
|
-
}
|
|
47787
|
-
path.moveTo(tl.x, tl.y);
|
|
47788
|
-
path.lineTo(tr.x, tr.y);
|
|
47789
|
-
path.lineTo(br.x, br.y);
|
|
47790
|
-
path.lineTo(bl.x, bl.y);
|
|
47791
|
-
path.closePath();
|
|
47792
|
-
drew = true;
|
|
48273
|
+
covered[r * wide + c] = this.isCovered(window.col0 + c, window.row0 + r, cols) ? 1 : 0;
|
|
47793
48274
|
}
|
|
47794
48275
|
}
|
|
47795
|
-
|
|
48276
|
+
const path = new Path2D();
|
|
48277
|
+
if (!lattice.Trace(path, covered, wide, high)) {
|
|
47796
48278
|
return;
|
|
47797
48279
|
}
|
|
47798
48280
|
this.ctx.strokeStyle = color;
|
|
47799
48281
|
this.ctx.lineWidth = (_b = this.settings.gridWidthPixels) !== null && _b !== void 0 ? _b : DEFAULT_GRID_WIDTH_PIXELS;
|
|
47800
48282
|
this.ctx.stroke(path);
|
|
47801
|
-
+" alpha" + this.ctx.globalAlpha;
|
|
47802
48283
|
}
|
|
47803
48284
|
/*
|
|
47804
48285
|
* Restricts drawing to the polygon, so nothing is ruled over ground it does not cover.
|
|
@@ -47820,33 +48301,11 @@
|
|
|
47820
48301
|
path.closePath();
|
|
47821
48302
|
this.ctx.clip(path);
|
|
47822
48303
|
}
|
|
47823
|
-
/*
|
|
47824
|
-
* The cell of whichever tile covers a point, as screen corners.
|
|
47825
|
-
*/
|
|
47826
|
-
tileCellOutline(lon, lat) {
|
|
47827
|
-
for (const tile of this.tiles) {
|
|
47828
|
-
if (lon < tile.West || lon > tile.East || lat < tile.South || lat > tile.North) {
|
|
47829
|
-
continue;
|
|
47830
|
-
}
|
|
47831
|
-
const c = (Math.min(tile.ResolutionX - 1, Math.max(0, Math.floor((lon - tile.West) / (tile.East - tile.West) * tile.ResolutionX))));
|
|
47832
|
-
const r = (Math.min(tile.ResolutionY - 1, Math.max(0, Math.floor((tile.North - lat) / (tile.North - tile.South) * tile.ResolutionY))));
|
|
47833
|
-
const w0 = tile.West + (c / tile.ResolutionX) * (tile.East - tile.West);
|
|
47834
|
-
const w1 = tile.West + ((c + 1) / tile.ResolutionX) * (tile.East - tile.West);
|
|
47835
|
-
const n0 = tile.North - (r / tile.ResolutionY) * (tile.North - tile.South);
|
|
47836
|
-
const n1 = tile.North - ((r + 1) / tile.ResolutionY) * (tile.North - tile.South);
|
|
47837
|
-
const corners = [
|
|
47838
|
-
this.project(w0, n0), this.project(w1, n0),
|
|
47839
|
-
this.project(w1, n1), this.project(w0, n1)
|
|
47840
|
-
];
|
|
47841
|
-
return corners.every((q) => q) ? corners : null;
|
|
47842
|
-
}
|
|
47843
|
-
return null;
|
|
47844
|
-
}
|
|
47845
48304
|
/*
|
|
47846
48305
|
* Outlines each tile's own cells, so cell size still shows where the mesh is refined.
|
|
47847
48306
|
*/
|
|
47848
48307
|
drawTileGrid(color) {
|
|
47849
|
-
var _a, _b
|
|
48308
|
+
var _a, _b;
|
|
47850
48309
|
const view = this.viewer.camera.computeViewRectangle(Cesium.Ellipsoid.WGS84);
|
|
47851
48310
|
if (!view) {
|
|
47852
48311
|
return;
|
|
@@ -47875,7 +48334,7 @@
|
|
|
47875
48334
|
coarse.lineTo(tr.x, tr.y);
|
|
47876
48335
|
coarse.lineTo(br.x, br.y);
|
|
47877
48336
|
coarse.lineTo(bl.x, bl.y);
|
|
47878
|
-
coarse.
|
|
48337
|
+
coarse.lineTo(tl.x, tl.y);
|
|
47879
48338
|
drewCoarse = true;
|
|
47880
48339
|
};
|
|
47881
48340
|
// Planned for every visible tile BEFORE any is drawn.
|
|
@@ -47936,31 +48395,29 @@
|
|
|
47936
48395
|
// moves, and so each step up is a subset of the one below it.
|
|
47937
48396
|
const firstR = Math.floor(r0 / step) * step;
|
|
47938
48397
|
const firstC = Math.floor(c0 / step) * step;
|
|
47939
|
-
|
|
47940
|
-
|
|
47941
|
-
|
|
47942
|
-
|
|
47943
|
-
|
|
47944
|
-
|
|
47945
|
-
|
|
47946
|
-
|
|
47947
|
-
|
|
47948
|
-
|
|
47949
|
-
|
|
47950
|
-
|
|
47951
|
-
|
|
47952
|
-
|
|
47953
|
-
|
|
47954
|
-
|
|
47955
|
-
|
|
47956
|
-
|
|
47957
|
-
|
|
47958
|
-
|
|
47959
|
-
|
|
47960
|
-
|
|
47961
|
-
|
|
47962
|
-
drew = true;
|
|
47963
|
-
}
|
|
48398
|
+
const wide = Math.ceil((c1 - firstC) / step);
|
|
48399
|
+
const high = Math.ceil((r1 - firstR) / step);
|
|
48400
|
+
const lonOf = (c) => tile.West
|
|
48401
|
+
+ (Math.min(firstC + c * step, tile.ResolutionX) / tile.ResolutionX) * (tile.East - tile.West);
|
|
48402
|
+
const latOf = (r) => tile.North
|
|
48403
|
+
- (Math.min(firstR + r * step, tile.ResolutionY) / tile.ResolutionY) * (tile.North - tile.South);
|
|
48404
|
+
const covered = new Uint8Array(wide * high);
|
|
48405
|
+
let any = false;
|
|
48406
|
+
for (let r = 0; r < high; r++) {
|
|
48407
|
+
for (let c = 0; c < wide; c++) {
|
|
48408
|
+
if (this.coveredAt((lonOf(c) + lonOf(c + 1)) / 2, (latOf(r) + latOf(r + 1)) / 2)) {
|
|
48409
|
+
covered[r * wide + c] = 1;
|
|
48410
|
+
any = true;
|
|
48411
|
+
}
|
|
48412
|
+
}
|
|
48413
|
+
}
|
|
48414
|
+
if (!any) {
|
|
48415
|
+
continue;
|
|
48416
|
+
}
|
|
48417
|
+
// Projected on demand, since a sparse raster leaves most corners belonging to no cell.
|
|
48418
|
+
const lattice = new Lattice(wide + 1, high + 1, (c, r) => this.projectInto(lonOf(c), latOf(r)));
|
|
48419
|
+
if (lattice.Trace(path, covered, wide, high)) {
|
|
48420
|
+
drew = true;
|
|
47964
48421
|
}
|
|
47965
48422
|
}
|
|
47966
48423
|
const gridWidth = (_b = this.settings.gridWidthPixels) !== null && _b !== void 0 ? _b : DEFAULT_GRID_WIDTH_PIXELS;
|
|
@@ -47978,7 +48435,7 @@
|
|
|
47978
48435
|
return;
|
|
47979
48436
|
}
|
|
47980
48437
|
this.ctx.strokeStyle = color;
|
|
47981
|
-
this.ctx.lineWidth =
|
|
48438
|
+
this.ctx.lineWidth = gridWidth;
|
|
47982
48439
|
this.ctx.stroke(path);
|
|
47983
48440
|
}
|
|
47984
48441
|
/*
|
|
@@ -47998,7 +48455,7 @@
|
|
|
47998
48455
|
};
|
|
47999
48456
|
}
|
|
48000
48457
|
/*
|
|
48001
|
-
* Whether the composited raster holds data at a lon/lat.
|
|
48458
|
+
* Whether the composited raster holds painted data at a lon/lat.
|
|
48002
48459
|
*/
|
|
48003
48460
|
coveredAt(lon, lat) {
|
|
48004
48461
|
if (!this.source) {
|
|
@@ -48011,60 +48468,20 @@
|
|
|
48011
48468
|
if (col < 0 || row < 0 || col >= cols || row >= rows) {
|
|
48012
48469
|
return false;
|
|
48013
48470
|
}
|
|
48014
|
-
const at = (row * cols + col) * 4 + 3;
|
|
48015
48471
|
// Painted, not merely covered: a dry cell holds a reading the ramp deliberately hides, and
|
|
48016
48472
|
// ruling a grid over it draws detail where the picture shows none.
|
|
48017
|
-
if (this.painted
|
|
48018
|
-
return this.
|
|
48473
|
+
if (this.painted || this.paintedRamp) {
|
|
48474
|
+
return this.paintedAt(col, row, cols);
|
|
48019
48475
|
}
|
|
48020
|
-
return this.source.data[
|
|
48476
|
+
return this.source.data[(row * cols + col) * 4 + 3] >= 128;
|
|
48021
48477
|
}
|
|
48022
48478
|
isCovered(col, row, cols) {
|
|
48023
48479
|
return Boolean(this.source && this.source.data[(row * cols + col) * 4 + 3] >= 128);
|
|
48024
48480
|
}
|
|
48025
48481
|
/*
|
|
48026
|
-
*
|
|
48027
|
-
*/
|
|
48028
|
-
widestLabel(parts) {
|
|
48029
|
-
const sample = [];
|
|
48030
|
-
if (parts.indexOf("value") >= 0) {
|
|
48031
|
-
sample.push(valueLine("-000.00", LabelUnits(this.metadata)));
|
|
48032
|
-
}
|
|
48033
|
-
const range = [];
|
|
48034
|
-
const showsRange = !exports.TextureFrameSeriesAnimator.IsTransformed(this.metadata);
|
|
48035
|
-
if (showsRange && parts.indexOf("floor") >= 0) {
|
|
48036
|
-
range.push("min: -000.00");
|
|
48037
|
-
}
|
|
48038
|
-
if (showsRange && parts.indexOf("ceiling") >= 0) {
|
|
48039
|
-
range.push("max: -000.00");
|
|
48040
|
-
}
|
|
48041
|
-
if (range.length) {
|
|
48042
|
-
sample.push(range.join(" - "));
|
|
48043
|
-
}
|
|
48044
|
-
if (!sample.length) {
|
|
48045
|
-
return 0;
|
|
48046
|
-
}
|
|
48047
|
-
const widest = Math.max(...sample.map((line) => this.ctx.measureText(line).width));
|
|
48048
|
-
return widest + LABEL_GAP_PIXELS;
|
|
48049
|
-
}
|
|
48050
|
-
/*
|
|
48051
|
-
* The four screen corners of one cell, or null if any of them will not project.
|
|
48482
|
+
* What a label says about one reading, a line at a time.
|
|
48052
48483
|
*/
|
|
48053
|
-
|
|
48054
|
-
// On a pyramid the composited texel is not a cell. Outlining it would draw a second,
|
|
48055
|
-
// uniform lattice over the tile grid, misaligned with it everywhere.
|
|
48056
|
-
if (this.tiles && this.tiles.length) {
|
|
48057
|
-
return this.tileCellOutline(this.lonAt(col, cols), this.latAt(row, rows));
|
|
48058
|
-
}
|
|
48059
|
-
const corners = [
|
|
48060
|
-
this.project(this.lonAt(col - 0.5, cols), this.latAt(row - 0.5, rows)),
|
|
48061
|
-
this.project(this.lonAt(col + 0.5, cols), this.latAt(row - 0.5, rows)),
|
|
48062
|
-
this.project(this.lonAt(col + 0.5, cols), this.latAt(row + 0.5, rows)),
|
|
48063
|
-
this.project(this.lonAt(col - 0.5, cols), this.latAt(row + 0.5, rows))
|
|
48064
|
-
];
|
|
48065
|
-
return corners.every((c) => c) ? corners : null;
|
|
48066
|
-
}
|
|
48067
|
-
drawLabel(point, reading, parts, outline) {
|
|
48484
|
+
labelLines(reading, parts) {
|
|
48068
48485
|
var _a;
|
|
48069
48486
|
const dp = (_a = this.settings.decimals) !== null && _a !== void 0 ? _a : (Math.abs(reading.value) < 10 ? 2 : 1);
|
|
48070
48487
|
const lines = [];
|
|
@@ -48083,32 +48500,7 @@
|
|
|
48083
48500
|
if (range.length) {
|
|
48084
48501
|
lines.push(range.join(" - "));
|
|
48085
48502
|
}
|
|
48086
|
-
|
|
48087
|
-
return;
|
|
48088
|
-
}
|
|
48089
|
-
if (outline) {
|
|
48090
|
-
this.ctx.strokeStyle = LABEL_ANCHOR_COLOR;
|
|
48091
|
-
this.ctx.lineWidth = 1.5;
|
|
48092
|
-
this.ctx.beginPath();
|
|
48093
|
-
this.ctx.moveTo(outline[0].x, outline[0].y);
|
|
48094
|
-
for (let i = 1; i < outline.length; i++) {
|
|
48095
|
-
this.ctx.lineTo(outline[i].x, outline[i].y);
|
|
48096
|
-
}
|
|
48097
|
-
this.ctx.closePath();
|
|
48098
|
-
this.ctx.stroke();
|
|
48099
|
-
}
|
|
48100
|
-
const width = Math.max(...lines.map((l) => this.ctx.measureText(l).width)) + 10;
|
|
48101
|
-
const height = lines.length * LINE_HEIGHT + 6;
|
|
48102
|
-
const top = point.y - height / 2;
|
|
48103
|
-
this.ctx.fillStyle = "rgba(16,16,14,0.72)";
|
|
48104
|
-
this.ctx.beginPath();
|
|
48105
|
-
this.ctx.roundRect(point.x - width / 2, top, width, height, LABEL_RADIUS);
|
|
48106
|
-
this.ctx.fill();
|
|
48107
|
-
lines.forEach((line, i) => {
|
|
48108
|
-
this.ctx.fillStyle = i === 0 ? "#ffffff" : "#a8a89f";
|
|
48109
|
-
this.ctx.fillText(line, point.x, top + LINE_HEIGHT * (i + 1) - 1);
|
|
48110
|
-
});
|
|
48111
|
-
this.drawn++;
|
|
48503
|
+
return lines;
|
|
48112
48504
|
}
|
|
48113
48505
|
/*
|
|
48114
48506
|
* The first cell with a reading at or near a lattice point, searched within its own block.
|
|
@@ -48135,11 +48527,24 @@
|
|
|
48135
48527
|
return null;
|
|
48136
48528
|
}
|
|
48137
48529
|
paintedAt(col, row, cols) {
|
|
48138
|
-
|
|
48139
|
-
|
|
48530
|
+
const at = (row * cols + col) * 4;
|
|
48531
|
+
if (this.painted && this.painted.length === this.source.data.length) {
|
|
48532
|
+
return this.painted[at + 3] > 0;
|
|
48140
48533
|
}
|
|
48141
|
-
|
|
48142
|
-
|
|
48534
|
+
if (this.paintedRamp) {
|
|
48535
|
+
const data = this.source.data;
|
|
48536
|
+
if (data[at + 3] === 0) {
|
|
48537
|
+
return false;
|
|
48538
|
+
}
|
|
48539
|
+
// The 8 bit grey the ramp is indexed on, which is what a tinted frame would have used.
|
|
48540
|
+
const grey = exports.TextureFrameSeriesAnimator.IsPackedValue(this.metadata)
|
|
48541
|
+
? Math.round((data[at] * 256 + data[at + 1]) / 65535 * 255)
|
|
48542
|
+
: data[at];
|
|
48543
|
+
const steps = this.paintedRamp.length / 4;
|
|
48544
|
+
const index = Math.min(steps - 1, Math.round(grey / 255 * (steps - 1)));
|
|
48545
|
+
return this.paintedRamp[index * 4 + 3] > 0;
|
|
48546
|
+
}
|
|
48547
|
+
return true;
|
|
48143
48548
|
}
|
|
48144
48549
|
// Decodes one cell, skipping anything the texture marks as having no data.
|
|
48145
48550
|
readAt(col, row, cols) {
|
|
@@ -48155,6 +48560,16 @@
|
|
|
48155
48560
|
sourceMap: this.sourceMap
|
|
48156
48561
|
});
|
|
48157
48562
|
}
|
|
48563
|
+
/*
|
|
48564
|
+
* The texel holding a lon/lat, or null outside the raster.
|
|
48565
|
+
*/
|
|
48566
|
+
texelAt(lon, lat) {
|
|
48567
|
+
const cols = this.source.width;
|
|
48568
|
+
const rows = this.source.height;
|
|
48569
|
+
const col = Math.floor((lon - this.extent.West) / (this.extent.East - this.extent.West) * cols);
|
|
48570
|
+
const row = Math.floor((this.extent.North - lat) / (this.extent.North - this.extent.South) * rows);
|
|
48571
|
+
return col < 0 || row < 0 || col >= cols || row >= rows ? null : { col, row };
|
|
48572
|
+
}
|
|
48158
48573
|
lonAt(col, cols) {
|
|
48159
48574
|
return this.extent.West + ((col + 0.5) / cols) * (this.extent.East - this.extent.West);
|
|
48160
48575
|
}
|
|
@@ -48183,15 +48598,102 @@
|
|
|
48183
48598
|
row1: Math.min(rows - 1, Math.ceil((this.extent.North - south) / spanLat * rows) + 1)
|
|
48184
48599
|
};
|
|
48185
48600
|
}
|
|
48601
|
+
// Metres the sheet sits at over a point, or the ellipsoid when nothing says otherwise.
|
|
48602
|
+
surfaceHeight(lon, lat) {
|
|
48603
|
+
const height = this.heightAt ? this.heightAt(lon, lat) : null;
|
|
48604
|
+
return height == null || !isFinite(height) ? 0 : height;
|
|
48605
|
+
}
|
|
48606
|
+
projectCell(col, row, cols, rows) {
|
|
48607
|
+
return this.project(this.lonAt(col, cols), this.latAt(row, rows));
|
|
48608
|
+
}
|
|
48609
|
+
// Projected at the sheet's own height, since up close the ellipsoid is metres from where it is drawn.
|
|
48186
48610
|
project(lon, lat) {
|
|
48187
|
-
|
|
48611
|
+
return this.projectInto(lon, lat) ? { x: this.scratchWindow.x, y: this.scratchWindow.y } : null;
|
|
48612
|
+
}
|
|
48613
|
+
/*
|
|
48614
|
+
* Projects into the shared scratch window position, returning whether it landed anywhere.
|
|
48615
|
+
*/
|
|
48616
|
+
projectInto(lon, lat) {
|
|
48617
|
+
const world = Cesium.Cartesian3.fromDegrees(lon, lat, this.surfaceHeight(lon, lat), Cesium.Ellipsoid.WGS84, this.scratchWorld);
|
|
48618
|
+
// One matrix multiply in 3D, where the general path rederives the viewport and frustum per point.
|
|
48619
|
+
if (this.viewProjection) {
|
|
48620
|
+
const clip = Cesium.Matrix4.multiplyByVector(this.viewProjection, Cesium.Cartesian4.fromElements(world.x, world.y, world.z, 1, this.scratchClip), this.scratchClip);
|
|
48621
|
+
if (!(clip.w > 0)) {
|
|
48622
|
+
return null;
|
|
48623
|
+
}
|
|
48624
|
+
const view = this.viewer.canvas;
|
|
48625
|
+
this.scratchWindow.x = (clip.x / clip.w * 0.5 + 0.5) * view.clientWidth;
|
|
48626
|
+
this.scratchWindow.y = (0.5 - clip.y / clip.w * 0.5) * view.clientHeight;
|
|
48627
|
+
return this.scratchWindow;
|
|
48628
|
+
}
|
|
48188
48629
|
const transforms = Cesium.SceneTransforms;
|
|
48189
48630
|
// Renamed in newer Cesium, and this library runs on whatever the host app supplies.
|
|
48190
48631
|
const fn = transforms.worldToWindowCoordinates || transforms.wgs84ToWindowCoordinates;
|
|
48191
|
-
return fn ? fn.call(transforms, this.viewer.scene, world) : null;
|
|
48632
|
+
return fn ? fn.call(transforms, this.viewer.scene, world, this.scratchWindow) || null : null;
|
|
48192
48633
|
}
|
|
48193
48634
|
}
|
|
48194
48635
|
TextureValueLabels.Labels = Labels;
|
|
48636
|
+
/*
|
|
48637
|
+
* A grid of window positions projected at most once each, and the cell edges traced through them.
|
|
48638
|
+
*/
|
|
48639
|
+
class Lattice {
|
|
48640
|
+
constructor(wide, high, projectAt) {
|
|
48641
|
+
this.wide = wide;
|
|
48642
|
+
this.projectAt = projectAt;
|
|
48643
|
+
this.xs = new Float64Array(wide * high);
|
|
48644
|
+
this.ys = new Float64Array(wide * high);
|
|
48645
|
+
this.state = new Uint8Array(wide * high);
|
|
48646
|
+
}
|
|
48647
|
+
at(c, r) {
|
|
48648
|
+
const i = r * this.wide + c;
|
|
48649
|
+
if (this.state[i] === 0) {
|
|
48650
|
+
const p = this.projectAt(c, r);
|
|
48651
|
+
if (p) {
|
|
48652
|
+
this.xs[i] = p.x;
|
|
48653
|
+
this.ys[i] = p.y;
|
|
48654
|
+
this.state[i] = 1;
|
|
48655
|
+
}
|
|
48656
|
+
else {
|
|
48657
|
+
this.state[i] = 2;
|
|
48658
|
+
}
|
|
48659
|
+
}
|
|
48660
|
+
return this.state[i] === 1 ? i : -1;
|
|
48661
|
+
}
|
|
48662
|
+
edge(path, a, b) {
|
|
48663
|
+
if (a < 0 || b < 0) {
|
|
48664
|
+
return false;
|
|
48665
|
+
}
|
|
48666
|
+
path.moveTo(this.xs[a], this.ys[a]);
|
|
48667
|
+
path.lineTo(this.xs[b], this.ys[b]);
|
|
48668
|
+
return true;
|
|
48669
|
+
}
|
|
48670
|
+
/*
|
|
48671
|
+
* Adds each covered cell's edges to a path, a shared edge once, and says whether any were added.
|
|
48672
|
+
*/
|
|
48673
|
+
Trace(path, covered, cellsWide, cellsHigh) {
|
|
48674
|
+
let drew = false;
|
|
48675
|
+
for (let r = 0; r < cellsHigh; r++) {
|
|
48676
|
+
for (let c = 0; c < cellsWide; c++) {
|
|
48677
|
+
if (!covered[r * cellsWide + c]) {
|
|
48678
|
+
continue;
|
|
48679
|
+
}
|
|
48680
|
+
const tl = this.at(c, r);
|
|
48681
|
+
const tr = this.at(c + 1, r);
|
|
48682
|
+
const bl = this.at(c, r + 1);
|
|
48683
|
+
if (!(r > 0 && covered[(r - 1) * cellsWide + c])) {
|
|
48684
|
+
drew = this.edge(path, tl, tr) || drew;
|
|
48685
|
+
}
|
|
48686
|
+
if (!(c > 0 && covered[r * cellsWide + c - 1])) {
|
|
48687
|
+
drew = this.edge(path, tl, bl) || drew;
|
|
48688
|
+
}
|
|
48689
|
+
const br = this.at(c + 1, r + 1);
|
|
48690
|
+
drew = this.edge(path, tr, br) || drew;
|
|
48691
|
+
drew = this.edge(path, bl, br) || drew;
|
|
48692
|
+
}
|
|
48693
|
+
}
|
|
48694
|
+
return drew;
|
|
48695
|
+
}
|
|
48696
|
+
}
|
|
48195
48697
|
/*
|
|
48196
48698
|
* The value line. Units go on it alone: repeating them on the range underneath doubles the widest
|
|
48197
48699
|
* label to say nothing, and the two lines are plainly the same quantity.
|
|
@@ -48287,6 +48789,9 @@ in vec2 st;
|
|
|
48287
48789
|
in float skirt;
|
|
48288
48790
|
|
|
48289
48791
|
uniform sampler2D u_valueTexture;
|
|
48792
|
+
// The frame being faded away from, and how far the fade has got. The same texture at 1 when settled.
|
|
48793
|
+
uniform sampler2D u_valueTextureFrom;
|
|
48794
|
+
uniform float u_valueFade;
|
|
48290
48795
|
uniform sampler2D u_groundTexture;
|
|
48291
48796
|
uniform float u_valuePacked;
|
|
48292
48797
|
uniform float u_valueMin;
|
|
@@ -48407,18 +48912,22 @@ float decodeValue(vec4 texel) {
|
|
|
48407
48912
|
}
|
|
48408
48913
|
|
|
48409
48914
|
void main() {
|
|
48410
|
-
vec4
|
|
48915
|
+
vec4 texelTo = texture(u_valueTexture, st);
|
|
48916
|
+
vec4 texelFrom = texture(u_valueTextureFrom, st);
|
|
48411
48917
|
v_st = st;
|
|
48918
|
+
// Decoded before mixing, since blending a packed pair byte by byte reads as some other value
|
|
48919
|
+
// halfway through the fade.
|
|
48920
|
+
float raw = mix(decodeValue(texelFrom), decodeValue(texelTo), u_valueFade);
|
|
48921
|
+
float alpha = mix(texelFrom.a, texelTo.a, u_valueFade);
|
|
48412
48922
|
|
|
48413
48923
|
// Inside a region the scene knows is water, a texel with no value still has to paint, or the
|
|
48414
48924
|
// hole the region exists to close is still a hole.
|
|
48415
48925
|
// The value it paints is the shallowest the ramp is not transparent at.
|
|
48416
48926
|
float inMask = u_maskWeight * texture(u_maskTexture, st).a;
|
|
48417
|
-
float hasData = step(u_coverageCutoffVertex,
|
|
48418
|
-
float raw = decodeValue(texel);
|
|
48927
|
+
float hasData = step(u_coverageCutoffVertex, alpha);
|
|
48419
48928
|
float filled = mix(u_maskFillValue, max(raw, u_maskFillValue), hasData);
|
|
48420
48929
|
v_value = mix(raw, filled, inMask * u_maskFill);
|
|
48421
|
-
v_coverage = max(
|
|
48930
|
+
v_coverage = max(alpha, inMask * u_maskFill);
|
|
48422
48931
|
|
|
48423
48932
|
// Exaggeration stretches the span above the archive's minimum rather than the height itself. On an
|
|
48424
48933
|
// elevation the height is measured from the ellipsoid, tens of metres from any of the data, so
|
|
@@ -48461,6 +48970,8 @@ void main() {
|
|
|
48461
48970
|
`;
|
|
48462
48971
|
const FRAGMENT_SHADER_GLSL300 = `
|
|
48463
48972
|
uniform sampler2D u_valueTexture;
|
|
48973
|
+
uniform sampler2D u_valueTextureFrom;
|
|
48974
|
+
uniform float u_valueFade;
|
|
48464
48975
|
uniform sampler2D u_rampTexture;
|
|
48465
48976
|
uniform float u_coverageCutoff;
|
|
48466
48977
|
uniform vec2 u_texelSize;
|
|
@@ -48497,12 +49008,15 @@ in vec2 v_waveMetres;
|
|
|
48497
49008
|
* Scaled per channel for the same reason the vertex shader's decode is: reassembling the pair reaches
|
|
48498
49009
|
* 65535, which mediump does not have to represent, and this is the shader that may not get highp.
|
|
48499
49010
|
*/
|
|
48500
|
-
float
|
|
48501
|
-
vec4 texel = texture(u_valueTexture, uv);
|
|
49011
|
+
float decodeValue(vec4 texel) {
|
|
48502
49012
|
float packed16 = texel.r * (255.0 * 256.0 / 65535.0) + texel.g * (255.0 / 65535.0);
|
|
48503
49013
|
return mix(texel.r, packed16, u_valuePacked);
|
|
48504
49014
|
}
|
|
48505
49015
|
|
|
49016
|
+
float valueAt(vec2 uv) {
|
|
49017
|
+
return mix(decodeValue(texture(u_valueTextureFrom, uv)), decodeValue(texture(u_valueTexture, uv)), u_valueFade);
|
|
49018
|
+
}
|
|
49019
|
+
|
|
48506
49020
|
//#water
|
|
48507
49021
|
/*
|
|
48508
49022
|
* Slope of the ripple field at a point, in metres of rise per metre travelled.
|
|
@@ -48949,6 +49463,13 @@ void main() {
|
|
|
48949
49463
|
this.rampTexture = null;
|
|
48950
49464
|
this.texture = null;
|
|
48951
49465
|
this.textureDirty = true;
|
|
49466
|
+
// Fed from a fading source: the frame being left, and which pixels each texture holds.
|
|
49467
|
+
this.fade = null;
|
|
49468
|
+
this.textureFrom = null;
|
|
49469
|
+
this.uploadedTo = null;
|
|
49470
|
+
this.uploadedFrom = null;
|
|
49471
|
+
// Size of whichever value raster is bound, canvas or pixels.
|
|
49472
|
+
this.sourceDims = null;
|
|
48952
49473
|
this.shaderProgram = null;
|
|
48953
49474
|
this.shaderProgramWater = false;
|
|
48954
49475
|
this.renderState = null;
|
|
@@ -49119,6 +49640,65 @@ void main() {
|
|
|
49119
49640
|
this.maskGroundTexture = null;
|
|
49120
49641
|
}
|
|
49121
49642
|
}
|
|
49643
|
+
/**
|
|
49644
|
+
* Metres above the ellipsoid the sheet sits at over a point, or null unless a fading source is bound.
|
|
49645
|
+
*/
|
|
49646
|
+
HeightAt(lon, lat) {
|
|
49647
|
+
const pixels = this.fade ? this.fade.to : null;
|
|
49648
|
+
const dims = this.sourceDims;
|
|
49649
|
+
if (!pixels || !dims) {
|
|
49650
|
+
return null;
|
|
49651
|
+
}
|
|
49652
|
+
const fx = (lon - this.extent.West) / (this.extent.East - this.extent.West);
|
|
49653
|
+
const fy = (this.extent.North - lat) / (this.extent.North - this.extent.South);
|
|
49654
|
+
if (!(fx >= 0 && fx <= 1 && fy >= 0 && fy <= 1)) {
|
|
49655
|
+
return null;
|
|
49656
|
+
}
|
|
49657
|
+
const col = Math.min(dims.width - 1, Math.floor(fx * dims.width));
|
|
49658
|
+
const row = Math.min(dims.height - 1, Math.floor(fy * dims.height));
|
|
49659
|
+
const at = (row * dims.width + col) * 4;
|
|
49660
|
+
const coverage = pixels[at + 3] / 255;
|
|
49661
|
+
const normalised = this.packedValue
|
|
49662
|
+
? (pixels[at] * 256 + pixels[at + 1]) / 65535
|
|
49663
|
+
: pixels[at] / 255;
|
|
49664
|
+
const metres = this.valueMin + normalised * (this.valueMax - this.valueMin) * this.exaggeration;
|
|
49665
|
+
const following = this.followGround && Boolean(this.groundPixels);
|
|
49666
|
+
const ground = following ? this.groundAt(fx, fy) : 0;
|
|
49667
|
+
return this.baseHeight + ground + metres * (following ? coverage : 1);
|
|
49668
|
+
}
|
|
49669
|
+
/**
|
|
49670
|
+
* Changes whenever HeightAt would answer differently for the same frame.
|
|
49671
|
+
*/
|
|
49672
|
+
GetPlacementKey() {
|
|
49673
|
+
return [this.groundPixels ? 1 : 0, this.baseHeight, this.exaggeration, this.followGround ? 1 : 0].join("|");
|
|
49674
|
+
}
|
|
49675
|
+
/**
|
|
49676
|
+
* The ramp the sheet is painted with, as 256 RGBA steps across the value range.
|
|
49677
|
+
*/
|
|
49678
|
+
GetRampPixels() {
|
|
49679
|
+
return this.rampPixels;
|
|
49680
|
+
}
|
|
49681
|
+
/*
|
|
49682
|
+
* The packed ground raster read the way sampleGround reads it, bilinear between texel centres.
|
|
49683
|
+
*/
|
|
49684
|
+
groundAt(u, v) {
|
|
49685
|
+
const size = GROUND_SAMPLES_PER_SIDE;
|
|
49686
|
+
const pixels = this.groundPixels;
|
|
49687
|
+
const read = (x, y) => {
|
|
49688
|
+
const i = (Math.min(size - 1, Math.max(0, y)) * size + Math.min(size - 1, Math.max(0, x))) * 4;
|
|
49689
|
+
const packed = (pixels[i] << 16) + (pixels[i + 1] << 8) + pixels[i + 2];
|
|
49690
|
+
return GROUND_MIN_METRES + (packed / 16777215) * (GROUND_MAX_METRES - GROUND_MIN_METRES);
|
|
49691
|
+
};
|
|
49692
|
+
const x = u * size - 0.5;
|
|
49693
|
+
const y = v * size - 0.5;
|
|
49694
|
+
const x0 = Math.floor(x);
|
|
49695
|
+
const y0 = Math.floor(y);
|
|
49696
|
+
const fx = x - x0;
|
|
49697
|
+
const fy = y - y0;
|
|
49698
|
+
const top = read(x0, y0) + (read(x0 + 1, y0) - read(x0, y0)) * fx;
|
|
49699
|
+
const bottom = read(x0, y0 + 1) + (read(x0 + 1, y0 + 1) - read(x0, y0 + 1)) * fx;
|
|
49700
|
+
return top + (bottom - top) * fy;
|
|
49701
|
+
}
|
|
49122
49702
|
/**
|
|
49123
49703
|
* Feeds the surface from an animated frame series.
|
|
49124
49704
|
* Height and colour advance with the clock without the caller having to push each frame in.
|
|
@@ -49137,23 +49717,39 @@ void main() {
|
|
|
49137
49717
|
return;
|
|
49138
49718
|
}
|
|
49139
49719
|
const context = frameState.context;
|
|
49140
|
-
|
|
49141
|
-
|
|
49142
|
-
|
|
49143
|
-
|
|
49144
|
-
|
|
49145
|
-
}
|
|
49146
|
-
|
|
49147
|
-
|
|
49148
|
-
|
|
49149
|
-
|
|
49150
|
-
|
|
49720
|
+
const fade = this.valueSource && this.valueSource.GetValueFade
|
|
49721
|
+
? this.valueSource.GetValueFade()
|
|
49722
|
+
: null;
|
|
49723
|
+
if (fade) {
|
|
49724
|
+
this.fade = fade;
|
|
49725
|
+
this.sourceDims = { width: fade.width, height: fade.height };
|
|
49726
|
+
}
|
|
49727
|
+
else {
|
|
49728
|
+
this.fade = null;
|
|
49729
|
+
if (this.valueSource) {
|
|
49730
|
+
const canvas = this.valueSource.GetValueCanvas();
|
|
49731
|
+
if (!canvas) {
|
|
49732
|
+
// No frame decoded yet, so there is nothing to displace against.
|
|
49733
|
+
return;
|
|
49734
|
+
}
|
|
49735
|
+
const version = this.valueSource.GetPresentVersion();
|
|
49736
|
+
if (canvas !== this.source || version !== this.lastPresentVersion) {
|
|
49737
|
+
this.source = canvas;
|
|
49738
|
+
this.lastPresentVersion = version;
|
|
49739
|
+
this.textureDirty = true;
|
|
49740
|
+
}
|
|
49151
49741
|
}
|
|
49742
|
+
this.sourceDims = this.source ? { width: this.source.width, height: this.source.height } : null;
|
|
49152
49743
|
}
|
|
49153
|
-
if (!this.
|
|
49744
|
+
if (!this.sourceDims || !this.sourceDims.width || !this.sourceDims.height) {
|
|
49154
49745
|
return;
|
|
49155
49746
|
}
|
|
49156
|
-
this.
|
|
49747
|
+
if (this.fade) {
|
|
49748
|
+
this.syncFadeTextures(context);
|
|
49749
|
+
}
|
|
49750
|
+
else {
|
|
49751
|
+
this.syncTexture(context);
|
|
49752
|
+
}
|
|
49157
49753
|
this.syncRampTexture(context);
|
|
49158
49754
|
this.requestGround();
|
|
49159
49755
|
this.syncGroundTexture(context);
|
|
@@ -49201,6 +49797,10 @@ void main() {
|
|
|
49201
49797
|
this.texture.destroy();
|
|
49202
49798
|
this.texture = null;
|
|
49203
49799
|
}
|
|
49800
|
+
if (this.textureFrom) {
|
|
49801
|
+
this.textureFrom.destroy();
|
|
49802
|
+
this.textureFrom = null;
|
|
49803
|
+
}
|
|
49204
49804
|
if (this.rampTexture) {
|
|
49205
49805
|
this.rampTexture.destroy();
|
|
49206
49806
|
this.rampTexture = null;
|
|
@@ -49468,7 +50068,7 @@ void main() {
|
|
|
49468
50068
|
const southWest = Cesium.Cartesian3.fromDegrees(this.extent.West, this.extent.South, 0);
|
|
49469
50069
|
const southEast = Cesium.Cartesian3.fromDegrees(this.extent.East, this.extent.South, 0);
|
|
49470
50070
|
const width = Cesium.Cartesian3.distance(southWest, southEast);
|
|
49471
|
-
return Math.max(1, width / Math.max(1, this.
|
|
50071
|
+
return Math.max(1, width / Math.max(1, this.sourceDims ? this.sourceDims.width : 1));
|
|
49472
50072
|
}
|
|
49473
50073
|
syncRampTexture(context) {
|
|
49474
50074
|
if (this.rampTexture) {
|
|
@@ -49489,6 +50089,67 @@ void main() {
|
|
|
49489
50089
|
})
|
|
49490
50090
|
});
|
|
49491
50091
|
}
|
|
50092
|
+
valueSampler() {
|
|
50093
|
+
// Nearest for a packed value, because filtering would blend the two bytes of the
|
|
50094
|
+
// packing rather than the values they encode, the same trap sampleGround avoids.
|
|
50095
|
+
const minification = this.packedValue
|
|
50096
|
+
? Cesium.TextureMinificationFilter.NEAREST
|
|
50097
|
+
: Cesium.TextureMinificationFilter.LINEAR;
|
|
50098
|
+
const magnification = this.packedValue
|
|
50099
|
+
? Cesium.TextureMagnificationFilter.NEAREST
|
|
50100
|
+
: Cesium.TextureMagnificationFilter.LINEAR;
|
|
50101
|
+
return new Cesium.Sampler({
|
|
50102
|
+
wrapS: Cesium.TextureWrap.CLAMP_TO_EDGE,
|
|
50103
|
+
wrapT: Cesium.TextureWrap.CLAMP_TO_EDGE,
|
|
50104
|
+
minificationFilter: minification,
|
|
50105
|
+
magnificationFilter: magnification
|
|
50106
|
+
});
|
|
50107
|
+
}
|
|
50108
|
+
/*
|
|
50109
|
+
* Uploads value pixels into a texture, reusing it when the size still fits.
|
|
50110
|
+
*/
|
|
50111
|
+
uploadPixels(context, texture, pixels, width, height) {
|
|
50112
|
+
const view = new Uint8Array(pixels.buffer, pixels.byteOffset, pixels.byteLength);
|
|
50113
|
+
const source = { width, height, arrayBufferView: view };
|
|
50114
|
+
if (texture && texture.width === width && texture.height === height) {
|
|
50115
|
+
texture.copyFrom({ source });
|
|
50116
|
+
return texture;
|
|
50117
|
+
}
|
|
50118
|
+
if (texture) {
|
|
50119
|
+
texture.destroy();
|
|
50120
|
+
}
|
|
50121
|
+
return new Cesium.Texture({
|
|
50122
|
+
context,
|
|
50123
|
+
width,
|
|
50124
|
+
height,
|
|
50125
|
+
pixelFormat: Cesium.PixelFormat.RGBA,
|
|
50126
|
+
pixelDatatype: Cesium.PixelDatatype.UNSIGNED_BYTE,
|
|
50127
|
+
source,
|
|
50128
|
+
// Row 0 is the archive's north edge, and has to stay at the top, as the canvas path does.
|
|
50129
|
+
flipY: false,
|
|
50130
|
+
sampler: this.valueSampler()
|
|
50131
|
+
});
|
|
50132
|
+
}
|
|
50133
|
+
/*
|
|
50134
|
+
* Keeps a texture each side of the fade, swapping them on a new frame so only that one is uploaded.
|
|
50135
|
+
*/
|
|
50136
|
+
syncFadeTextures(context) {
|
|
50137
|
+
const fade = this.fade;
|
|
50138
|
+
if (fade.to !== this.uploadedTo) {
|
|
50139
|
+
if (fade.from && fade.from === this.uploadedTo && this.texture) {
|
|
50140
|
+
const previous = this.textureFrom;
|
|
50141
|
+
this.textureFrom = this.texture;
|
|
50142
|
+
this.uploadedFrom = this.uploadedTo;
|
|
50143
|
+
this.texture = previous;
|
|
50144
|
+
}
|
|
50145
|
+
this.texture = this.uploadPixels(context, this.texture, fade.to, fade.width, fade.height);
|
|
50146
|
+
this.uploadedTo = fade.to;
|
|
50147
|
+
}
|
|
50148
|
+
if (fade.from && fade.from !== this.uploadedFrom) {
|
|
50149
|
+
this.textureFrom = this.uploadPixels(context, this.textureFrom, fade.from, fade.width, fade.height);
|
|
50150
|
+
this.uploadedFrom = fade.from;
|
|
50151
|
+
}
|
|
50152
|
+
}
|
|
49492
50153
|
syncTexture(context) {
|
|
49493
50154
|
const sizeChanged = this.texture
|
|
49494
50155
|
&& (this.texture.width !== this.source.width || this.texture.height !== this.source.height);
|
|
@@ -49496,27 +50157,15 @@ void main() {
|
|
|
49496
50157
|
if (this.texture) {
|
|
49497
50158
|
this.texture.destroy();
|
|
49498
50159
|
}
|
|
49499
|
-
// Nearest for a packed value, because filtering would blend the two bytes of the
|
|
49500
|
-
// packing rather than the values they encode, the same trap sampleGround avoids.
|
|
49501
|
-
const minification = this.packedValue
|
|
49502
|
-
? Cesium.TextureMinificationFilter.NEAREST
|
|
49503
|
-
: Cesium.TextureMinificationFilter.LINEAR;
|
|
49504
|
-
const magnification = this.packedValue
|
|
49505
|
-
? Cesium.TextureMagnificationFilter.NEAREST
|
|
49506
|
-
: Cesium.TextureMagnificationFilter.LINEAR;
|
|
49507
50160
|
this.texture = new Cesium.Texture({
|
|
49508
50161
|
context,
|
|
49509
50162
|
source: this.source,
|
|
49510
50163
|
// Cesium flips canvas rows on upload by default, which would put the archive's
|
|
49511
50164
|
// row 0 (its north edge) at the south edge and mirror the whole surface.
|
|
49512
50165
|
flipY: false,
|
|
49513
|
-
sampler:
|
|
49514
|
-
wrapS: Cesium.TextureWrap.CLAMP_TO_EDGE,
|
|
49515
|
-
wrapT: Cesium.TextureWrap.CLAMP_TO_EDGE,
|
|
49516
|
-
minificationFilter: minification,
|
|
49517
|
-
magnificationFilter: magnification
|
|
49518
|
-
})
|
|
50166
|
+
sampler: this.valueSampler()
|
|
49519
50167
|
});
|
|
50168
|
+
this.uploadedTo = null;
|
|
49520
50169
|
this.textureDirty = false;
|
|
49521
50170
|
return;
|
|
49522
50171
|
}
|
|
@@ -49579,7 +50228,7 @@ void main() {
|
|
|
49579
50228
|
* carry, so the ladder is capped one step above the texel count rather than run to its top.
|
|
49580
50229
|
*/
|
|
49581
50230
|
tierCeiling() {
|
|
49582
|
-
const source = this.
|
|
50231
|
+
const source = this.sourceDims;
|
|
49583
50232
|
if (!source || !source.width || !source.height) {
|
|
49584
50233
|
return TIERS[TIERS.length - 1];
|
|
49585
50234
|
}
|
|
@@ -49657,6 +50306,10 @@ void main() {
|
|
|
49657
50306
|
owner: this,
|
|
49658
50307
|
uniformMap: {
|
|
49659
50308
|
u_valueTexture: () => self.texture,
|
|
50309
|
+
u_valueTextureFrom: () => (self.fade && self.fade.from && self.textureFrom
|
|
50310
|
+
? self.textureFrom
|
|
50311
|
+
: self.texture),
|
|
50312
|
+
u_valueFade: () => (self.fade && self.fade.from ? self.fade.mix : 1),
|
|
49660
50313
|
u_groundTexture: () => self.groundTexture,
|
|
49661
50314
|
u_valuePacked: () => (self.packedValue ? 1 : 0),
|
|
49662
50315
|
u_groundWeight: () => (self.followGround && self.groundPixels ? 1 : 0),
|
|
@@ -49694,7 +50347,7 @@ void main() {
|
|
|
49694
50347
|
: 0,
|
|
49695
50348
|
u_coverageCutoff: () => COVERAGE_CUTOFF,
|
|
49696
50349
|
u_rampTexture: () => self.rampTexture,
|
|
49697
|
-
u_texelSize: () => new Cesium.Cartesian2(1 / self.
|
|
50350
|
+
u_texelSize: () => new Cesium.Cartesian2(1 / self.sourceDims.width, 1 / self.sourceDims.height),
|
|
49698
50351
|
u_waterOn: () => (self.water ? 1 : 0),
|
|
49699
50352
|
u_waterWavelength: () => (self.water && self.water.wavelength)
|
|
49700
50353
|
|| DEFAULT_WATER_WAVELENGTH_METRES,
|
|
@@ -50065,6 +50718,10 @@ void main() {
|
|
|
50065
50718
|
syncTextureValueLabels({
|
|
50066
50719
|
cEntity,
|
|
50067
50720
|
viewer: params.viewer,
|
|
50721
|
+
surface: drawsDisplacedSurface ? cEntity[DISPLACED_SURFACE_KEY] : null,
|
|
50722
|
+
hover: params.visualRegister
|
|
50723
|
+
? { register: params.visualRegister, entityId: String(entity.Bruce.ID), menuItemId: params.menuItemId }
|
|
50724
|
+
: undefined,
|
|
50068
50725
|
settings: overlaySettings(style, entity, params.tags),
|
|
50069
50726
|
extent: labelArchive
|
|
50070
50727
|
? (drawsDisplacedSurface ? archiveExtent(labelArchive.metadata, posses) : drapeExtent)
|
|
@@ -50371,6 +51028,7 @@ void main() {
|
|
|
50371
51028
|
// rendered: null
|
|
50372
51029
|
rendered: (_c = params.rendered) === null || _c === void 0 ? void 0 : _c.get(entity.Bruce.ID),
|
|
50373
51030
|
menuItemId: params.menuItemId,
|
|
51031
|
+
visualRegister: params.visualRegister,
|
|
50374
51032
|
onSeriesDiscovered: params.onSeriesDiscovered
|
|
50375
51033
|
});
|
|
50376
51034
|
}
|
|
@@ -50808,8 +51466,8 @@ void main() {
|
|
|
50808
51466
|
*/
|
|
50809
51467
|
const LABEL_GRID_COLOR = "rgba(255,255,255,0.55)";
|
|
50810
51468
|
const LABEL_GRID_WIDTH_PIXELS = 1;
|
|
50811
|
-
const LABEL_MIN_SPACING_PIXELS =
|
|
50812
|
-
const LABEL_MAX_LABELS =
|
|
51469
|
+
const LABEL_MIN_SPACING_PIXELS = 140;
|
|
51470
|
+
const LABEL_MAX_LABELS = 60;
|
|
50813
51471
|
function overlaySettings(style, entity, tags) {
|
|
50814
51472
|
if (!wantsLabels(style)) {
|
|
50815
51473
|
return undefined;
|
|
@@ -50829,6 +51487,7 @@ void main() {
|
|
|
50829
51487
|
*/
|
|
50830
51488
|
function syncTextureValueLabels(params) {
|
|
50831
51489
|
const { cEntity, viewer, settings, extent, animator, metadata } = params;
|
|
51490
|
+
const surface = params.surface && !params.surface.isDestroyed() ? params.surface : null;
|
|
50832
51491
|
const existing = cEntity[TEXTURE_VALUE_LABELS_KEY];
|
|
50833
51492
|
// Offline has no extent to rebuild the overlay from, so an existing one is left running.
|
|
50834
51493
|
if (params.offline && existing) {
|
|
@@ -50847,8 +51506,13 @@ void main() {
|
|
|
50847
51506
|
West: tile.West, East: tile.East, South: tile.South, North: tile.North,
|
|
50848
51507
|
ResolutionX: tile.ResolutionX, ResolutionY: tile.ResolutionY
|
|
50849
51508
|
})),
|
|
50850
|
-
clipRing: params.clipRing
|
|
51509
|
+
clipRing: params.clipRing,
|
|
51510
|
+
heightAt: surface ? (lon, lat) => surface.HeightAt(lon, lat) : undefined,
|
|
51511
|
+
heightKey: surface ? () => surface.GetPlacementKey() : undefined,
|
|
51512
|
+
hover: params.hover
|
|
50851
51513
|
});
|
|
51514
|
+
// A surface keeps no tinted frame, so whether a cell was painted is read off its ramp.
|
|
51515
|
+
labels.SetPaintedRamp(surface ? surface.GetRampPixels() : null);
|
|
50852
51516
|
let lastVersion = -1;
|
|
50853
51517
|
let appliedFloor = null;
|
|
50854
51518
|
let appliedCeiling = null;
|
|
@@ -50860,15 +51524,15 @@ void main() {
|
|
|
50860
51524
|
if (animator.IsDisposed()) {
|
|
50861
51525
|
return;
|
|
50862
51526
|
}
|
|
50863
|
-
const canvas = animator.GetValueCanvas();
|
|
50864
|
-
if (!canvas) {
|
|
50865
|
-
return;
|
|
50866
|
-
}
|
|
50867
51527
|
const version = animator.GetPresentVersion();
|
|
50868
51528
|
if (version !== lastVersion) {
|
|
51529
|
+
// Read from the frame cache, since reading the value canvas back copies a whole frame.
|
|
51530
|
+
const raster = animator.GetValueRaster();
|
|
51531
|
+
if (!raster) {
|
|
51532
|
+
return;
|
|
51533
|
+
}
|
|
50869
51534
|
lastVersion = version;
|
|
50870
|
-
|
|
50871
|
-
labels.SetSource(ctx.getImageData(0, 0, canvas.width, canvas.height));
|
|
51535
|
+
labels.SetSource(raster);
|
|
50872
51536
|
labels.SetPainted(animator.GetDisplayedPixels());
|
|
50873
51537
|
}
|
|
50874
51538
|
// Tracked by reference rather than latched on the first one to arrive: a tiled archive
|
|
@@ -52592,7 +53256,7 @@ void main() {
|
|
|
52592
53256
|
StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
|
|
52593
53257
|
})(exports.StyleUtils || (exports.StyleUtils = {}));
|
|
52594
53258
|
|
|
52595
|
-
const VERSION = "7.5.
|
|
53259
|
+
const VERSION = "7.5.9";
|
|
52596
53260
|
/**
|
|
52597
53261
|
* Updates the environment instance used by bruce-cesium to one specified.
|
|
52598
53262
|
* This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.
|