UNPKG

@arcgis/core

Version:

ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API

6 lines (5 loc) • 10.7 kB
/* All material copyright ESRI, All Rights Reserved, unless otherwise specified. See https://js.arcgis.com/4.33/esri/copyright.txt for details. */ import{_ as e}from"../../../chunks/tslib.es6.js";import t from"../../../core/Error.js";import"../../../core/has.js";import i from"../../../core/Logger.js";import{property as r}from"../../../core/accessorSupport/decorators/property.js";import"../../../core/RandomLCG.js";import{subclass as s}from"../../../core/accessorSupport/decorators/subclass.js";import a from"../../../geometry/Extent.js";import n from"../../../geometry/Point.js";import o from"../../../geometry/SpatialReference.js";import f from"../RasterInfo.js";import l from"../RasterStorageInfo.js";import u from"./BaseRaster.js";import c from"./DBFParser.js";import{parsePAMInfo as h,parseSpatialReference as p}from"./pamParser.js";import{parseSignature as d,getPyramidIFDs as m,getMaskIFDs as y,parseIFD as g,parseFieldValues as x,isBSQConfig as w,getImageInfo as I}from"../rasterFormats/TiffDecoder.js";import{tiffTags as T,geoKeys as R}from"../rasterFormats/tiffTag.js";import{estimateStatisticsFromHistograms as _}from"../rasterFunctions/stretchUtils.js";import O from"../rasterTransforms/PolynomialTransform.js";import F from"../../../rest/support/FeatureSet.js";const S=(e,t)=>e.get(t)?.values,b=(e,t)=>e.get(t)?.values?.[0];let k=class extends u{constructor(){super(...arguments),this._files=null,this._headerInfo=null,this._bufferSize=1048576,this._chunkSize=10485760,this.datasetFormat="TIFF"}async fetchRawTile(e,t,i,r={}){if(!this._headerInfo?.isSupported||this.isBlockOutside(e,t,i))return null;const s=await this._fetchRawTiffTile(e,t,i,!1,r);if(null!=s&&this._headerInfo.hasMaskBand){const a=await this._fetchRawTiffTile(e,t,i,!0,r);null!=a&&a.pixels[0]instanceof Uint8Array&&(s.mask=a.pixels[0])}return s}async _open(e){const r=e?e.signal:null,{data:s}=await this.request(this.url,{range:{from:0,to:this._bufferSize},responseType:"array-buffer",signal:r});if(!s)throw new t("tiffraster:open","failed to open url "+this.url);this.datasetName=this.url.slice(this.url.lastIndexOf("/")+1,this.url.lastIndexOf("."));const{littleEndian:a,firstIFDPos:n,isBigTiff:o}=d(s),f=[],l={fileChunk:s,posIFD:n,fileOffset:0};await this._readIFDs(f,l,a,o?8:4,r);const{imageInfo:u,rasterInfo:c}=E(f),h=m(f),p=y(f);if(this._headerInfo={littleEndian:a,isBigTiff:o,ifds:f,pyramidIFDs:h,maskIFDs:p,...u},this._set("rasterInfo",c),!u.isSupported)throw new t("tiffraster:open","this tiff is not supported: "+u.message);if(!u.tileWidth)throw new t("tiffraster:open","none-tiled tiff is not optimized for access, convert to COG and retry.");c.isPseudoSpatialReference&&i.getLogger(this).warn("The spatial reference for this tiff is unsupported. Only EPSG spatial reference codes and Esri WKTs are supported.");const g=f[0].get("PREDICTOR")?.values?.[0],x=f[0].get("SAMPLEFORMAT")?.values?.[0];if(3===x&&2===g)throw new t("tiffraster:open","unsupported horizontal difference encoding. Predictor=3 is supported for floating point data");const{skipMapInfo:w,skipExtensions:I=[]}=this.ioConfig;if(!I.includes("aux.xml")&&!w){const t=await this._fetchAuxiliaryMetaData(e);null!=t&&D(t,c)}I.includes("vat.dbf")||1!==c.bandCount||"u8"!==c.pixelType||w||(c.attributeTable=await this._fetchAuxiliaryTable(e),null!=c.attributeTable&&(c.keyProperties.DataType="thematic")),w&&this.updateImageSpaceRasterInfo(c),this.updateTileInfo()}async _validateOrFetchHeaderBuffer(e,t){let{fileChunk:i,fileOffset:r,posIFD:s}=e;if(s+8>=i.byteLength||s<0){r=s+r;i=(await this.request(this.url,{range:{from:r,to:r+this._bufferSize},responseType:"array-buffer",signal:t})).data,s=0}return{fileChunk:i,fileOffset:r,posIFD:s}}async _readIFDs(e,i,r,s=4,a){if(!i.posIFD)return null;i=await this._validateOrFetchHeaderBuffer(i,a);const n=await this._readIFD(i,r,T,s,a);if(!n?.ifd)throw new t("tiffraster:open","cannot parse tiff header. failed to open url "+this.url);if(e.push(n.ifd),!n.nextIFD)return null;i.posIFD=n.nextIFD-i.fileOffset,await this._readIFDs(e,i,r,s,a)}async _readIFD(e,t,i=T,r=4,s){let{fileChunk:a,posIFD:n,fileOffset:o}=e;if(!e.fileChunk)return null;const f=g(a,t,n,o,i,r);if(f.success){const e=[];if(f.ifd?.forEach((t=>{t.values||e.push(t)})),e.length>0&&await this._fillOffsets(e,t,f.nextIFD,s),f.ifd?.has("GEOKEYDIRECTORY")){const e=f.ifd.get("GEOKEYDIRECTORY"),i=e?.values;if(i&&i.length>4){const r=i[0]+"."+i[1]+"."+i[2];n=e.valueOffset+6-o;const f=await this._validateOrFetchHeaderBuffer({fileChunk:a,posIFD:n,fileOffset:o},s),l=await this._readIFD(f,t,R,2,s);e.data=l?.ifd,e.data&&e.data.set("GEOTIFFVersion",{id:0,type:2,valueCount:1,valueOffset:null,values:[r]})}}return f}if(f.requiredBufferSize){return a=(await this.request(this.url,{range:{from:o,to:o+n+f.requiredBufferSize+8},responseType:"array-buffer",signal:s})).data,a.byteLength<n+f.requiredBufferSize?null:(e.fileChunk=a,e.fileOffset=o,this._readIFD(e,t,i,r,s))}return null}async _fillOffsets(e,t,i,r){const s=e.filter((e=>null!=e.offlineOffsetSize));if(0===s.length)return;const a=s.map((e=>e.offlineOffsetSize)),n=Math.min.apply(null,a.map((e=>e[0]))),o=Math.max.apply(null,a.map((e=>e[0]+e[1])));let f=1===a.length||o-n<=this._bufferSize;if(!f&&a.length>1){a.sort(((e,t)=>e[0]-t[0]));f=a.reduce(((e,t)=>e===t[0]?t[0]+t[1]:0),a[0][0])===o}if(f){const e=await this._fetchOffsets(n,Math.max(o,n+this._bufferSize),r);return void s.forEach((i=>x(e,t,i,n)))}const l=s.map((async e=>{const i=e.offlineOffsetSize,s=await this._fetchOffsets(i[0],i[1]+i[0],r);x(s,t,e,i[0])}));await Promise.all(l)}async _fetchOffsets(e,t,i){const r=[],s=this._chunkSize,a=Math.ceil((t-e)/s);let n=e;for(let l=0;l<a;l++)r.push(this.request(this.url,{range:{from:n,to:l===a-1?t:n+s-1},responseType:"array-buffer",signal:i})),n+=s;const o=await Promise.all(r);if(1===a)return o[0].data;const f=new Uint8Array(t-e+1);for(let l=0;l<a;l++)f.set(new Uint8Array(o[l].data),l*s);return f.buffer}async _fetchRawTiffTile(e,t,i,r,s={}){const a=this._getTileLocation(e,t,i,r);if(!a)return null;const{ranges:n,actualTileWidth:o,actualTileHeight:f,ifd:l}=a,u=n.map((e=>this.request(this.url,{range:e,responseType:"array-buffer",signal:s.signal}))),c=await Promise.all(u),h=c.map((e=>e.data.byteLength)).reduce(((e,t)=>e+t)),p=1===c.length?c[0].data:new ArrayBuffer(h),d=[0],m=[0];if(c.length>1){const e=new Uint8Array(p);for(let t=0,i=0;t<c.length;t++){const r=c[t].data;e.set(new Uint8Array(r),i),d[t]=i,i+=r.byteLength,m[t]=r.byteLength}}const{blockWidth:y,blockHeight:g}=this.getBlockWidthHeight(e),x=await this.decodePixelBlock(p,{format:"tiff",customOptions:{headerInfo:this._headerInfo,ifd:l,offsets:d,sizes:m},width:y,height:g,planes:null,pixelType:null});if(null==x)return null;let w,I,T;if(o!==y||f!==g){let e=x.mask;if(e)for(w=0;w<g;w++)if(T=w*y,w<f)for(I=o;I<y;I++)e[T+I]=0;else for(I=0;I<y;I++)e[T+I]=0;else for(e=new Uint8Array(y*g),x.mask=e,w=0;w<f;w++)for(T=w*y,I=0;I<o;I++)e[T+I]=1}return x}_getTileLocation(e,t,i,r=!1){const{firstPyramidLevel:s,blockBoundary:a}=this.rasterInfo.storageInfo,n=0===e?0:e-(s-1),{_headerInfo:o}=this;if(!o)return null;const f=r?o.maskIFDs[n]:0===n?o?.ifds[0]:o?.pyramidIFDs[n-1];if(!f)return null;const l=w(f,o),u=S(f,"TILEOFFSETS");if(void 0===u)return null;const c=S(f,"TILEBYTECOUNTS"),{minRow:h,minCol:p,maxRow:d,maxCol:m}=a[n];if(t>d||i>m||t<h||i<p)return null;const y=b(f,"IMAGEWIDTH"),g=b(f,"IMAGELENGTH"),x=b(f,"TILEWIDTH"),I=b(f,"TILELENGTH"),T=[];if(l){const{bandCount:e}=this.rasterInfo;for(let r=0;r<e;r++){const e=r*(d+1)*(m+1)+t*(m+1)+i;T[r]={from:u[e],to:u[e]+c[e]-1}}}else{const e=t*(m+1)+i;T.push({from:u[e],to:u[e]+c[e]-1})}for(let w=0;w<T.length;w++)if(null==T[w].from||!T[w].to||T[w].to<0)return null;return{ranges:T,ifd:f,actualTileWidth:i===m&&y%x||x,actualTileHeight:t===d&&g%I||I}}async _fetchAuxiliaryMetaData(e){try{const{data:t}=await this.request(this.url+".aux.xml",{responseType:"xml",signal:e?.signal});return h(t)}catch{return null}}async _fetchAuxiliaryTable(e){try{const{data:t}=await this.request(this.url+".vat.dbf",{responseType:"array-buffer",signal:e?.signal}),i=c.parse(t);return i?.recordSet?F.fromJSON(i.recordSet):null}catch{return null}}};function E(e){const t=I(e),{width:i,height:r,tileWidth:s,tileHeight:u,planes:c,pixelType:h,compression:d,firstPyramidLevel:m,maximumPyramidLevel:y,pyramidBlockWidth:g,pyramidBlockHeight:x,pyramidResolutions:w,tileBoundary:T,affine:R,metadata:_}=t,F=t.extent.spatialReference?.wkt||t.extent.spatialReference?.wkid;let k=p(F),E=!!t.isPseudoGeographic;null==k&&(E=!0,k=new o({wkid:3857}));const D=new a({...t.extent,spatialReference:k}),v=new n(D?{x:D.xmin,y:D.ymax,spatialReference:k}:{x:0,y:0}),P=new l({blockWidth:s,blockHeight:u,pyramidBlockWidth:g,pyramidBlockHeight:x,compression:d,origin:v,firstPyramidLevel:m,maximumPyramidLevel:y,pyramidResolutions:w,blockBoundary:T}),B=new n({x:(D.xmax-D.xmin)/i,y:(D.ymax-D.ymin)/r,spatialReference:k}),C=_?{BandProperties:_.bandProperties,DataType:_.dataType}:{};let L=null;const z=b(e[0],"PHOTOMETRICINTERPRETATION"),j=S(e[0],"COLORMAP");if(z<=3&&j?.length>3&&j.length%3==0){L=[];const e=j.length/3;for(let t=0;t<e;t++)L.push([t,j[t]>>>8,j[t+e]>>>8,j[t+2*e]>>>8])}const A=new f({width:i,height:r,bandCount:c,pixelType:h,pixelSize:B,storageInfo:P,spatialReference:k,isPseudoSpatialReference:E,keyProperties:C,extent:D,colormap:L,statistics:_?_.statistics:null});if(R?.length&&(A.nativeExtent=new a({xmin:-.5,ymin:.5-r,xmax:i-.5,ymax:.5,spatialReference:k}),A.transform=new O({polynomialOrder:1,forwardCoefficients:[R[2]+R[0]/2,R[5]-R[3]/2,R[0],R[3],-R[1],-R[4]]}),A.extent=A.transform.forwardTransform(A.nativeExtent),A.pixelSize=new n({x:(D.xmax-D.xmin)/i,y:(D.ymax-D.ymin)/r,spatialReference:k}),P.origin.x=-.5,P.origin.y=.5),w){const{x:e,y:t}=A.pixelSize;w.forEach((i=>{i.x*=e,i.y*=t}))}return{imageInfo:t,rasterInfo:A}}function D(e,t){if(t.statistics=e.statistics??t.statistics,t.histograms=e.histograms,e.histograms&&null==t.statistics&&(t.statistics=_(e.histograms)),e.transform&&null==t.transform){t.transform=e.transform,t.nativeExtent=t.extent;const i=t.transform.forwardTransform(t.nativeExtent);t.pixelSize=new n({x:(i.xmax-i.xmin)/t.width,y:(i.ymax-i.ymin)/t.height,spatialReference:t.spatialReference}),t.extent=i}t.isPseudoSpatialReference&&e.spatialReference&&(t.spatialReference=e.spatialReference,t.extent.spatialReference=t.nativeExtent.spatialReference=t.storageInfo.origin.spatialReference=t.spatialReference)}e([r()],k.prototype,"_files",void 0),e([r()],k.prototype,"_headerInfo",void 0),e([r()],k.prototype,"_bufferSize",void 0),e([r()],k.prototype,"_chunkSize",void 0),e([r({type:String,json:{write:!0}})],k.prototype,"datasetFormat",void 0),k=e([s("esri.layers.support.rasterDatasets.TIFFRaster")],k);const v=k;export{v as default};