UNPKG

@arcgis/core

Version:

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

3 lines (2 loc) • 10.5 kB
/* COPYRIGHT Esri - https://js.arcgis.com/5.0.8/LICENSE.txt */ import{__decorate as e}from"tslib";import t from"../../../core/Error.js";import"../../../core/has.js";import i from"../../../core/Logger.js";import{property as r,subclass as s}from"../../../core/accessorSupport/decorators.js";import a from"../../../geometry/Extent.js";import n from"../../../geometry/Point.js";import o from"../../../geometry/SpatialReference.js";import f from"./BaseRaster.js";import l from"./DBFParser.js";import{parsePAMInfo as u,parseSpatialReference as c}from"./pamParser.js";import{parseSignature as h,getPyramidIFDs as p,getMaskIFDs as d,parseIFD as m,parseFieldValues as y,isBSQConfig as g,getImageInfo as x}from"../formats/TiffDecoder.js";import{tiffTags as w,geoKeys as I}from"../formats/tiffTag.js";import{estimateStatisticsFromHistograms as T}from"../functions/stretchUtils.js";import R from"../transforms/PolynomialTransform.js";import _ from"../../support/RasterInfo.js";import O from"../../support/RasterStorageInfo.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 E=class extends f{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}=h(s),f=[],l={fileChunk:s,posIFD:n,fileOffset:0};await this._readIFDs(f,l,a,o?8:4,r);const{imageInfo:u,rasterInfo:c}=k(f),m=p(f),y=d(f);if(this._headerInfo={littleEndian:a,isBigTiff:o,ifds:f,pyramidIFDs:m,maskIFDs:y,...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&&v(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,w,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=w,r=4,s){let{fileChunk:a,posIFD:n,fileOffset:o}=e;if(!e.fileChunk)return null;const f=m(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,I,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=>y(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);y(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;if(o!==y||f!==g){let e=x.mask;if(e)for(let t=0;t<g;t++){const i=t*y;if(t<f)for(let t=o;t<y;t++)e[i+t]=0;else for(let t=0;t<y;t++)e[i+t]=0}else{e=new Uint8Array(y*g),x.mask=e;for(let t=0;t<f;t++){const i=t*y;for(let t=0;t<o;t++)e[i+t]=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=g(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"),x=b(f,"IMAGELENGTH"),w=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 g=0;g<T.length;g++)if(null==T[g].from||!T[g].to||T[g].to<0)return null;return{ranges:T,ifd:f,actualTileWidth:i===m&&y%w||w,actualTileHeight:t===d&&x%I||I}}async _fetchAuxiliaryMetaData(e){try{const{data:t}=await this.request(this.url+".aux.xml",{responseType:"xml",signal:e?.signal});return u(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=l.parse(t);return i?.recordSet?F.fromJSON(i.recordSet):null}catch{return null}}};function k(e){const t=x(e),{width:i,height:r,tileWidth:s,tileHeight:f,planes:l,pixelType:u,compression:h,firstPyramidLevel:p,maximumPyramidLevel:d,pyramidBlockWidth:m,pyramidBlockHeight:y,pyramidResolutions:g,tileBoundary:w,affine:I,metadata:T}=t,F=t.extent.spatialReference?.wkt||t.extent.spatialReference?.wkid;let E=c(F),k=!!t.isPseudoGeographic;null==E&&(k=!0,E=new o({wkid:3857}));const v=new a({...t.extent,spatialReference:E}),D=new n(v?{x:v.xmin,y:v.ymax,spatialReference:E}:{x:0,y:0}),P=new O({blockWidth:s,blockHeight:f,pyramidBlockWidth:m,pyramidBlockHeight:y,compression:h,origin:D,firstPyramidLevel:p,maximumPyramidLevel:d,pyramidResolutions:g,blockBoundary:w}),B=new n({x:(v.xmax-v.xmin)/i,y:(v.ymax-v.ymin)/r,spatialReference:E}),z=T?{BandProperties:T.bandProperties,DataType:T.dataType}:{};let C=null;const L=b(e[0],"PHOTOMETRICINTERPRETATION"),j=S(e[0],"COLORMAP");if(L<=3&&j?.length>3&&j.length%3==0){C=[];const e=j.length/3;for(let t=0;t<e;t++)C.push([t,j[t]>>>8,j[t+e]>>>8,j[t+2*e]>>>8])}const A=new _({width:i,height:r,bandCount:l,pixelType:u,pixelSize:B,storageInfo:P,spatialReference:E,isPseudoSpatialReference:k,keyProperties:z,extent:v,colormap:C,statistics:T?T.statistics:null});if(I?.length&&(A.nativeExtent=new a({xmin:-.5,ymin:.5-r,xmax:i-.5,ymax:.5,spatialReference:E}),A.transform=new R({polynomialOrder:1,forwardCoefficients:[I[2]+I[0]/2,I[5]-I[3]/2,I[0],I[3],-I[1],-I[4]]}),A.extent=A.transform.forwardTransform(A.nativeExtent),A.pixelSize=new n({x:(v.xmax-v.xmin)/i,y:(v.ymax-v.ymin)/r,spatialReference:E}),P.origin.x=-.5,P.origin.y=.5),g){const{x:e,y:t}=A.pixelSize;g.forEach(i=>{i.x*=e,i.y*=t})}return{imageInfo:t,rasterInfo:A}}function v(e,t){if(t.statistics=e.statistics??t.statistics,t.histograms=e.histograms,e.histograms&&null==t.statistics&&(t.statistics=T(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()],E.prototype,"_files",void 0),e([r()],E.prototype,"_headerInfo",void 0),e([r()],E.prototype,"_bufferSize",void 0),e([r()],E.prototype,"_chunkSize",void 0),e([r({type:String,json:{write:!0}})],E.prototype,"datasetFormat",void 0),E=e([s("esri.layers.raster.datasets.TIFFRaster")],E);const D=E;export{D as default};