index.js (4276B)
1 import {Cache} from '~/lib/cache'; 2 import {XHRQueue} from '~/lib/xhr-promise'; 3 4 class TiledDataLoader { 5 constructor(cacheSize = 50) { 6 this._cache = new Cache(cacheSize); 7 this._pendingRequests = {}; 8 this._xhrQueue = new XHRQueue(); 9 } 10 11 makeTileKey(coords) { 12 return `${coords.x}:${coords.y}:${coords.z}`; 13 } 14 15 getFromCache(dataTileCoords) { 16 const key = this.makeTileKey(dataTileCoords); 17 const res = this._cache.get(key); 18 res['coords'] = dataTileCoords; 19 return res; 20 } 21 22 layerTileToDataTileCoords(layerTileCoords) { 23 return {...layerTileCoords}; 24 } 25 26 makeRequestData(_unused_dataTileCoords) { 27 throw new Error('Not implemented'); 28 // return { 29 // url, 30 // options 31 // } 32 } 33 34 processResponse(_unused_xhr, _unused_originalDataTileCoords) { 35 throw new Error('Not implemented'); 36 // return { 37 // tileData, 38 // coords 39 // } 40 } 41 42 calcAdjustment(layerTileCoords, dataTileCoords) { 43 if (layerTileCoords.x === dataTileCoords.x && 44 layerTileCoords.y === dataTileCoords.y && 45 layerTileCoords.z === dataTileCoords.z) { 46 return null; 47 } 48 if (dataTileCoords.z > layerTileCoords.z) { 49 const multiplier = 1 << (dataTileCoords.z - layerTileCoords.z); 50 return {multiplier: 1 / multiplier, offsetX: 0, offsetY: 0}; 51 } 52 const multiplier = 1 << (layerTileCoords.z - dataTileCoords.z); 53 return { 54 multiplier, 55 offsetX: (layerTileCoords.x - dataTileCoords.x * multiplier), 56 offsetY: (layerTileCoords.y - dataTileCoords.y * multiplier) 57 }; 58 } 59 60 requestTileData(layerTileCoords) { 61 const dataTileCoords = this.layerTileToDataTileCoords(layerTileCoords); 62 let res = this.getFromCache(dataTileCoords); 63 if (res.found) { 64 return { 65 dataPromise: Promise.resolve({ 66 coords: res.coords, 67 tileData: res.value, 68 adjustment: this.calcAdjustment(layerTileCoords, res.coords) 69 } 70 ), 71 abortLoading: () => { 72 // no need to abort Promise which is resolved immediately 73 } 74 }; 75 } 76 77 const dataTileKey = this.makeTileKey(dataTileCoords); 78 if (!(dataTileKey in this._pendingRequests)) { 79 const {url, options} = this.makeRequestData(dataTileCoords); 80 const fetchPromise = this._xhrQueue.put(url, options); 81 const dataPromise = (async() => { 82 let xhr; 83 try { 84 xhr = await fetchPromise; 85 } catch (e) { 86 return {error: e}; 87 } finally { 88 delete this._pendingRequests[dataTileKey]; 89 } 90 const {tileData, coords} = await this.processResponse(xhr, dataTileCoords); 91 this._cache.put(this.makeTileKey(coords), tileData); 92 return {tileData, coords}; 93 })(); 94 const pendingRequest = this._pendingRequests[dataTileKey] = { 95 dataPromise, 96 refCount: 0 97 }; 98 pendingRequest.abortLoading = () => { 99 pendingRequest.refCount -= 1; 100 if (pendingRequest.refCount < 1) { 101 fetchPromise.abort(); 102 delete this._pendingRequests[dataTileKey]; 103 } 104 }; 105 } 106 107 let pendingRequest = this._pendingRequests[dataTileKey]; 108 pendingRequest.refCount += 1; 109 110 return { 111 dataPromise: pendingRequest.dataPromise.then((data) => { 112 if (data.error) { 113 return data; 114 } 115 return { 116 coords: data.coords, 117 tileData: data.tileData, 118 adjustment: this.calcAdjustment(layerTileCoords, data.coords) 119 }; 120 }), 121 abortLoading: () => pendingRequest.abortLoading() 122 }; 123 } 124 } 125 126 export {TiledDataLoader};