UNPKG

@ngageoint/geopackage

Version:
1,124 lines 51.4 kB
"use strict";
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 (_) 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 };
    }
};
var __values = (this && this.__values) || function(o) {
    var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
    if (m) return m.call(o);
    if (o && typeof o.length === "number") return {
        next: function () {
            if (o && i >= o.length) o = void 0;
            return { value: o && o[i++], done: !o };
        }
    };
    throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FeatureTiles = void 0;
// @ts-ignore
var reproject_1 = __importDefault(require("reproject"));
var polygon_to_line_1 = __importDefault(require("@turf/polygon-to-line"));
var boolean_clockwise_1 = __importDefault(require("@turf/boolean-clockwise"));
var simplify_js_1 = __importDefault(require("simplify-js"));
var tileBoundingBoxUtils_1 = require("../tileBoundingBoxUtils");
var boundingBox_1 = require("../../boundingBox");
var iconCache_1 = require("../../extension/style/iconCache");
var geometryCache_1 = require("./geometryCache");
var featureDrawType_1 = require("./featureDrawType");
var featurePaintCache_1 = require("./featurePaintCache");
var paint_1 = require("./paint");
var featureTableStyles_1 = require("../../extension/style/featureTableStyles");
var canvas_1 = require("../../canvas/canvas");
var projection_1 = require("../../projection/projection");
var projectionConstants_1 = require("../../projection/projectionConstants");
/**
 * FeatureTiles module.
 * @module tiles/features
 */
/**
 *  Tiles drawn from or linked to features. Used to query features and optionally draw tiles
 *  from those features.
 */
var FeatureTiles = /** @class */ (function () {
    function FeatureTiles(featureDao, tileWidth, tileHeight) {
        if (tileWidth === void 0) { tileWidth = 256; }
        if (tileHeight === void 0) { tileHeight = 256; }
        this.featureDao = featureDao;
        this.tileWidth = tileWidth;
        this.tileHeight = tileHeight;
        this.projection = null;
        this.webMercatorProjection = null;
        this.simplifyGeometries = true;
        this.simplifyToleranceInPixels = 1;
        this.compressFormat = 'png';
        this.pointRadius = 4.0;
        this.pointPaint = new paint_1.Paint();
        this.pointIcon = null;
        this.linePaint = new paint_1.Paint();
        this._lineStrokeWidth = 2.0;
        this.polygonPaint = new paint_1.Paint();
        this._polygonStrokeWidth = 2.0;
        this.fillPolygon = true;
        this.polygonFillPaint = new paint_1.Paint();
        this.featurePaintCache = new featurePaintCache_1.FeaturePaintCache();
        this.geometryCache = new geometryCache_1.GeometryCache();
        this.cacheGeometries = true;
        this.iconCache = new iconCache_1.IconCache();
        this._scale = 1.0;
        this.maxFeaturesPerTile = null;
        this.maxFeaturesTileDraw = null;
        this.projection = this.featureDao.projection;
        this.linePaint.strokeWidth = 2.0;
        this.polygonPaint.strokeWidth = 2.0;
        this.polygonFillPaint.color = '#00000011';
        this.geoPackage = this.featureDao.geoPackage;
        if (this.geoPackage != null) {
            this.featureTableStyles = new featureTableStyles_1.FeatureTableStyles(this.geoPackage, featureDao.table);
            if (!this.featureTableStyles.has()) {
                this.featureTableStyles = null;
            }
        }
        this.webMercatorProjection = projection_1.Projection.getWebMercatorToWGS84Converter();
        this.calculateDrawOverlap();
    }
    /**
     * Will handle disposing any saved icons
     */
    FeatureTiles.prototype.cleanup = function () {
        this.clearIconCache();
        if (this.pointIcon) {
            canvas_1.Canvas.disposeImage(this.pointIcon.getIcon());
            this.pointIcon = null;
        }
    };
    Object.defineProperty(FeatureTiles.prototype, "drawOverlap", {
        /**
         * Manually set the width and height draw overlap
         * @param {Number} pixels pixels
         */
        set: function (pixels) {
            this.widthDrawOverlap = pixels;
            this.heightDrawOverlap = pixels;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(FeatureTiles.prototype, "simplifyTolerance", {
        /**
         * Get the simplify tolerance in pixels
         * @return {Number} width draw overlap
         */
        get: function () {
            return this.simplifyToleranceInPixels;
        },
        /**
         * Set the tolerance in pixels used for simplifying rendered geometries
         * @param {Number} pixels pixels
         */
        set: function (pixels) {
            this.simplifyToleranceInPixels = pixels;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(FeatureTiles.prototype, "widthDrawOverlap", {
        /**
         * Get the width draw overlap
         * @return {Number} width draw overlap
         */
        get: function () {
            return this.widthOverlap;
        },
        /**
         * Manually set the width draw overlap
         * @param {Number} pixels pixels
         */
        set: function (pixels) {
            this.widthOverlap = pixels;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(FeatureTiles.prototype, "heightDrawOverlap", {
        /**
         * Get the height draw overlap
         * @return {Number} height draw overlap
         */
        get: function () {
            return this.heightOverlap;
        },
        /**
         * Manually set the height draw overlap
         * @param {Number} pixels pixels
         */
        set: function (pixels) {
            this.heightOverlap = pixels;
        },
        enumerable: false,
        configurable: true
    });
    /**
     * Ignore the feature table styles within the GeoPackage
     */
    FeatureTiles.prototype.ignoreFeatureTableStyles = function () {
        this.featureTableStyles = null;
        this.calculateDrawOverlap();
    };
    /**
     * Clear all caches
     */
    FeatureTiles.prototype.clearCache = function () {
        this.clearStylePaintCache();
        this.clearIconCache();
    };
    /**
     * Clear the style paint cache
     */
    FeatureTiles.prototype.clearStylePaintCache = function () {
        this.featurePaintCache.clear();
    };
    Object.defineProperty(FeatureTiles.prototype, "stylePaintCacheSize", {
        /**
         * Set / resize the style paint cache size
         *
         * @param {Number} size
         * @since 3.3.0
         */
        set: function (size) {
            this.featurePaintCache.resize(size);
        },
        enumerable: false,
        configurable: true
    });
    /**
     * Clear the icon cache
     */
    FeatureTiles.prototype.clearIconCache = function () {
        this.iconCache.clear();
    };
    Object.defineProperty(FeatureTiles.prototype, "iconCacheSize", {
        /**
         * Set / resize the icon cache size
         * @param {Number} size new size
         */
        set: function (size) {
            this.iconCache.resize(size);
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(FeatureTiles.prototype, "scale", {
        get: function () {
            return this._scale;
        },
        /**
         * Set the scale
         *
         * @param {Number} scale scale factor
         */
        set: function (scale) {
            this._scale = scale;
            this.linePaint.strokeWidth = scale * this.lineStrokeWidth;
            this.polygonPaint.strokeWidth = scale * this.polygonStrokeWidth;
            this.featurePaintCache.clear();
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(FeatureTiles.prototype, "geometryCacheMaxSize", {
        /**
         * Set geometry cache's max size
         * @param {Number} maxSize
         */
        set: function (maxSize) {
            this.geometryCache.resize(maxSize);
        },
        enumerable: false,
        configurable: true
    });
    FeatureTiles.prototype.calculateDrawOverlap = function () {
        if (this.pointIcon) {
            this.heightOverlap = this.scale * this.pointIcon.getHeight();
            this.widthOverlap = this.scale * this.pointIcon.getWidth();
        }
        else {
            this.heightOverlap = this.scale * this.pointRadius;
            this.widthOverlap = this.scale * this.pointRadius;
        }
        var lineHalfStroke = (this.scale * this.lineStrokeWidth) / 2.0;
        this.heightOverlap = Math.max(this.heightOverlap, lineHalfStroke);
        this.widthOverlap = Math.max(this.widthOverlap, lineHalfStroke);
        var polygonHalfStroke = (this.scale * this.polygonStrokeWidth) / 2.0;
        this.heightOverlap = Math.max(this.heightOverlap, polygonHalfStroke);
        this.widthOverlap = Math.max(this.widthOverlap, polygonHalfStroke);
        if (this.featureTableStyles != null && this.featureTableStyles.has()) {
            var styleRowIds_1 = [];
            var tableStyleIds = this.featureTableStyles.getAllTableStyleIds();
            if (tableStyleIds != null) {
                styleRowIds_1 = styleRowIds_1.concat(tableStyleIds);
            }
            var styleIds = this.featureTableStyles.getAllStyleIds();
            if (styleIds != null) {
                styleRowIds_1 = styleRowIds_1.concat(styleIds.filter(function (id) { return styleRowIds_1.indexOf(id) === -1; }));
            }
            var styleDao = this.featureTableStyles.getStyleDao();
            for (var i = 0; i < styleRowIds_1.length; i++) {
                var styleRowId = styleRowIds_1[i];
                var styleRow = styleDao.queryForId(styleRowId);
                var styleHalfWidth = this.scale * (styleRow.getWidthOrDefault() / 2.0);
                this.widthOverlap = Math.max(this.widthOverlap, styleHalfWidth);
                this.heightOverlap = Math.max(this.heightOverlap, styleHalfWidth);
            }
            var iconRowIds_1 = [];
            var tableIconIds = this.featureTableStyles.getAllTableIconIds();
            if (tableIconIds != null) {
                iconRowIds_1 = iconRowIds_1.concat(tableIconIds);
            }
            var iconIds = this.featureTableStyles.getAllIconIds();
            if (iconIds != null) {
                iconRowIds_1 = iconRowIds_1.concat(iconIds.filter(function (id) { return iconRowIds_1.indexOf(id) === -1; }));
            }
            var iconDao = this.featureTableStyles.getIconDao();
            for (var i = 0; i < iconRowIds_1.length; i++) {
                var iconRowId = iconRowIds_1[i];
                var iconRow = iconDao.queryForId(iconRowId);
                var iconDimensions = iconRow.derivedDimensions;
                var iconWidth = this.scale * Math.ceil(iconDimensions[0]);
                var iconHeight = this.scale * Math.ceil(iconDimensions[1]);
                this.widthOverlap = Math.max(this.widthOverlap, iconWidth);
                this.heightOverlap = Math.max(this.heightOverlap, iconHeight);
            }
        }
    };
    Object.defineProperty(FeatureTiles.prototype, "drawOverlapsWithPixels", {
        set: function (pixels) {
            this.widthOverlap = pixels;
            this.heightOverlap = pixels;
        },
        enumerable: false,
        configurable: true
    });
    FeatureTiles.prototype.getFeatureStyle = function (featureRow) {
        var featureStyle = null;
        if (this.featureTableStyles != null) {
            featureStyle = this.featureTableStyles.getFeatureStyleForFeatureRow(featureRow);
        }
        return featureStyle;
    };
    /**
     * Get the point paint for the feature style, or return the default paint
     * @param featureStyle feature style
     * @return paint
     */
    FeatureTiles.prototype.getPointPaint = function (featureStyle) {
        var paint = this.getFeatureStylePaint(featureStyle, featureDrawType_1.FeatureDrawType.CIRCLE);
        if (paint == null) {
            paint = this.pointPaint;
        }
        return paint;
    };
    /**
     * Get the line paint for the feature style, or return the default paint
     * @param featureStyle feature style
     * @return paint
     */
    FeatureTiles.prototype.getLinePaint = function (featureStyle) {
        var paint = this.getFeatureStylePaint(featureStyle, featureDrawType_1.FeatureDrawType.STROKE);
        if (paint == null) {
            paint = this.linePaint;
        }
        return paint;
    };
    /**
     * Get the polygon paint for the feature style, or return the default paint
     * @param featureStyle feature style
     * @return paint
     */
    FeatureTiles.prototype.getPolygonPaint = function (featureStyle) {
        var paint = this.getFeatureStylePaint(featureStyle, featureDrawType_1.FeatureDrawType.STROKE);
        if (paint == null) {
            paint = this.polygonPaint;
        }
        return paint;
    };
    /**
     * Get the polygon fill paint for the feature style, or return the default
     * paint
     * @param featureStyle feature style
     * @return paint
     */
    FeatureTiles.prototype.getPolygonFillPaint = function (featureStyle) {
        var paint = null;
        var hasStyleColor = false;
        if (featureStyle != null) {
            var style = featureStyle.style;
            if (style != null) {
                if (style.hasFillColor()) {
                    paint = this.getStylePaint(style, featureDrawType_1.FeatureDrawType.FILL);
                }
                else {
                    hasStyleColor = style.hasColor();
                }
            }
        }
        if (paint === null && !hasStyleColor && this.fillPolygon) {
            paint = this.polygonFillPaint;
        }
        return paint;
    };
    /**
     * Get the feature style paint from cache, or create and cache it
     * @param featureStyle feature style
     * @param drawType draw type
     * @return feature style paint
     */
    FeatureTiles.prototype.getFeatureStylePaint = function (featureStyle, drawType) {
        var paint = null;
        if (featureStyle != null) {
            var style = featureStyle.style;
            if (style != null && style.hasColor()) {
                paint = this.getStylePaint(style, drawType);
            }
        }
        return paint;
    };
    /**
     * Get the style paint from cache, or create and cache it
     * @param style style row
     * @param drawType draw type
     * @return {Paint} paint
     */
    FeatureTiles.prototype.getStylePaint = function (style, drawType) {
        var paint = this.featurePaintCache.getPaintForStyleRow(style, drawType);
        if (paint === undefined || paint === null) {
            var color = null;
            var strokeWidth = null;
            if (drawType === featureDrawType_1.FeatureDrawType.CIRCLE) {
                color = style.getColor();
            }
            else if (drawType === featureDrawType_1.FeatureDrawType.STROKE) {
                color = style.getColor();
                strokeWidth = this.scale * style.getWidthOrDefault();
            }
            else if (drawType === featureDrawType_1.FeatureDrawType.FILL) {
                color = style.getFillColor();
                strokeWidth = this.scale * style.getWidthOrDefault();
            }
            else {
                throw new Error('Unsupported Draw Type: ' + drawType);
            }
            var stylePaint = new paint_1.Paint();
            stylePaint.color = color;
            if (strokeWidth != null) {
                stylePaint.strokeWidth = strokeWidth;
            }
            paint = this.featurePaintCache.getPaintForStyleRow(style, drawType);
            if (paint === undefined || paint === null) {
                this.featurePaintCache.setPaintForStyleRow(style, drawType, stylePaint);
                paint = stylePaint;
            }
        }
        return paint;
    };
    Object.defineProperty(FeatureTiles.prototype, "pointColor", {
        /**
         * Get point color
         * @return {String} color
         */
        get: function () {
            return this.pointPaint.color;
        },
        /**
         * Set point color
         * @param {String} pointColor point color
         */
        set: function (pointColor) {
            this.pointPaint.color = pointColor;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(FeatureTiles.prototype, "lineStrokeWidth", {
        /**
         * Get line stroke width
         * @return {Number} width
         */
        get: function () {
            return this._lineStrokeWidth;
        },
        /**
         * Set line stroke width
         * @param {Number} lineStrokeWidth line stroke width
         */
        set: function (lineStrokeWidth) {
            this._lineStrokeWidth = lineStrokeWidth;
            this.linePaint.strokeWidth = this.scale * this.lineStrokeWidth;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(FeatureTiles.prototype, "lineColor", {
        /**
         * Get line color
         * @return {String} color
         */
        get: function () {
            return this.linePaint.color;
        },
        /**
         * Set line color
         * @param {String} lineColor line color
         */
        set: function (lineColor) {
            this.linePaint.color = lineColor;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(FeatureTiles.prototype, "polygonStrokeWidth", {
        /**
         * Get polygon stroke width
         * @return {Number} width
         */
        get: function () {
            return this._polygonStrokeWidth;
        },
        /**
         * Set polygon stroke width
         * @param {Number} polygonStrokeWidth polygon stroke width
         */
        set: function (polygonStrokeWidth) {
            this._polygonStrokeWidth = polygonStrokeWidth;
            this.polygonPaint.strokeWidth = this.scale * polygonStrokeWidth;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(FeatureTiles.prototype, "polygonColor", {
        /**
         * Get polygon color
         * @return {String} color
         */
        get: function () {
            return this.polygonPaint.color;
        },
        /**
         * Set polygon color
         * @param {String} polygonColor polygon color
         */
        set: function (polygonColor) {
            this.polygonPaint.color = polygonColor;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(FeatureTiles.prototype, "polygonFillColor", {
        /**
         * Get polygon fill color
         * @return {String} color
         */
        get: function () {
            return this.polygonFillPaint.color;
        },
        /**
         * Set polygon fill color
         * @param {String} polygonFillColor polygon fill color
         */
        set: function (polygonFillColor) {
            this.polygonFillPaint.color = polygonFillColor;
        },
        enumerable: false,
        configurable: true
    });
    FeatureTiles.prototype.getFeatureCountXYZ = function (x, y, z) {
        var boundingBox = tileBoundingBoxUtils_1.TileBoundingBoxUtils.getWebMercatorBoundingBoxFromXYZ(x, y, z);
        boundingBox = this.expandBoundingBox(boundingBox);
        return this.featureDao.countWebMercatorBoundingBox(boundingBox);
    };
    FeatureTiles.prototype.drawTile = function (x, y, z, canvas) {
        if (canvas === void 0) { canvas = null; }
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                if (this.featureDao.isIndexed()) {
                    return [2 /*return*/, this.drawTileQueryIndex(x, y, z, canvas)];
                }
                else {
                    return [2 /*return*/, this.drawTileQueryAll(x, y, z, canvas)];
                }
                return [2 /*return*/];
            });
        });
    };
    FeatureTiles.prototype.drawTileQueryAll = function (x, y, zoom, canvas) {
        return __awaiter(this, void 0, void 0, function () {
            var boundingBox, count;
            return __generator(this, function (_a) {
                boundingBox = tileBoundingBoxUtils_1.TileBoundingBoxUtils.getWebMercatorBoundingBoxFromXYZ(x, y, zoom);
                count = this.featureDao.getCount();
                if (this.maxFeaturesPerTile === null || count <= this.maxFeaturesPerTile) {
                    return [2 /*return*/, this.drawTileWithBoundingBox(boundingBox, zoom, canvas)];
                }
                else if (this.maxFeaturesTileDraw != null) {
                    return [2 /*return*/, this.maxFeaturesTileDraw.drawUnindexedTile(256, 256, canvas)];
                }
                return [2 /*return*/];
            });
        });
    };
    /**
     * Transform geojson to web mercator if it is in another projection.
     * @param geoJson
     */
    FeatureTiles.prototype.webMercatorTransform = function (geoJson) {
        if (this.projection !== this.webMercatorProjection) {
            return reproject_1.default.reproject(geoJson, this.projection, this.webMercatorProjection);
        }
        else {
            return geoJson;
        }
    };
    FeatureTiles.prototype.drawTileQueryIndex = function (x, y, z, tileCanvas) {
        return __awaiter(this, void 0, void 0, function () {
            var canvas, context, dispose, image, boundingBox, expandedBoundingBox, width, height, featureCount, iterator, iterator_1, iterator_1_1, featureRow, geojson, style, e_1, e_2_1;
            var e_2, _a;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        dispose = false;
                        boundingBox = tileBoundingBoxUtils_1.TileBoundingBoxUtils.getWebMercatorBoundingBoxFromXYZ(x, y, z);
                        expandedBoundingBox = this.expandBoundingBox(boundingBox);
                        width = 256;
                        height = 256;
                        return [4 /*yield*/, canvas_1.Canvas.initializeAdapter()];
                    case 1:
                        _b.sent();
                        // create Canvas if user does not provide canvas.
                        if (tileCanvas != null) {
                            canvas = tileCanvas;
                            context = canvas.getContext('2d');
                            context.clearRect(0, 0, width, height);
                        }
                        else {
                            canvas = canvas_1.Canvas.create(width, height);
                            context = canvas.getContext('2d');
                            dispose = true;
                        }
                        featureCount = this.featureDao.countWebMercatorBoundingBox(expandedBoundingBox);
                        if (!(featureCount > 0)) return [3 /*break*/, 16];
                        if (!(this.maxFeaturesPerTile == null || featureCount <= this.maxFeaturesPerTile)) return [3 /*break*/, 13];
                        iterator = this.featureDao.fastQueryWebMercatorBoundingBox(expandedBoundingBox);
                        _b.label = 2;
                    case 2:
                        _b.trys.push([2, 9, 10, 11]);
                        iterator_1 = __values(iterator), iterator_1_1 = iterator_1.next();
                        _b.label = 3;
                    case 3:
                        if (!!iterator_1_1.done) return [3 /*break*/, 8];
                        featureRow = iterator_1_1.value;
                        if (!(featureRow.geometry != null)) return [3 /*break*/, 7];
                        geojson = null;
                        if (this.cacheGeometries) {
                            geojson = this.geometryCache.getGeometry(featureRow.id);
                        }
                        if (geojson == null) {
                            geojson = featureRow.geometry.geometry.toGeoJSON();
                            this.geometryCache.setGeometry(featureRow.id, geojson);
                        }
                        style = this.getFeatureStyle(featureRow);
                        _b.label = 4;
                    case 4:
                        _b.trys.push([4, 6, , 7]);
                        return [4 /*yield*/, this.drawGeometry(this.webMercatorTransform(geojson), context, boundingBox, style)];
                    case 5:
                        _b.sent();
                        return [3 /*break*/, 7];
                    case 6:
                        e_1 = _b.sent();
                        console.error('Failed to draw feature in tile. Id: ' + featureRow.id + ', Table: ' + this.featureDao.table_name);
                        return [3 /*break*/, 7];
                    case 7:
                        iterator_1_1 = iterator_1.next();
                        return [3 /*break*/, 3];
                    case 8: return [3 /*break*/, 11];
                    case 9:
                        e_2_1 = _b.sent();
                        e_2 = { error: e_2_1 };
                        return [3 /*break*/, 11];
                    case 10:
                        try {
                            if (iterator_1_1 && !iterator_1_1.done && (_a = iterator_1.return)) _a.call(iterator_1);
                        }
                        finally { if (e_2) throw e_2.error; }
                        return [7 /*endfinally*/];
                    case 11: return [4 /*yield*/, canvas_1.Canvas.toDataURL(canvas, 'image/' + this.compressFormat)];
                    case 12:
                        image = _b.sent();
                        return [3 /*break*/, 15];
                    case 13:
                        if (!(this.maxFeaturesTileDraw != null)) return [3 /*break*/, 15];
                        return [4 /*yield*/, this.maxFeaturesTileDraw.drawTile(width, height, featureCount.toString(), canvas)];
                    case 14:
                        // Draw the max features tile
                        image = _b.sent();
                        _b.label = 15;
                    case 15: return [3 /*break*/, 18];
                    case 16: return [4 /*yield*/, canvas_1.Canvas.toDataURL(canvas, 'image/' + this.compressFormat)];
                    case 17:
                        image = _b.sent();
                        _b.label = 18;
                    case 18:
                        if (dispose) {
                            canvas_1.Canvas.disposeCanvas(canvas);
                        }
                        return [2 /*return*/, image];
                }
            });
        });
    };
    FeatureTiles.prototype.drawTileWithBoundingBox = function (boundingBox, zoom, tileCanvas) {
        return __awaiter(this, void 0, void 0, function () {
            var width, height, canvas, dispose, context, featureDao, each, each_1, each_1_1, row, fr, gj, style, e_3, e_4_1, image;
            var e_4, _a;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        width = 256;
                        height = 256;
                        dispose = false;
                        return [4 /*yield*/, canvas_1.Canvas.initializeAdapter()];
                    case 1:
                        _b.sent();
                        if (tileCanvas != null) {
                            canvas = tileCanvas;
                        }
                        else {
                            canvas = canvas_1.Canvas.create(width, height);
                            dispose = true;
                        }
                        context = canvas.getContext('2d');
                        context.clearRect(0, 0, width, height);
                        featureDao = this.featureDao;
                        each = featureDao.queryForEach(undefined, undefined, undefined, undefined, undefined, [featureDao.table.getIdColumn().getName(), featureDao.table.getGeometryColumn().getName()]);
                        _b.label = 2;
                    case 2:
                        _b.trys.push([2, 9, 10, 11]);
                        each_1 = __values(each), each_1_1 = each_1.next();
                        _b.label = 3;
                    case 3:
                        if (!!each_1_1.done) return [3 /*break*/, 8];
                        row = each_1_1.value;
                        fr = featureDao.getRow(row);
                        if (!(fr.geometry != null)) return [3 /*break*/, 7];
                        gj = null;
                        if (this.cacheGeometries) {
                            gj = this.geometryCache.getGeometryForFeatureRow(fr);
                        }
                        if (gj == null) {
                            gj = fr.geometry.geometry.toGeoJSON();
                            this.geometryCache.setGeometry(fr.id, gj);
                        }
                        if (!(gj != null)) return [3 /*break*/, 7];
                        style = this.getFeatureStyle(fr);
                        _b.label = 4;
                    case 4:
                        _b.trys.push([4, 6, , 7]);
                        return [4 /*yield*/, this.drawGeometry(this.webMercatorTransform(gj), context, boundingBox, style)];
                    case 5:
                        _b.sent();
                        return [3 /*break*/, 7];
                    case 6:
                        e_3 = _b.sent();
                        console.error('Failed to draw feature in tile. Id: ' + fr.id + ', Table: ' + this.featureDao.table_name);
                        return [3 /*break*/, 7];
                    case 7:
                        each_1_1 = each_1.next();
                        return [3 /*break*/, 3];
                    case 8: return [3 /*break*/, 11];
                    case 9:
                        e_4_1 = _b.sent();
                        e_4 = { error: e_4_1 };
                        return [3 /*break*/, 11];
                    case 10:
                        try {
                            if (each_1_1 && !each_1_1.done && (_a = each_1.return)) _a.call(each_1);
                        }
                        finally { if (e_4) throw e_4.error; }
                        return [7 /*endfinally*/];
                    case 11: return [4 /*yield*/, canvas_1.Canvas.toDataURL(canvas, 'image/' + this.compressFormat)];
                    case 12:
                        image = _b.sent();
                        if (dispose) {
                            canvas_1.Canvas.disposeCanvas(canvas);
                        }
                        return [2 /*return*/, image];
                }
            });
        });
    };
    /**
     * Draw a point in the context
     * @param geoJson
     * @param context
     * @param boundingBox
     * @param featureStyle
     */
    FeatureTiles.prototype.drawPoint = function (geoJson, context, boundingBox, featureStyle) {
        return __awaiter(this, void 0, void 0, function () {
            var width, height, iconX, iconY, x, y, iconRow, image, radius, styleRow, pointPaint, circleX, circleY;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        x = tileBoundingBoxUtils_1.TileBoundingBoxUtils.getXPixel(this.tileWidth, boundingBox, geoJson.coordinates[0]);
                        y = tileBoundingBoxUtils_1.TileBoundingBoxUtils.getYPixel(this.tileHeight, boundingBox, geoJson.coordinates[1]);
                        if (!(featureStyle != null && featureStyle.useIcon())) return [3 /*break*/, 2];
                        iconRow = featureStyle.icon;
                        return [4 /*yield*/, this.iconCache.createIcon(iconRow)];
                    case 1:
                        image = _a.sent();
                        width = Math.round(this.scale * image.width);
                        height = Math.round(this.scale * image.height);
                        if (x >= 0 - width && x <= this.tileWidth + width && y >= 0 - height && y <= this.tileHeight + height) {
                            iconX = Math.round(x - iconRow.anchorUOrDefault * width);
                            iconY = Math.round(y - iconRow.anchorVOrDefault * height);
                            context.drawImage(image.image, iconX, iconY, width, height);
                        }
                        return [3 /*break*/, 3];
                    case 2:
                        if (this.pointIcon != null) {
                            width = Math.round(this.scale * this.pointIcon.getWidth());
                            height = Math.round(this.scale * this.pointIcon.getHeight());
                            if (x >= 0 - width && x <= this.tileWidth + width && y >= 0 - height && y <= this.tileHeight + height) {
                                iconX = Math.round(x - this.scale * this.pointIcon.getXOffset());
                                iconY = Math.round(y - this.scale * this.pointIcon.getYOffset());
                                try {
                                    context.drawImage(this.pointIcon.getIcon().image, iconX, iconY, width, height);
                                }
                                catch (e) {
                                    // ignore error
                                }
                            }
                        }
                        else {
                            context.save();
                            radius = null;
                            if (featureStyle != null) {
                                styleRow = featureStyle.style;
                                if (styleRow != null) {
                                    radius = this.scale * (styleRow.getWidthOrDefault() / 2.0);
                                }
                            }
                            if (radius == null) {
                                radius = this.scale * this.pointRadius;
                            }
                            pointPaint = this.getPointPaint(featureStyle);
                            if (x >= 0 - radius && x <= this.tileWidth + radius && y >= 0 - radius && y <= this.tileHeight + radius) {
                                circleX = Math.round(x);
                                circleY = Math.round(y);
                                context.beginPath();
                                context.arc(circleX, circleY, radius, 0, 2 * Math.PI, true);
                                context.closePath();
                                context.fillStyle = pointPaint.colorRGBA;
                                context.fill();
                            }
                            context.restore();
                        }
                        _a.label = 3;
                    case 3: return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Simplify x,y tile coordinates by 1 pixel
     * @param lineString GeoJSON with coordinates in pixels
     * @param isPolygon determines if the first and last point need to stay connected
     * @return simplified GeoJSON
     * @since 2.0.0
     */
    FeatureTiles.prototype.simplifyPoints = function (lineString, isPolygon) {
        if (isPolygon === void 0) { isPolygon = false; }
        var coords = (0, simplify_js_1.default)(lineString.coordinates.map(function (coordinate) {
            return { x: coordinate[0], y: coordinate[1] };
        }), this.simplifyToleranceInPixels, false).map(function (point) { return [point.x, point.y]; });
        if (isPolygon) {
            if (coords.length < 4) {
                return null;
            }
            else if (coords[0][0] !== coords[coords.length - 1][0] || coords[0][1] !== coords[coords.length - 1][1]) {
                // if first and last point do not match, add first point to end
                coords.push(coords[0].slice());
            }
        }
        else if (coords.length < 2) {
            return null;
        }
        lineString.coordinates = coords;
        return lineString;
    };
    /**
     * Get the path of the line string
     * @param lineString
     * @param context
     * @param boundingBox
     * @param isPolygon if this was a polygon
     */
    FeatureTiles.prototype.getPath = function (lineString, context, boundingBox, isPolygon) {
        var _this = this;
        if (isPolygon === void 0) { isPolygon = false; }
        lineString.coordinates = lineString.coordinates.map(function (coordinate) { return [tileBoundingBoxUtils_1.TileBoundingBoxUtils.getXPixel(_this.tileWidth, boundingBox, coordinate[0]), tileBoundingBoxUtils_1.TileBoundingBoxUtils.getYPixel(_this.tileHeight, boundingBox, coordinate[1])]; });
        var simplifiedLineString = this.simplifyGeometries ? this.simplifyPoints(lineString, isPolygon) : lineString;
        if (simplifiedLineString != null && simplifiedLineString.coordinates.length > 0) {
            context.moveTo(simplifiedLineString.coordinates[0][0], simplifiedLineString.coordinates[0][1]);
            for (var i = 1; i < simplifiedLineString.coordinates.length; i++) {
                context.lineTo(simplifiedLineString.coordinates[i][0], simplifiedLineString.coordinates[i][1]);
            }
        }
    };
    /**
     * Draw a line in the context
     * @param geoJson
     * @param context
     * @param featureStyle
     * @param boundingBox
     */
    FeatureTiles.prototype.drawLine = function (geoJson, context, featureStyle, boundingBox) {
        context.save();
        context.beginPath();
        var paint = this.getLinePaint(featureStyle);
        context.strokeStyle = paint.colorRGBA;
        context.lineWidth = paint.strokeWidth;
        this.getPath(geoJson, context, boundingBox);
        context.stroke();
        context.closePath();
        context.restore();
    };
    /**
     * Draw a polygon in the context
     * @param externalRing
     * @param internalRings
     * @param context
     * @param featureStyle
     * @param boundingBox
     * @param fill
     */
    FeatureTiles.prototype.drawPolygon = function (externalRing, internalRings, context, featureStyle, boundingBox, fill) {
        if (fill === void 0) { fill = true; }
        // get paint
        context.save();
        context.beginPath();
        if (!(0, boolean_clockwise_1.default)(externalRing.coordinates)) {
            externalRing.coordinates = externalRing.coordinates.reverse();
        }
        this.getPath(externalRing, context, boundingBox, true);
        context.closePath();
        for (var i = 0; i < internalRings.length; i++) {
            if ((0, boolean_clockwise_1.default)(internalRings[i].coordinates)) {
                internalRings[i].coordinates = internalRings[i].coordinates.reverse();
            }
            this.getPath(internalRings[i], context, boundingBox, true);
            context.closePath();
        }
        var fillPaint = this.getPolygonFillPaint(featureStyle);
        if (fill && fillPaint !== undefined && fillPaint != null) {
            context.fillStyle = fillPaint.colorRGBA;
            context.fill();
        }
        var paint = this.getPolygonPaint(featureStyle);
        context.strokeStyle = paint.colorRGBA;
        context.lineWidth = paint.strokeWidth;
        context.stroke();
        context.restore();
    };
    /**
     * Add a feature to the batch
     * @param simplifyTolerance
     * @param geoJson
     * @param context
     * @param boundingBox
     * @param featureStyle
     */
    FeatureTiles.prototype.drawGeometry = function (geoJson, context, boundingBox, featureStyle) {
        return __awaiter(this, void 0, void 0, function () {
            var i, converted, externalRing, internalRings;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (!(geoJson.type === 'Point')) return [3 /*break*/, 2];
                        return [4 /*yield*/, this.drawPoint(geoJson, context, boundingBox, featureStyle)];
                    case 1:
                        _a.sent();
                        return [3 /*break*/, 19];
                    case 2:
                        if (!(geoJson.type === 'LineString')) return [3 /*break*/, 3];
                        this.drawLine(geoJson, context, featureStyle, boundingBox);
                        return [3 /*break*/, 19];
                    case 3:
                        if (!(geoJson.type === 'Polygon')) return [3 /*break*/, 4];
                        converted = (0, polygon_to_line_1.default)(geoJson);
                        if (converted.type === 'Feature') {
                            if (converted.geometry.type === 'LineString') {
                                this.drawPolygon(converted.geometry, [], context, featureStyle, boundingBox);
                            }
                            else if (converted.geometry.type === 'MultiLineString') { // internal rings
                                externalRing = { type: 'LineString', coordinates: converted.geometry.coordinates[0] };
                                internalRings = converted.geometry.coordinates.slice(1).map(function (coords) {
                                    return {
                                        type: 'LineString',
                                        coordinates: coords
                                    };
                                });
                                this.drawPolygon(externalRing, internalRings, context, featureStyle, boundingBox);
                            }
                        }
                        else {
                            converted.features.forEach(function (feature) {
                                if (feature.geometry.type === 'LineString') {
                                    _this.drawPolygon(feature.geometry, [], context, featureStyle, boundingBox);
                                }
                                else if (feature.geometry.type === 'MultiLineString') {
                                    var externalRing = { type: 'LineString', coordinates: feature.geometry.coordinates[0] };
                                    var internalRings = feature.geometry.coordinates.slice(1).map(function (coords) {
                                        return {
                                            type: 'LineString',
                                            coordinates: coords
                                        };
                                    });
                                    _this.drawPolygon(externalRing, internalRings, context, featureStyle, boundingBox);
                                }
                            });
                        }
                        return [3 /*break*/, 19];
                    case 4:
                        if (!(geoJson.type === 'MultiPoint')) return [3 /*break*/, 9];
                        i = 0;
                        _a.label = 5;
                    case 5:
                        if (!(i < geoJson.coordinates.length)) return [3 /*break*/, 8];
                        return [4 /*yield*/, this.drawPoint({
                                type: 'Point',
                                coordinates: geoJson.coordinates[i],
                            }, context, boundingBox, featureStyle)];
                    case 6:
                        _a.sent();
                        _a.label = 7;
                    case 7:
                        i++;
                        return [3 /*break*/, 5];
                    case 8: return [3 /*break*/, 19];
                    case 9:
                        if (!(geoJson.type === 'MultiLineString')) return [3 /*break*/, 10];
                        for (i = 0; i < geoJson.coordinates.length; i++) {
                            this.drawLine({
                                type: 'LineString',
                                coordinates: geoJson.coordinates[i],
                            }, context, featureStyle, boundingBox);
                        }
                        return [3 /*break*/, 19];
                    case 10:
                        if (!(geoJson.type === 'MultiPolygon')) return [3 /*break*/, 15];
                        i = 0;
                        _a.label = 11;
                    case 11:
                        if (!(i < geoJson.coordinates.length)) return [3 /*break*/, 14];
                        return [4 /*yield*/, this.drawGeometry({
                                type: 'Polygon',
                                coordinates: geoJson.coordinates[i],
                            }, context, boundingBox, featureStyle)];
                    case 12:
                        _a.sent();
                        _a.label = 13;
                    case 13:
                        i++;
                        return [3 /*break*/, 11];
                    case 14: return [3 /*break*/, 19];
                    case 15:
                        if (!(geoJson.type === 'GeometryCollection')) return [3 /*break*/, 19];
                        i = 0;
                        _a.label = 16;
                    case 16:
                        if (!(i < geoJson.geometries.length)) return [3 /*break*/, 19];
                        return [4 /*yield*/, this.drawGeometry(geoJson.geometries[i], context, boundingBox, featureStyle)];
                    case 17:
                        _a.sent();
                        _a.label = 18;
                    case 18:
                        i++;
                        return [3 /*break*/, 16];
                    case 19: return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Create an expanded bounding box to handle features outside the tile that overlap
     * @param webMercatorBoundingBox  web mercator bounding box
     * @return {BoundingBox} bounding box
     */
    FeatureTiles.prototype.expandBoundingBox = function (webMercatorBoundingBox) {
        return this.expandWebMercatorBoundingBox(webMercatorBoundingBox, webMercatorBoundingBox);
    };
    /**
     * Create an expanded bounding box to handle features outside the tile that overlap
     * @param webMercatorBoundingBox web mercator bounding box
     * @param tileWebMercatorBoundingBox  tile web mercator bounding box
     * @return {BoundingBox} bounding box
     */
    FeatureTiles.prototype.expandWebMercatorBoundingBox = function (webMercatorBoundingBox, tileWebMercatorBoundingBox) {
        // Create an expanded bounding box to handle features outside the tile that overlap
        var minLongitude = tileBoundingBoxUtils_1.TileBoundingBoxUtils.getLongitudeFromPixel(this.tileWidth, webMercatorBoundingBox, tileWebMercatorBoundingBox, 0 - this.widthOverlap);
        var maxLongitude = tileBoundingBoxUtils_1.TileBoundingBoxUtils.getLongitudeFromPixel(this.tileWidth, webMercatorBoundingBox, tileWebMercatorBoundingBox, this.tileWidth + this.widthOverlap);
        var maxLatitude = tileBoundingBoxUtils_1.TileBoundingBoxUtils.getLatitudeFromPixel(this.tileHeight, webMercatorBoundingBox, tileWebMercatorBoundingBox, 0 - this.heightOverlap);
        var minLatitude = tileBoundingBoxUtils_1.TileBoundingBoxUtils.getLatitudeFromPixel(this.tileHeight, webMercatorBoundingBox, tileWebMercatorBoundingBox, this.tileHeight + this.heightOverlap);
        // Choose the most expanded longitudes and latitudes
        minLongitude = Math.min(minLongitude, webMercatorBoundingBox.minLongitude);
        maxLongitude = Math.max(maxLongitude, webMercatorBoundingBox.maxLongitude);
        minLatitude = Math.min(minLatitude, webMercatorBoundingBox.minLatitude);
        maxLatitude = Math.max(maxLatitude, webMercatorBoundingBox.maxLatitude);
        // Bound with the web mercator limits
        minLongitude = Math.max(minLongitude, -1 * projectionConstants_1.ProjectionConstants.WEB_MERCATOR_HALF_WORLD_WIDTH);
        maxLongitude = Math.min(maxLongitude, projectionConstants_1.ProjectionConstants.WEB_MERCATOR_HALF_WORLD_WIDTH);
        minLatitude = Math.max(minLatitude, -1 * projectionConstants_1.ProjectionConstants.WEB_MERCATOR_HALF_WORLD_WIDTH);
        maxLatitude = Math.min(maxLatitude, projectionConstants_1.ProjectionConstants.WEB_MERCATOR_HALF_WORLD_WIDTH);
        return new boundingBox_1.BoundingBox(minLongitude, maxLongitude, minLatitude, maxLatitude);
    };
    return FeatureTiles;
}());
exports.FeatureTiles = FeatureTiles;
//# sourceMappingURL=index.js.map