@ngageoint/geopackage
Version:
GeoPackage JavaScript Library
424 lines • 19.5 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FeatureTableIndex = void 0;
var rtreeIndex_1 = require("../rtree/rtreeIndex");
var baseExtension_1 = require("../baseExtension");
var extension_1 = require("../extension");
var tableIndex_1 = require("./tableIndex");
var geometryIndexDao_1 = require("./geometryIndexDao");
var rtreeIndexDao_1 = require("../rtree/rtreeIndexDao");
var envelopeBuilder_1 = require("../../geom/envelopeBuilder");
/**
* This class will either use the RTree index if it exists, or the
* Feature Table Index NGA Extension implementation. This extension is used to
* index Geometries within a feature table by their minimum bounding box for
* bounding box queries.
* @class
* @extends BaseExtension
*/
var FeatureTableIndex = /** @class */ (function (_super) {
__extends(FeatureTableIndex, _super);
/**
*
* @param geoPackage GeoPackage object
* @param featureDao FeatureDao to index
*/
function FeatureTableIndex(geoPackage, featureDao) {
var _this = _super.call(this, geoPackage) || this;
_this.featureDao = featureDao;
_this.extensionName = FeatureTableIndex.EXTENSION_NAME;
_this.extensionDefinition = FeatureTableIndex.EXTENSION_GEOMETRY_INDEX_DEFINITION;
_this.tableName = featureDao.table_name;
_this.columnName = featureDao.getGeometryColumnName();
_this.tableIndexDao = geoPackage.tableIndexDao;
_this.geometryIndexDao = geoPackage.getGeometryIndexDao(featureDao);
_this.rtreeIndexDao = new rtreeIndexDao_1.RTreeIndexDao(geoPackage, featureDao);
_this.rtreeIndexDao.gpkgTableName = 'rtree_' + _this.tableName + '_' + _this.columnName;
_this.rtreeIndex = new rtreeIndex_1.RTreeIndex(geoPackage, featureDao);
/**
* true if the table is indexed with an RTree
* @type {Boolean}
*/
_this.rtreeIndexed = _this.hasExtension('gpkg_rtree_index', _this.tableName, _this.columnName);
return _this;
}
/**
* Index the table if not already indexed
* @param {Function} progress function which is called with progress while indexing
* @return {Promise<Boolean>} promise resolved when the indexing is complete
*/
FeatureTableIndex.prototype.index = function (progress) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, this.indexWithForce(false, progress)];
});
});
};
/**
* Index the table if not already indexed or force is true
* @param {Boolean} force force index even if the table is already indexed
* @param {Function} progress function which is called with progress while indexing
* @return {Promise<Boolean>} promise resolved when the indexing is complete
*/
FeatureTableIndex.prototype.indexWithForce = function (force, progress) {
return __awaiter(this, void 0, void 0, function () {
var indexed, rtreeIndex, tableIndex;
return __generator(this, function (_a) {
// eslint-disable-next-line @typescript-eslint/no-empty-function
progress = progress || function () { };
this.progress = function (message) {
setTimeout(progress, 0, message);
};
indexed = this.isIndexed();
if (!indexed || force) {
rtreeIndex = new rtreeIndex_1.RTreeIndex(this.geoPackage, this.featureDao);
rtreeIndex.create();
this.rtreeIndexed = rtreeIndex.hasExtension(rtreeIndex.extensionName, rtreeIndex.tableName, rtreeIndex.columnName);
indexed = this.isIndexed();
}
if (!indexed) {
this.getOrCreateExtension();
tableIndex = this.getOrCreateTableIndex();
this.createOrClearGeometryIndicies();
return [2 /*return*/, this.indexTable(tableIndex)];
}
else {
return [2 /*return*/, indexed];
}
return [2 /*return*/];
});
});
};
/**
* Index the table using the NGA index and Rtree if not already indexed or force is true
* @param {Boolean} force force index even if the table is already indexed
* @param {Function} progress function which is called with progress while indexing
* @return {Promise<Boolean>} promise resolved when the indexing is complete
*/
FeatureTableIndex.prototype.ngaIndexWithForce = function (force, progress) {
return __awaiter(this, void 0, void 0, function () {
var indexed, rtreeIndex, tableIndex;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
// eslint-disable-next-line @typescript-eslint/no-empty-function
progress = progress || function () { };
this.progress = function (message) {
setTimeout(progress, 0, message);
};
indexed = this.isIndexed(true);
if (!(!indexed || force)) return [3 /*break*/, 2];
rtreeIndex = new rtreeIndex_1.RTreeIndex(this.geoPackage, this.featureDao);
return [4 /*yield*/, rtreeIndex.create()];
case 1:
_a.sent();
_a.label = 2;
case 2:
if (!(!indexed || force)) return [3 /*break*/, 5];
this.getOrCreateExtension();
return [4 /*yield*/, this.getOrCreateTableIndex()];
case 3:
tableIndex = _a.sent();
return [4 /*yield*/, this.createOrClearGeometryIndicies()];
case 4:
_a.sent();
return [2 /*return*/, this.indexTable(tableIndex)];
case 5: return [2 /*return*/, indexed];
}
});
});
};
/**
* Check if the table is indexed either with an RTree or the NGA Feature Table Index
* @return {Boolean}
*/
FeatureTableIndex.prototype.isIndexed = function (checkOnlyNGA) {
if (checkOnlyNGA === void 0) { checkOnlyNGA = false; }
if (!checkOnlyNGA) {
var rtreeIndex = new rtreeIndex_1.RTreeIndex(this.geoPackage, this.featureDao);
this.rtreeIndexed = rtreeIndex.hasExtension(rtreeIndex.extensionName, rtreeIndex.tableName, rtreeIndex.columnName);
if (this.rtreeIndexed) {
return true;
}
}
try {
var result = this.getFeatureTableIndexExtension();
if (result) {
var contentsDao = this.geoPackage.contentsDao;
var contents = contentsDao.queryForId(this.tableName);
if (!contents)
return false;
var lastChange = new Date(contents.last_change);
var tableIndex = this.tableIndexDao.queryForId(this.tableName);
if (!tableIndex || !tableIndex.last_indexed) {
return false;
}
var lastIndexed = new Date(tableIndex.last_indexed);
return lastIndexed >= lastChange;
}
else {
return false;
}
}
catch (e) {
return false;
}
};
/**
* Returns the feature table index extension for this table and column name if exists
* @return {module:extension~Extension}
*/
FeatureTableIndex.prototype.getFeatureTableIndexExtension = function () {
return this.getExtension(this.extensionName, this.tableName, this.columnName)[0];
};
/**
* Get or create the extension for this table name and column name
* @return {module:extension~Extension}
*/
FeatureTableIndex.prototype.getOrCreateExtension = function () {
return this.getOrCreate(this.extensionName, this.tableName, this.columnName, this.extensionDefinition, extension_1.Extension.READ_WRITE);
};
/**
* Get or create if needed the table index
* @return {TableIndex}
*/
FeatureTableIndex.prototype.getOrCreateTableIndex = function () {
var tableIndex = this.tableIndex;
if (tableIndex)
return tableIndex;
this.tableIndexDao.createTable();
this.createTableIndex();
return this.tableIndex;
};
/**
* Create the table index
* @return {module:extension/index~TableIndex}
*/
FeatureTableIndex.prototype.createTableIndex = function () {
var ti = new tableIndex_1.TableIndex();
ti.table_name = this.tableName;
ti.last_indexed = new Date();
return this.tableIndexDao.create(ti);
};
Object.defineProperty(FeatureTableIndex.prototype, "tableIndex", {
/**
* Get the table index
* @return {module:extension/index~TableIndex}
*/
get: function () {
if (this.tableIndexDao.isTableExists()) {
return this.tableIndexDao.queryForId(this.tableName);
}
else {
return;
}
},
enumerable: false,
configurable: true
});
/**
* Clear the geometry indices or create the table if needed
* @return {Promise} resolved when complete
*/
FeatureTableIndex.prototype.createOrClearGeometryIndicies = function () {
this.geometryIndexDao.createTable();
return this.clearGeometryIndicies();
};
/**
* Clears the geometry indices
* @return {Number} number of rows deleted
*/
FeatureTableIndex.prototype.clearGeometryIndicies = function () {
var where = this.geometryIndexDao.buildWhereWithFieldAndValue(geometryIndexDao_1.GeometryIndexDao.COLUMN_TABLE_NAME, this.tableName);
var whereArgs = this.geometryIndexDao.buildWhereArgs(this.tableName);
return this.geometryIndexDao.deleteWhere(where, whereArgs);
};
/**
* Indexes the table
* @param {module:extension/index~TableIndex} tableIndex TableIndex
* @return {Promise} resolved when complete
*/
FeatureTableIndex.prototype.indexTable = function (tableIndex) {
return __awaiter(this, void 0, void 0, function () {
var _this = this;
return __generator(this, function (_a) {
return [2 /*return*/, new Promise(function (resolve, reject) {
setTimeout(function () {
_this.indexChunk(0, tableIndex, resolve, reject);
});
}).then(function () {
return _this.updateLastIndexed(tableIndex) === 1;
})];
});
});
};
/**
* Indexes a chunk of 100 rows
* @param {Number} page page to start on
* @param {module:extension/index~TableIndex} tableIndex TableIndex
* @param {Function} resolve function to call when all chunks are indexed
* @param {Function} reject called if there is an error
*/
FeatureTableIndex.prototype.indexChunk = function (page, tableIndex, resolve, reject) {
var _this = this;
var rows = this.featureDao.queryForChunk(100, page);
if (rows.length) {
this.progress('Indexing ' + page * 100 + ' to ' + (page + 1) * 100);
rows.forEach(function (row) {
var fr = _this.featureDao.getRow(row);
_this.indexRow(tableIndex, fr.id, fr.geometry);
});
setTimeout(function () {
_this.indexChunk(++page, tableIndex, resolve, reject);
});
}
else {
resolve();
}
};
/**
* Indexes a row
* @param {ableIndex} tableIndex TableIndex`
* @param {Number} geomId id of the row
* @param {GeometryData} geomData GeometryData to index
* @return {Boolean} success
*/
FeatureTableIndex.prototype.indexRow = function (tableIndex, geomId, geomData) {
if (!geomData)
return false;
var envelope = geomData.envelope;
if (!envelope) {
var geometry = geomData.geometry;
if (geometry) {
envelope = envelopeBuilder_1.EnvelopeBuilder.buildEnvelopeWithGeometry(geometry);
}
}
if (envelope) {
var geometryIndex = this.geometryIndexDao.populate(tableIndex, geomId, envelope);
return this.geometryIndexDao.createOrUpdate(geometryIndex) === 1;
}
else {
return false;
}
};
/**
* Update the last time this feature table was indexed
* @param {module:extension/index~TableIndex} tableIndex TableIndex
* @return {Object} update status
*/
FeatureTableIndex.prototype.updateLastIndexed = function (tableIndex) {
if (!tableIndex) {
tableIndex = new tableIndex_1.TableIndex();
tableIndex.table_name = this.tableName;
}
tableIndex.last_indexed = new Date().toISOString();
var updateIndex = this.tableIndexDao.createOrUpdate(tableIndex);
return updateIndex;
};
/**
* Query the index with the specified bounding box and projection
* @param {module:boundingBox~BoundingBox} boundingBox bounding box to query for
* @param {string} projection projection the boundingBox is in
* @return {IterableIterator}
*/
FeatureTableIndex.prototype.queryWithBoundingBox = function (boundingBox, projection) {
var projectedBoundingBox = boundingBox.projectBoundingBox(projection, this.featureDao.projection);
var envelope = projectedBoundingBox.buildEnvelope();
return this.queryWithGeometryEnvelope(envelope);
};
/**
* Query witha geometry envelope
* @param {any} envelope envelope
* @return {IterableIterator<any>}
*/
FeatureTableIndex.prototype.queryWithGeometryEnvelope = function (envelope) {
if (this.rtreeIndexed) {
return this.rtreeIndexDao.queryWithGeometryEnvelope(envelope);
}
else {
return this.geometryIndexDao.queryWithGeometryEnvelope(envelope);
}
};
/**
* Count the index with the specified bounding box and projection
* @param {module:boundingBox~BoundingBox} boundingBox bounding box to query for
* @param {string} projection projection the boundingBox is in
* @return {Number}
*/
FeatureTableIndex.prototype.countWithBoundingBox = function (boundingBox, projection) {
var projectedBoundingBox = boundingBox.projectBoundingBox(projection, this.featureDao.projection);
var envelope = projectedBoundingBox.buildEnvelope();
return this.countWithGeometryEnvelope(envelope);
};
/**
* Count with a geometry envelope
* @param {any} envelope envelope
* @return {Number}
*/
FeatureTableIndex.prototype.countWithGeometryEnvelope = function (envelope) {
if (this.rtreeIndexed) {
return this.rtreeIndexDao.countWithGeometryEnvelope(envelope);
}
else {
return this.geometryIndexDao.countWithGeometryEnvelope(envelope);
}
};
FeatureTableIndex.EXTENSION_GEOMETRY_INDEX_AUTHOR = 'nga';
FeatureTableIndex.EXTENSION_GEOMETRY_INDEX_NAME_NO_AUTHOR = 'geometry_index';
FeatureTableIndex.EXTENSION_NAME = extension_1.Extension.buildExtensionName(FeatureTableIndex.EXTENSION_GEOMETRY_INDEX_AUTHOR, FeatureTableIndex.EXTENSION_GEOMETRY_INDEX_NAME_NO_AUTHOR);
FeatureTableIndex.EXTENSION_GEOMETRY_INDEX_DEFINITION = 'http://ngageoint.github.io/GeoPackage/docs/extensions/geometry-index.html';
return FeatureTableIndex;
}(baseExtension_1.BaseExtension));
exports.FeatureTableIndex = FeatureTableIndex;
//# sourceMappingURL=featureTableIndex.js.map