UNPKG

@yaga/indexed-db-tile-cache

Version:

Spatial tile cache that saves its data into the IndexedDB of your browser

238 lines 11.4 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); var tile_utils_1 = require("@yaga/tile-utils"); var buffer_1 = require("buffer"); var events_1 = require("events"); var request = require("request"); var consts_1 = require("./consts"); /** * Class for a spatial-tile-cache that stores its data in the browsers IndexedDB */ var IndexedDbTileCache = /** @class */ (function (_super) { __extends(IndexedDbTileCache, _super); function IndexedDbTileCache(options) { if (options === void 0) { options = {}; } var _this = _super.call(this) || this; _this.options = options; _this.options.databaseName = _this.options.databaseName || consts_1.DEFAULT_DATABASE_NAME; _this.options.databaseVersion = _this.options.databaseVersion || consts_1.DEFAULT_DATABASE_VERSION; _this.options.objectStoreName = _this.options.objectStoreName || consts_1.DEFAULT_OBJECT_STORE_NAME; _this.options.tileUrl = _this.options.tileUrl || consts_1.DEFAULT_TILE_URL; _this.options.tileUrlSubDomains = _this.options.tileUrlSubDomains || consts_1.DEFAULT_TILE_URL_SUB_DOMAINS; _this.options.crawlDelay = _this.options.crawlDelay || consts_1.DEFAULT_CRAWL_DELAY; _this.options.maxAge = _this.options.maxAge || consts_1.DEFAULT_MAX_AGE; // Create the store if it does not exists... var dbRequest = indexedDB.open(_this.options.databaseName, _this.options.databaseVersion); dbRequest.addEventListener("upgradeneeded", function (dbEvent) { /** * Fired event from IndexedDB to give the possibility to enhance something on the store * @event IndexedDbTileCache#upgradeneeded */ _this.emit("upgradeneeded", dbEvent); var database = dbEvent.target.result; database.createObjectStore(_this.options.objectStoreName, { keyPath: "url" }); }); dbRequest.addEventListener("error", function (dbEvent) { /** * Piping the error event * @event IndexedDbTileCache#upgradeneeded */ _this.emit("error", dbEvent.target.error); }); return _this; } /** * Get the internal tile entry from the database with all its additional meta information. * * If the tile is marked as outdated by the `IIndexedDbTileCacheOptions.maxAge` property, it tries to download it * again. On any error it will provide the cached version. * * If you pass `true` as parameter for the `downloadIfUnavaiable` argument, it tries to dowenload a tile if it is * not stored already. */ IndexedDbTileCache.prototype.getTileEntry = function (tileCoordinates, downloadIfUnavaiable) { var _this = this; var dbRequest = indexedDB.open(this.options.databaseName, this.options.databaseVersion); return new Promise(function (resolve, reject) { dbRequest.addEventListener("success", function (dbEvent) { var database = dbEvent.target.result; var tx = database.transaction([_this.options.objectStoreName]) .objectStore(_this.options.objectStoreName).get(_this.createInternalTileUrl(tileCoordinates)); tx.addEventListener("success", function (event) { if (!event.target.result) { if (downloadIfUnavaiable) { return _this.downloadTile(tileCoordinates).then(resolve, reject); } return reject(new Error("Unable to find entry")); } var tileEntry = event.target.result; // Make a buffer from UInt8Array to get additional methods if (!(tileEntry.data instanceof buffer_1.Buffer)) { tileEntry.data = new buffer_1.Buffer(tileEntry.data); } if (tileEntry.timestamp < Date.now() - _this.options.maxAge) { return _this.downloadTile(tileCoordinates).catch(function () { // Not available so keep cached version... return resolve(tileEntry); }).then(resolve); } resolve(tileEntry); }); tx.addEventListener("error", function (event) { _this.emit("error", dbEvent.target.error); reject(event.target.error); }); }); dbRequest.addEventListener("error", function (dbEvent) { _this.emit("error", dbEvent.target.error); reject(dbEvent.target.error); }); }); }; /** * Creates an internal tile url from the url template from IIndexedDbTileCacheOptions * * It keeps the sub-domain placeholder to provide unique database entries while seeding from multiple sub-domains. */ IndexedDbTileCache.prototype.createInternalTileUrl = function (tileCoordinates) { return this.options.tileUrl .split(/{x}/).join(tileCoordinates.x.toString()) .split(/{y}/).join(tileCoordinates.y.toString()) .split(/{z}/).join(tileCoordinates.z.toString()); }; /** * Creates a real tile url from the url template from IIndexedDbTileCacheOptions */ IndexedDbTileCache.prototype.createTileUrl = function (tileCoordinates) { var randomSubDomain = this.options .tileUrlSubDomains[Math.floor(Math.random() * this.options.tileUrlSubDomains.length)]; return this.createInternalTileUrl(tileCoordinates) .split(/{s}/).join(randomSubDomain); }; /** * Receive a tile as an Uint8Array / Buffer */ IndexedDbTileCache.prototype.getTileAsBuffer = function (tileCoordinates) { return this.getTileEntry(tileCoordinates, true).then(function (tileEntry) { return Promise.resolve(tileEntry.data); }); }; /** * Receives a tile as its base64 encoded data url. */ IndexedDbTileCache.prototype.getTileAsDataUrl = function (tileCoordinates) { return this.getTileEntry(tileCoordinates, true).then(function (tileEntry) { return Promise.resolve("data:" + tileEntry.contentType + ";base64," + tileEntry.data.toString("base64")); }); }; /** * Download a specific tile by its coordinates and store it within the indexed-db */ IndexedDbTileCache.prototype.downloadTile = function (tileCoordinates) { var _this = this; var buffers = []; return new Promise(function (resolve, reject) { var contentType = ""; request.get(_this.createTileUrl(tileCoordinates)) .on("data", function (chunk) { buffers.push(chunk); }) .on("response", function (response) { contentType = response.headers["content-type"]; }) .on("error", reject) .on("end", function () { var dbRequest = indexedDB.open(_this.options.databaseName, _this.options.databaseVersion); var tileCacheEntry = { contentType: contentType, data: buffer_1.Buffer.concat(buffers), timestamp: Date.now(), url: _this.createInternalTileUrl(tileCoordinates), }; dbRequest.addEventListener("success", function (dbEvent) { var database = dbEvent.target.result; var tx = database.transaction([_this.options.objectStoreName], "readwrite") .objectStore(_this.options.objectStoreName).put(tileCacheEntry); tx.addEventListener("success", function () { resolve(tileCacheEntry); }); tx.addEventListener("error", function (event) { _this.emit("error", event.target.error); reject(event.target.error); }); }); dbRequest.addEventListener("error", function (dbEvent) { _this.emit("error", dbEvent.target.error); reject(dbEvent.target.error); }); }); }); }; /** * Seeds an area of tiles by the given bounding box, the maximal z value and the optional minimal z value. * * The returned number in the promise is equal to the duration of the operation in milliseconds. */ IndexedDbTileCache.prototype.seedBBox = function (bbox, maxZ, minZ) { var _this = this; if (minZ === void 0) { minZ = 0; } var start = Date.now(); var list = tile_utils_1.getListOfTilesInBBox(bbox, maxZ, minZ); var total = list.length; return new Promise(function (resolve, reject) { var fn = function () { /** * @event IndexedDbTileCache#seed-progess * @type IIndexedDbTileCacheSeedProgress */ _this.emit("seed-progress", { total: total, remains: list.length }); var val = list.shift(); if (val) { _this.downloadTile(val).then(function () { setTimeout(fn, _this.options.crawlDelay); }, reject); return; } resolve(Date.now() - start); }; fn(); }); }; /** * Purge the whole store */ IndexedDbTileCache.prototype.purgeStore = function () { var _this = this; var dbRequest = indexedDB.open(this.options.databaseName, this.options.databaseVersion); return new Promise(function (resolve, reject) { dbRequest.addEventListener("success", function (dbEvent) { var database = dbEvent.target.result; var tx = database.transaction([_this.options.objectStoreName], "readwrite") .objectStore(_this.options.objectStoreName).clear(); tx.addEventListener("success", function () { resolve(true); }); tx.addEventListener("error", function (event) { _this.emit("error", dbEvent.target.error); reject(event.target.error); }); }); dbRequest.addEventListener("error", function (dbEvent) { _this.emit("error", dbEvent.target.error); }); }); }; return IndexedDbTileCache; }(events_1.EventEmitter)); exports.IndexedDbTileCache = IndexedDbTileCache; //# sourceMappingURL=tile-cache.js.map