@doegis/core
Version:
DOE GIS API
3 lines (1 loc) • 11.2 kB
JavaScript
import t from"../../../../../core/Error.js";import has from"../../../../../core/has.js";import e from"../../../../../core/Logger.js";import{clamp as i}from"../../../../../core/mathUtils.js";import{isNone as s,isSome as r,mapMany as a,forEachSome as n,applySome as o,unwrap as h}from"../../../../../core/maybe.js";import{createResolver as l,isAbortError as u}from"../../../../../core/promiseUtils.js";import{diff as d}from"../../../../../core/accessorSupport/diffUtils.js";import c from"../../../../../layers/support/FieldsIndex.js";import{MAX_FILTERS as p,HIGHLIGHT_FLAG as g,ATTRIBUTE_DATA_VV as _,NAN_MAGIC_NUMBER as f,ATTRIBUTE_DATA_ANIMATION as y,ATTRIBUTE_DATA_GPGPU as m}from"../../../engine/webgl/definitions.js";import{DISPLAY_ID_TYPE_AGGREGATE as b,DISPLAY_ID_TYPE_FEATURE as x,getDisplayIdTexel as S,getDisplayIdType as T,getDisplayIdFilterMask as z}from"../../../engine/webgl/DisplayId.js";import{getPixelArrayCtor as A}from"../../../engine/webgl/Utils.js";import{createDebugLogger as E,DEBUG_ATTR_UPDATES as U}from"../../../engine/webgl/util/debug.js";import{getVisualVariableSizeValueRepresentationRatio as w}from"../tileRenderers/support/visualVariablesUtils.js";import{PixelType as D}from"../../../../webgl/enums.js";const k=e.getLogger("esri.views.layers.2d.features.support.AttributeStore"),F=E(U,k),I={sharedArrayBuffer:has("esri-shared-array-buffer"),atomics:has("esri-atomics")};function B(t,e){return i=>e(t(i))}class C{constructor(t,e,i,s){this.size=0,this.texelSize=4,this.dirtyStart=0,this.dirtyEnd=0;const{pixelType:r,layout:a,textureOnly:n}=s;this.textureOnly=n||!1,this.pixelType=r,this._ctype=e,this.layout=a,this._resetRange(),this._shared=t,this.size=i,n||(this.data=this._initData(r,i,t,e))}get buffer(){return o(this.data,(t=>t.buffer))}unsetComponentAllTexels(t,e){const i=h(this.data);for(let s=0;s<this.size*this.size;s++)i[s*this.texelSize+t]&=~e;this.dirtyStart=0,this.dirtyEnd=this.size*this.size-1}setComponentAllTexels(t,e){const i=h(this.data);for(let s=0;s<this.size*this.size;s++)i[s*this.texelSize+t]|=255&e;this.dirtyStart=0,this.dirtyEnd=this.size*this.size-1}setComponent(t,e,i){const s=h(this.data);for(const r of i)s[r*this.texelSize+t]|=e,this.dirtyStart=Math.min(this.dirtyStart,r),this.dirtyEnd=Math.max(this.dirtyEnd,r)}setComponentTexel(t,e,i){h(this.data)[i*this.texelSize+t]|=e,this.dirtyStart=Math.min(this.dirtyStart,i),this.dirtyEnd=Math.max(this.dirtyEnd,i)}unsetComponentTexel(t,e,i){h(this.data)[i*this.texelSize+t]&=~e,this.dirtyStart=Math.min(this.dirtyStart,i),this.dirtyEnd=Math.max(this.dirtyEnd,i)}getData(t,e){const i=S(t);return h(this.data)[i*this.texelSize+e]}setData(t,e,i){const r=S(t),a=1<<e;0!=(this.layout&a)?s(this.data)||(this.data[r*this.texelSize+e]=i,this.dirtyStart=Math.min(this.dirtyStart,r),this.dirtyEnd=Math.max(this.dirtyEnd,r)):k.error("mapview-attributes-store","Tried to set a value for a texel's readonly component")}lock(){this.pixelType===D.UNSIGNED_BYTE&&this._shared&&I.atomics&&"local"!==this._ctype&&Atomics.store(this.data,0,1)}unlock(){this.pixelType===D.UNSIGNED_BYTE&&this._shared&&I.atomics&&"local"!==this._ctype&&Atomics.store(this.data,0,0)}expand(t){if(this.size=t,!this.textureOnly){const e=this._initData(this.pixelType,t,this._shared,this._ctype),i=h(this.data);e.set(i),this.data=e}}toMessage(){const t=this.dirtyStart,e=this.dirtyEnd,i=this.texelSize;if(t>e)return null;this._resetRange();const s=!(this._shared||"local"===this._ctype),r=this.pixelType,a=this.layout,n=h(this.data);return{start:t,end:e,data:s&&n.slice(t*i,(e+1)*i)||null,pixelType:r,layout:a}}_initData(t,e,i,s){const r=i&&"local"!==s?SharedArrayBuffer:ArrayBuffer,a=A(t),n=new a(new r(e*e*4*a.BYTES_PER_ELEMENT));for(let o=0;o<n.length;o+=4)n[o+1]=255;return n}_resetRange(){this.dirtyStart=2147483647,this.dirtyEnd=0}}class M{constructor(t,e,i=(()=>{})){this._client=t,this.config=e,this._notifyChange=i,this._blocks=new Array,this._filters=new Array(p),this._attributeComputeInfo=null,this._targetType=0,this._abortController=new AbortController,this._hasScaleExpr=!1,this._size=32,this._nextUpdate=null,this._currUpdate=null,this._idsToHighlight=new Set;const s=e.supportsTextureFloat?D.FLOAT:D.UNSIGNED_BYTE;F(`Creating AttributeStore ${I.sharedArrayBuffer?"with":"without"} shared memory`),this._blockDescriptors=[{pixelType:D.UNSIGNED_BYTE,layout:1},{pixelType:D.UNSIGNED_BYTE,layout:15,textureOnly:!0},{pixelType:D.UNSIGNED_BYTE,layout:15,textureOnly:!0},{pixelType:s,layout:15},{pixelType:s,layout:15},{pixelType:s,layout:15},{pixelType:s,layout:15}],this._blocks=this._blockDescriptors.map((()=>null))}destroy(){this._abortController.abort()}get hasScaleExpr(){return this._hasScaleExpr}get _signal(){return this._abortController.signal}get hasHighlight(){return this._idsToHighlight.size>0}isUpdating(){return!!this._currUpdate||!!this._nextUpdate}update(t,e){this.config=e;const i=e.schema.processors[0].storage,a=d(this._schema,i);if((t.targets.feature||t.targets.aggregate)&&(t.storage.data=!0),a&&(has("esri-2d-update-debug")&&console.debug("Applying Update - AttributeStore:",a),t.storage.data=!0,this._schema=i,this._attributeComputeInfo=null,!s(i))){switch(i.target){case"feature":this._targetType=x;break;case"aggregate":this._targetType=b}if("subtype"===i.type){this._attributeComputeInfo={isSubtype:!0,subtypeField:i.subtypeField,map:new Map};for(const t in i.mapping){const e=i.mapping[t];if(r(e)&&r(e.vvMapping))for(const i of e.vvMapping)this._bindAttribute(i,parseInt(t,10))}}else{if(this._attributeComputeInfo={isSubtype:!1,map:new Map},r(i.vvMapping))for(const t of i.vvMapping)this._bindAttribute(t);if(r(i.attributeMapping))for(const t of i.attributeMapping)this._bindAttribute(t)}}}onTileData(t,e){if(s(e.addOrUpdate))return;const i=e.addOrUpdate.getCursor();for(;i.next();){const t=i.getDisplayId();this.setAttributeData(t,i)}}async setHighlight(t,e){const i=1,s=this._getBlock(0),r=e.map((t=>S(t)));s.lock(),s.unsetComponentAllTexels(0,i),s.setComponent(0,i,r),s.unlock(),this._idsToHighlight.clear();for(const a of t)this._idsToHighlight.add(a);await this.sendUpdates()}async updateFilters(t,e,i){const{service:s,spatialReference:r}=i,{filters:a}=e,n=a.map(((t,e)=>this._updateFilter(t,e,s,r)));(await Promise.all(n)).some((t=>t))&&(t.storage.filters=!0,has("esri-2d-update-debug")&&console.debug("Applying Update - AttributeStore:","Filters changed"))}setData(t,e,i,s){const r=S(t);this._ensureSizeForTexel(r),this._getBlock(e).setData(t,i,s)}getData(t,e,i){return this._getBlock(e).getData(t,i)}getHighlightFlag(t){return this._idsToHighlight.has(t)?g:0}unsetAttributeData(t){const e=S(t);this._getBlock(0).setData(e,0,0)}setAttributeData(t,e){const s=S(t);if(this._ensureSizeForTexel(s),this._getBlock(0).setData(s,0,this.getFilterFlags(e)),this._targetType!==T(t))return;const r=this._attributeComputeInfo,a=this.config.supportsTextureFloat?1:2,n=4;let o=null;r&&(o=r.isSubtype?r.map.get(e.readAttribute(r.subtypeField)):r.map,o&&o.size&&o.forEach(((t,r)=>{const o=r*a%n,h=Math.floor(r*a/n),l=this._getBlock(h+_),u=t(e);if(this.config.supportsTextureFloat)l.setData(s,o,u);else if(u===f)l.setData(s,o,255),l.setData(s,o+1,255);else{const t=i(Math.round(u),-32767,32766)+32768,e=255&t,r=(65280&t)>>8;l.setData(s,o,e),l.setData(s,o+1,r)}})))}sendUpdates(){if(has("esri-2d-update-debug")&&console.debug("AttributeStore::sendUpdate"),this._notifyChange(),this._nextUpdate)return this._nextUpdate.promise;if(this._currUpdate)return this._nextUpdate=l(),this._nextUpdate.promise;const e={blocks:this._blocks.map((t=>r(t)?t.toMessage():null))};return this._currUpdate=this._createResources().then((()=>{const t=()=>{if(this._currUpdate=null,this._nextUpdate){const t=this._nextUpdate;this._nextUpdate=null,this.sendUpdates().then((()=>t.resolve()))}else has("esri-2d-update-debug")&&console.debug("AttributeStore::sendUpdate::No additional updates queued");this._notifyChange()};has("esri-2d-update-debug")&&console.debug("AttributeStore::sendUpdate::client.update");const i=this._client.update(e,this._signal).then(t).catch(t);return this._client.render(this._signal),i})).catch((e=>{if(u(e))return this._createResourcesPromise=null,this._createResources();this._notifyChange(),k.error(new t("mapview-attribute-store","Encountered an error during client update",e))})),this._currUpdate}_ensureSizeForTexel(t){for(;t>=this._size*this._size;)if(this._expand())return}_bindAttribute(t,e){function i(){const{normalizationField:e}=t;return e?i=>{const s=i.readAttribute(e);if(!s)return null;return i.readAttribute(t.field)/s}:e=>e.readAttribute(t.field)}function s(){return t.normalizationField&&k.warn("mapview-arcade","Ignoring normalizationField specified with an arcade expression which is not supported."),e=>e.getComputedNumericAtIndex(t.fieldIndex)}let r;if(null!=t.fieldIndex)r=s();else{if(!t.field)return;r=i()}const{valueRepresentation:a}=t;if(a){r=B(r,(t=>w(t,a)))}const n=t=>null===t||isNaN(t)||t===1/0||t===-1/0?f:t,o=this._attributeComputeInfo;if(o.isSubtype){const i=o.map.get(e)??new Map;i.set(t.binding,B(r,n)),o.map.set(e,i)}else o.map.set(t.binding,B(r,n))}_createResources(){if(r(this._createResourcesPromise))return this._createResourcesPromise;this._getBlock(y),this._getBlock(m),F("Initializing AttributeStore");const e={shared:I.sharedArrayBuffer&&!("local"===this._client.type),size:this._size,blocks:a(this._blocks,(t=>({textureOnly:t.textureOnly,buffer:t.buffer,pixelType:t.pixelType})))},i=this._client.initialize(e,this._signal).catch((e=>{u(e)?this._createResourcesPromise=null:k.error(new t("mapview-attribute-store","Encountered an error during client initialization",e))}));return this._createResourcesPromise=i,i.then((()=>s(this._createResourcesPromise)?this._createResources():void 0)),i}_getBlock(t){const e=this._blocks[t];if(r(e))return e;F(`Initializing AttributeBlock at index ${t}`);const i=I.sharedArrayBuffer,s=this._client.type,a=new C(i,s,this._size,this._blockDescriptors[t]);return this._blocks[t]=a,this._createResourcesPromise=null,a}_expand(){if(this._size<this.config.maxTextureSize){const t=this._size<<=1;return F("Expanding block size to",t,this._blocks),n(this._blocks,(e=>e.expand(t))),this._createResourcesPromise=null,this._size=t,0}return k.error(new t("mapview-limitations","Maximum number of onscreen features exceeded.")),-1}async _updateFilter(t,e,i,a){const n=this._filters[e],o=r(n)&&n.hash;if(!n&&!t)return!1;if(o===JSON.stringify(t))return!1;if(s(t)){if(!n)return!1;const t=1<<e+1,i=this._getBlock(0);return this._filters[e]=null,i.setComponentAllTexels(0,t),this.sendUpdates(),!0}const h=await this._getFilter(e,i);return await h.update(t,a),!0}async _getFilter(t,e){const i=this._filters[t];if(r(i))return i;const{default:s}=await import("./FeatureFilter.js"),a=new s({geometryType:e.geometryType,hasM:!1,hasZ:!1,timeInfo:e.timeInfo,fieldsIndex:new c(e.fields)});return this._filters[t]=a,a}isVisible(t){return!!(2&this._getBlock(0).getData(t,0))}getFilterFlags(t){let e=0;const i=z(t.getDisplayId());for(let a=0;a<this._filters.length;a++){const r=!!(i&1<<a),n=this._filters[a];e|=(!r||s(n)||n.check(t)?1:0)<<a}let r=0;if(this._idsToHighlight.size){const e=t.getObjectId();r=this.getHighlightFlag(e)}return e<<1|r}}export{M as default};