UNPKG

flatgeobuf

Version:

A performant binary encoding for geographic data

2 lines 3.47 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 i}from"./flat-geobuf/feature.js";import{fromByteBuffer as h}from"./header-meta.js";import{calcTreeSize as n,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,i={}){this.headerClient=e,this.header=t,this.headerLength=r,this.indexLength=a,this.nocache=s,this.headers=i}static async open(e,r,s={}){let i,d=new f(e,r,s),u=2024+(()=>{let e,t=0;for(e=0;e<3;e++)t+=o**e*l;return t})();if(!new Uint8Array(await d.getRange(0,8,u,"header")).subarray(0,3).every((e,t)=>a[t]===e))throw Error("Not a FlatGeobuf file");if((i=new DataView(await d.getRange(8,4,u,"header")).getUint32(0,!0))>0xa00000||i<8)throw Error("Invalid header size");let g=await d.getRange(12,i,u,"header"),c=h(new t.ByteBuffer(new Uint8Array(g)));if(0===c.indexNodeSize)throw Error("No index found, cannot read features filtered by bbox");let w=n(c.featuresCount,c.indexNodeSize);return new HttpReader(d,c,i,w,r,s)}async *selectBbox(t){let a=this.lengthBeforeTree(),s=this.headerClient,i=async(e,t)=>s.getRange(a+e,t,0,"index"),h=[],n=[];for await(let e of d(this.header.featuresCount,this.header.indexNodeSize,t,i)){let[t,a]=e,[,,s]=e;if(s||(s=4),0===n.length){n.push([t,s,a]);continue}let i=n[n.length-1];t-(i[0]+i[1])>r.global.extraRequestThreshold()&&(h.push(n),n=[]),n.push([t,s,a])}this.headerClient.logUsage("header+index"),n.length>0&&h.push(n);let o=h.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],i=this.buildFeatureClient(t),h=a+s-r;for(let[t,,r]of e){let e=await this.readFeature(i,t,h);yield{id:r,feature:e},h=0}i.logUsage("feature")}async readFeature(e,r,a){let h,n=r+this.lengthBeforeFeatures();h=new DataView(await e.getRange(n,4,a,"feature length")).getUint32(0,!0);let o=new Uint8Array(await e.getRange(n+4,h,a,"feature data")),l=new Uint8Array(h+s);l.set(o,s);let d=new t.ByteBuffer(l);return d.setPosition(s),i.getRootAsFeature(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 u(e,t,r);else if(e instanceof u)this.httpClient=e;else throw Error("Unknown source")}async getRange(e,t,r,a){this.bytesEverUsed+=t;let s=e-this.head,i=s+t;if(s>=0&&i<=this.buffer.byteLength)return this.buffer.slice(s,i);let h=Math.max(t,r);return this.bytesEverFetched+=h,this.buffer=await this.httpClient.getRange(e,h,a),this.head=e,this.buffer.slice(0,t)}logUsage(e){e.split(" ")[0],(100*this.bytesEverUsed/this.bytesEverFetched).toFixed(2)}}class u{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 i=await fetch(this.url,{headers:s});return await i.arrayBuffer()}} //# sourceMappingURL=http-reader.js.map