weatherlayers-gl
Version:
WeatherLayers GL - Weather Visualization Layers for deck.gl
15 lines (13 loc) • 11.4 kB
JavaScript
/*!
* Copyright (c) 2021-2026 WeatherLayers.com
*
* WeatherLayers Cloud Client 2026.5.1
*
* A valid access token is required to use the library. Contact support@weatherlayers.com for details.
*
* Homepage - https://weatherlayers.com/
* Demo - https://demo.weatherlayers.com/
* Docs - https://docs.weatherlayers.com/
* WeatherLayers Cloud Terms of Use - https://weatherlayers.com/terms-of-use.html
*/
;var t=require("cpt2js");const e="2026.5.1",a=new Map;const n=new Map;const i=new class{constructor(){this.queue=Promise.resolve()}async run(t){const e=this.queue;let a;this.queue=new Promise((t=>{a=t}));try{return await e,await t()}finally{a()}}};async function r(t,e){const n=await async function(t){if(a.has(t))return a.get(t);try{if("geotiff"===t)return await import("geotiff")}catch(e){throw new Error(`Optional dependency '${t}' is missing, install it with a package manager or provide with \`setLibrary('${t}', library)\``,{cause:e})}}("geotiff");let i;try{i=await n.fromUrl(t,{allowFullFile:!0,blockSize:Number.MAX_SAFE_INTEGER,fetch:(t,a)=>fetch(t,{...a,headers:{...a?.headers,...e?.headers}})},e?.signal)}catch(e){throw new Error(`Image ${t} can't be decoded.`,{cause:e})}const r=await i.getImage(0),s=await r.readRasters({interleave:!0,signal:e?.signal});if(!(s instanceof Uint8Array||s instanceof Uint8ClampedArray||s instanceof Float32Array))throw new Error("Unsupported data format");const o=function(t,e){if(null==e)return t;const a=t.slice(0);for(let t=0;t<a.length;t++)Math.abs(a[t]-e)<2*Number.EPSILON&&(a[t]=NaN);return a}(s,r.getGDALNoData());return{data:o,width:r.getWidth(),height:r.getHeight()}}function s(t){return async(e,a)=>{if(!1===a?.cache)return t(e);const i=a?.cache??n,r=e+(a?.headers?":"+JSON.stringify(a?.headers):""),s=i.get(r);if(s)return s;const o={...a,cache:void 0},c=t(e,o);return i.set(r,c),c.then((t=>{i.set(r,t)})),c}}const o=s((async(t,e)=>{if(t.includes(".png")||t.includes(".webp")||t.includes("image/png")||t.includes("image/webp"))return async function(t,e){let a;if(e?.headers||e?.signal){const n=await fetch(t,{headers:e.headers,signal:e.signal});if(!n.ok)throw new Error(`URL ${t} can't be loaded. Status: ${n.status}`);const i=await n.blob();a=URL.createObjectURL(i)}const n=new Image;try{await new Promise(((e,i)=>{n.addEventListener("load",e),n.addEventListener("error",i),n.crossOrigin="anonymous",n.src=a??t}))}catch(e){throw new Error(`URL ${t} can't be loaded.`,{cause:e})}finally{a&&URL.revokeObjectURL(a)}try{await i.run((()=>n.decode()))}catch(e){throw new Error(`Image ${t} can't be decoded.`,{cause:e})}const r=document.createElement("canvas");r.width=n.width,r.height=n.height;const s=r.getContext("2d");s.drawImage(n,0,0);const o=s.getImageData(0,0,r.width,r.height),{data:c,width:l,height:h}=o;return{data:c,width:l,height:h}}(t,e);if(t.includes(".tif")||t.includes("image/tif"))return r(t,e);throw new Error("Unsupported data format")})),c=s((async(t,e)=>{const a=await fetch(t,{headers:e?.headers});if(!a.ok)throw new Error(`URL ${t} can't be loaded. Status: ${a.status}`);return a.json()}));function l(t,e,a){if(!e){if(t===a)return 0;throw new Error("Invalid state")}if(a<=t)return 0;if(a>=e)return 1;{const n=new Date(t),i=new Date(e);return(new Date(a).getTime()-n.getTime())/(i.getTime()-n.getTime())}}function h(t,e){return[...t].reverse().find((t=>t<=e))}function d(t,e){return t.find((t=>t>=e))}function m(t,e){const a=new Date(t);return new Date(a.getTime()+1e3*e*60*60).toISOString()}const u={METRIC:"METRIC",METRIC_KILOMETERS:"METRIC_KILOMETERS",IMPERIAL:"IMPERIAL",NAUTICAL:"NAUTICAL"},f="producer",g="processor",w="item",p="data",D="search",S="palette",_=u.METRIC;function C(t,e){const a=t.providers.find((t=>t.roles.includes(f))),n=t.providers.find((t=>t.roles.includes(g)));return[...a?[`<a href="${a.url}"${e?` class="${e}"`:""}>${a.name}</a>`]:[],...n?[`<a href="${n.url}"${e?` class="${e}"`:""}>${n.name}</a>`]:[]].join(" via ")}function y(t,e){const a=t["weatherLayers:units"],n=a.find((t=>t.system===e))??a.find((t=>t.system===_))??a[0],{unit:i,scale:r,offset:s,decimals:o}=n;return{unit:i,scale:r,offset:s,decimals:o}}Object.defineProperty(exports,"colorRampCanvas",{enumerable:!0,get:function(){return t.colorRampCanvas}}),Object.defineProperty(exports,"parsePalette",{enumerable:!0,get:function(){return t.parsePalette}}),exports.Client=class{constructor(t){this._cache=new Map,this._datasetStacCollectionCache=new Map,this._datasetDataStacItemCache=new Map,this._config=t}getConfig(){return{...this._config}}setConfig(t){this._config=t}updateConfig(t){this.setConfig({...this._config,...t})}_getAuthenticatedUrl(t,a={}){const n=a.accessToken??this._config.accessToken??null,i=new URL(t);return i.searchParams.has("access_token")||null==n||i.searchParams.set("access_token",n),i.searchParams.has("version")||i.searchParams.set("version",e),i.toString()}_cacheDatasetStacCollection(t){this._datasetStacCollectionCache.set(t.id,t)}_cacheDatasetDataStacItem(t,e){this._datasetDataStacItemCache.has(t)||this._datasetDataStacItemCache.set(t,new Map),this._datasetDataStacItemCache.get(t).set(e.properties.datetime,e)}async _loadStacCatalog(t={}){const e=t.url??this._config.url??"https://catalog.weatherlayers.com",a=this._getAuthenticatedUrl(`${e}/catalog`,t);return await c(a,{cache:this._cache})}async _loadDatasetStacCollections(t={}){const e=(await this._loadStacCatalog(t)).links.find((t=>t.rel===p));if(!e)throw new Error("STAC Catalog data link not found");const a=this._getAuthenticatedUrl(e.href,t),n=(await c(a,{cache:this._cache})).collections;for(const t of n)this._cacheDatasetStacCollection(t);return n}async _loadDatasetStacCollection(t,e={}){await this._loadDatasetStacCollections(e);let a=this._datasetStacCollectionCache.get(t);if(!a)throw new Error(`STAC Collection ${t} not found`);return this._cacheDatasetStacCollection(a),a}async _loadDatasetStacCollectionPalette(t,e={}){const a=await this._loadDatasetStacCollection(t,e),n=Object.values(a.assets??{}).find((t=>t.roles.includes(S)&&"application/json"===t.type));if(!n)throw new Error(`STAC Collection ${t} palette asset not found`);const i=this._getAuthenticatedUrl(n.href,this._config);return await c(i,{cache:this._cache})}async _searchDatasetDataStacItems(t,e,a,n={}){const i=(await this._loadStacCatalog(n)).links.find((t=>t.rel===D));if(!i)throw new Error("STAC Catalog search link not found");const r=new URL(i.href);r.searchParams.set("collections",t),r.searchParams.set("datetime",function(t){if(Array.isArray(t)&&2===t.length){const[e,a]=t;return`${e??".."}/${a??".."}`}throw new Error("Invalid datetime range")}(e)),"number"==typeof a&&a>1&&r.searchParams.set("datetime_step",`${a}`);const s=this._getAuthenticatedUrl(r.toString(),n),o=(await c(s,{cache:this._cache})).features;for(const e of o)this._cacheDatasetDataStacItem(t,e);return o}async _loadDatasetDataStacItem(t,e,a={}){const n=a.datetimeStep??this._config.datetimeStep??1;let i=this._datasetDataStacItemCache.get(t)?.get(e);if(!i){i=(await this._searchDatasetDataStacItems(t,[e,e],n,a))[0]}if(!i)throw new Error(`STAC Item ${t}/${e} not found`);return i}async _loadStacItemData(t,e={}){const a=e.dataFormat??this._config.dataFormat??"byte.webp",n=t.assets[`data.${a}`];if(!n)throw new Error("STAC Item data asset not found");const i=this._getAuthenticatedUrl(n.href,this._config),r=await o(i,{cache:this._cache,signal:e.signal});return{datetime:t.properties.datetime,referenceDatetime:t.properties["forecast:reference_datetime"],horizon:t.properties["forecast:horizon"],image:r}}async _loadDatasetDataStacItemDataNow(t,e={}){const a=(await this._loadDatasetStacCollection(t,e)).links.find((t=>t.rel===w&&"!now"===t.datetime));if(!a)throw new Error("STAC Collection now item link not found");const n=this._getAuthenticatedUrl(a.href,this._config),i=await c(n,{cache:this._cache});return await this._loadStacItemData(i,e)}async _loadDatasetDataStacItemData(t,e,a={}){const n=await this._loadDatasetDataStacItem(t,e);return await this._loadStacItemData(n,a)}async loadCatalog(t={}){return(await this._loadDatasetStacCollections(t)).map((t=>t.id))}async loadDataset(t,e={}){const a=await this._loadDatasetStacCollection(t,e),n=e.unitSystem??this._config.unitSystem??_,i=e.attributionLinkClass??this._config.attributionLinkClass??"";return{title:a.title,unitFormat:y(a,n),attribution:C(a,i),bounds:a.extent.spatial.bbox[0],datetimeRange:a.extent.temporal.interval[0],datetimes:a.links.filter((t=>t.rel===w)).map((t=>t.datetime)).filter((t=>!!t)),palette:await this._loadDatasetStacCollectionPalette(t),layers:a["weatherLayers:layers"],controls:a["weatherLayers:controls"]}}async loadDatasetSlice(t,e,a={}){const n=a.datetimeStep??this._config.datetimeStep??1;return{datetimes:(await this._searchDatasetDataStacItems(t,e,n,a)).map((t=>t.properties.datetime))}}async loadDatasetData(t,e,a={}){const n=a.datetimeStep??this._config.datetimeStep??1,i=a.datetimeInterpolate??this._config.datetimeInterpolate??!1,r=await this._loadDatasetStacCollection(t,a);if(!e){const e=await this._loadDatasetDataStacItemDataNow(t,a);return{datetime:e.datetime,referenceDatetime:e.referenceDatetime,horizon:e.horizon,image:e.image,datetime2:null,referenceDatetime2:null,horizon2:null,image2:null,imageWeight:0,imageType:r["weatherLayers:imageType"],imageUnscale:e.image.data instanceof Uint8Array||e.image.data instanceof Uint8ClampedArray?r["weatherLayers:imageUnscale"]:null,bounds:r.extent.spatial.bbox[0]}}let s=this._datasetDataStacItemCache.has(t)?Array.from(this._datasetDataStacItemCache.get(t).values()):[],o=s.map((t=>t.properties.datetime)).sort();if((!o.length||o[0]>e||o[o.length-1]<e)&&(s=await this._searchDatasetDataStacItems(t,[e,e],n,a),o=s.map((t=>t.properties.datetime)).sort()),!o.length)throw new Error(`STAC Item ${t}/${e} not found`);const c=h(o,e),m=d(o,e);let u,f;if(i&&c&&m&&c!==m)u=c,f=m;else{if(!c)throw new Error(`STAC Item ${t}/${e} not found`);u=c,f=null}const[g,w]=await Promise.all([this._loadDatasetDataStacItemData(t,u,a),i&&f?this._loadDatasetDataStacItemData(t,f,a):null]);return{datetime:g.datetime,referenceDatetime:g.referenceDatetime,horizon:g.horizon,image:g.image,datetime2:w?w.datetime:null,referenceDatetime2:w?w.referenceDatetime:null,horizon2:w?w.horizon:null,image2:w?w.image:null,imageWeight:w?l(g.datetime,w.datetime,e):0,imageType:r["weatherLayers:imageType"],imageUnscale:g.image.data instanceof Uint8Array||g.image.data instanceof Uint8ClampedArray?r["weatherLayers:imageUnscale"]:null,bounds:r.extent.spatial.bbox[0]}}},exports.DATETIME="2026-05-11T19:53:53.176Z",exports.ImageType={SCALAR:"SCALAR",VECTOR:"VECTOR"},exports.UnitSystem=u,exports.VERSION=e,exports.formatDatetime=function(t){if(!t)return t;const e=new Date(t);return e.getDate()?`${e.getUTCFullYear()}/${`${e.getUTCMonth()+1}`.padStart(2,"0")}/${`${e.getUTCDate()}`.padStart(2,"0")} ${`${e.getUTCHours()}`.padStart(2,"0")}:${`${e.getUTCMinutes()}`.padStart(2,"0")} UTC`:t},exports.getClosestEndDatetime=d,exports.getClosestStartDatetime=h,exports.getDatetimeWeight=l,exports.interpolateDatetime=function(t,e,a){if(!e){if(0===a)return t;throw new Error("Invalid state")}if(a<=0)return t;if(a>=1)return e;{const n=new Date(t),i=new Date(e);return new Date(n.getTime()+(i.getTime()-n.getTime())*a).toISOString()}},exports.loadJson=c,exports.loadTextureData=o,exports.offsetDatetime=m,exports.offsetDatetimeRange=function(t,e,a){return[m(t,e),m(t,a)]},exports.setLibrary=function(t,e){a.set(t,e)};