UNPKG

flatgeobuf

Version:

A performant binary encoding for geographic data

2 lines 3.52 kB
import{Repeater as e}from"@repeaterjs/repeater";import*as t from"flatbuffers";import r from"./config.js";import{magicbytes as a,SIZE_PREFIX_LEN as s}from"./constants.js";import{Feature as n}from"./flat-geobuf/feature.js";import{fromByteBuffer as i}from"./header-meta.js";import{calcTreeSize as h,DEFAULT_NODE_SIZE as o,NODE_ITEM_BYTE_LEN as l,streamSearch as d}from"./packedrtree.js";export class HttpReader{headerClient;header;headerLength;indexLength;nocache;headers;constructor(e,t,r,a,s,n={}){this.headerClient=e,this.header=t,this.headerLength=r,this.indexLength=a,this.nocache=s,this.headers=n}static async open(e,r,n={}){let d,u=new f(e,r,n),g=2024+(()=>{let e,t=0;for(e=0;e<3;e++)t+=o**e*l;return t})();if(!new Uint8Array(await u.getRange(0,8,g,"header")).subarray(0,3).every((e,t)=>a[t]===e))throw Error("Not a FlatGeobuf file");if((d=new DataView(await u.getRange(8,4,g,"header")).getUint32(0,!0))>0xa00000||d<8)throw Error("Invalid header size");let c=await u.getRange(a.length,s+d,g,"header"),w=i(new t.ByteBuffer(new Uint8Array(c)));if(0===w.indexNodeSize)throw Error("No index found, cannot read features filtered by bbox");let y=h(w.featuresCount,w.indexNodeSize);return new HttpReader(u,w,d,y,r,n)}async *selectBbox(t){let a=this.lengthBeforeTree(),s=this.headerClient,n=async(e,t)=>s.getRange(a+e,t,0,"index"),i=[],h=[];for await(let e of d(this.header.featuresCount,this.header.indexNodeSize,t,n)){let[t,a]=e,[,,s]=e;if(s||(s=4),0===h.length){h.push([t,s,a]);continue}let n=h[h.length-1];t-(n[0]+n[1])>r.global.extraRequestThreshold()&&(i.push(h),h=[]),h.push([t,s,a])}this.headerClient.logUsage("header+index"),h.length>0&&i.push(h);let o=i.flatMap(e=>this.readFeatureBatch(e,this.nocache));yield*e.merge(o)}lengthBeforeTree(){return a.length+s+this.headerLength}lengthBeforeFeatures(){return this.lengthBeforeTree()+this.indexLength}buildFeatureClient(e){return new f(this.headerClient.httpClient,e,this.headers)}async *readFeatureBatch(e,t){let[r]=e[0],[a,s]=e[e.length-1],n=this.buildFeatureClient(t),i=a+s-r;for(let[t,,r]of e){let e=await this.readFeature(n,t,i);yield{id:r,feature:e},i=0}n.logUsage("feature")}async readFeature(e,r,a){let i,h=r+this.lengthBeforeFeatures();i=new DataView(await e.getRange(h,4,a,"feature length")).getUint32(0,!0);let o=new Uint8Array(await e.getRange(h+4,i,a,"feature data")),l=new Uint8Array(i+s);l.set(o,s);let d=new t.ByteBuffer(l);return n.getSizePrefixedRootAsFeature(d)}}class f{httpClient;bytesEverUsed=0;bytesEverFetched=0;buffer=new ArrayBuffer(0);head=0;constructor(e,t,r={}){if("string"==typeof e)this.httpClient=new HttpRangeClient(e,t,r);else if(e instanceof HttpRangeClient)this.httpClient=e;else throw Error("Unknown source")}async getRange(e,t,r,a){this.bytesEverUsed+=t;let s=e-this.head,n=s+t;if(s>=0&&n<=this.buffer.byteLength)return this.buffer.slice(s,n);let i=Math.max(t,r);return this.bytesEverFetched+=i,this.buffer=await this.httpClient.getRange(e,i,a),this.head=e,this.buffer.slice(0,t)}logUsage(e){e.split(" ")[0],(100*this.bytesEverUsed/this.bytesEverFetched).toFixed(2)}}export class HttpRangeClient{url;nocache;headers;requestsEverMade=0;bytesEverRequested=0;constructor(e,t,r={}){this.url=e,this.nocache=t,this.headers=r}async getRange(e,t,r){this.requestsEverMade+=1,this.bytesEverRequested+=t;let a=`bytes=${e}-${e+t-1}`,s=new Headers(this.headers);s.set("Range",a),this.nocache&&s.set("Cache-Control","no-cache, no-store");let n=await fetch(this.url,{headers:s});return await n.arrayBuffer()}} //# sourceMappingURL=http-reader.js.map