UNPKG

openlayers

Version:

Build tools and sources for developing OpenLayers based mapping applications

303 lines (258 loc) 8.46 kB
goog.provide('ol.source.ImageVector'); goog.require('ol'); goog.require('ol.dom'); goog.require('ol.events'); goog.require('ol.events.EventType'); goog.require('ol.extent'); goog.require('ol.render.canvas.ReplayGroup'); goog.require('ol.renderer.vector'); goog.require('ol.source.ImageCanvas'); goog.require('ol.style.Style'); goog.require('ol.transform'); /** * @classdesc * An image source whose images are canvas elements into which vector features * read from a vector source (`ol.source.Vector`) are drawn. An * `ol.source.ImageVector` object is to be used as the `source` of an image * layer (`ol.layer.Image`). Image layers are rotated, scaled, and translated, * as opposed to being re-rendered, during animations and interactions. So, like * any other image layer, an image layer configured with an * `ol.source.ImageVector` will exhibit this behaviour. This is in contrast to a * vector layer, where vector features are re-drawn during animations and * interactions. * * @constructor * @extends {ol.source.ImageCanvas} * @param {olx.source.ImageVectorOptions} options Options. * @api */ ol.source.ImageVector = function(options) { /** * @private * @type {ol.source.Vector} */ this.source_ = options.source; /** * @private * @type {ol.Transform} */ this.transform_ = ol.transform.create(); /** * @private * @type {CanvasRenderingContext2D} */ this.canvasContext_ = ol.dom.createCanvasContext2D(); /** * @private * @type {ol.Size} */ this.canvasSize_ = [0, 0]; /** * @private * @type {number} */ this.renderBuffer_ = options.renderBuffer == undefined ? 100 : options.renderBuffer; /** * @private * @type {ol.render.canvas.ReplayGroup} */ this.replayGroup_ = null; ol.source.ImageCanvas.call(this, { attributions: options.attributions, canvasFunction: this.canvasFunctionInternal_.bind(this), logo: options.logo, projection: options.projection, ratio: options.ratio, resolutions: options.resolutions, state: this.source_.getState() }); /** * User provided style. * @type {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction} * @private */ this.style_ = null; /** * Style function for use within the library. * @type {ol.StyleFunction|undefined} * @private */ this.styleFunction_ = undefined; this.setStyle(options.style); ol.events.listen(this.source_, ol.events.EventType.CHANGE, this.handleSourceChange_, this); }; ol.inherits(ol.source.ImageVector, ol.source.ImageCanvas); /** * @param {ol.Extent} extent Extent. * @param {number} resolution Resolution. * @param {number} pixelRatio Pixel ratio. * @param {ol.Size} size Size. * @param {ol.proj.Projection} projection Projection; * @return {HTMLCanvasElement} Canvas element. * @private */ ol.source.ImageVector.prototype.canvasFunctionInternal_ = function(extent, resolution, pixelRatio, size, projection) { var replayGroup = new ol.render.canvas.ReplayGroup( ol.renderer.vector.getTolerance(resolution, pixelRatio), extent, resolution, this.source_.getOverlaps(), this.renderBuffer_); this.source_.loadFeatures(extent, resolution, projection); var loading = false; this.source_.forEachFeatureInExtent(extent, /** * @param {ol.Feature} feature Feature. */ function(feature) { loading = loading || this.renderFeature_(feature, resolution, pixelRatio, replayGroup); }, this); replayGroup.finish(); if (loading) { return null; } if (this.canvasSize_[0] != size[0] || this.canvasSize_[1] != size[1]) { this.canvasContext_.canvas.width = size[0]; this.canvasContext_.canvas.height = size[1]; this.canvasSize_[0] = size[0]; this.canvasSize_[1] = size[1]; } else { this.canvasContext_.clearRect(0, 0, size[0], size[1]); } var transform = this.getTransform_(ol.extent.getCenter(extent), resolution, pixelRatio, size); replayGroup.replay(this.canvasContext_, pixelRatio, transform, 0, {}); this.replayGroup_ = replayGroup; return this.canvasContext_.canvas; }; /** * @inheritDoc */ ol.source.ImageVector.prototype.forEachFeatureAtCoordinate = function( coordinate, resolution, rotation, hitTolerance, skippedFeatureUids, callback) { if (!this.replayGroup_) { return undefined; } else { /** @type {Object.<string, boolean>} */ var features = {}; return this.replayGroup_.forEachFeatureAtCoordinate( coordinate, resolution, 0, hitTolerance, skippedFeatureUids, /** * @param {ol.Feature|ol.render.Feature} feature Feature. * @return {?} Callback result. */ function(feature) { var key = ol.getUid(feature).toString(); if (!(key in features)) { features[key] = true; return callback(feature); } }); } }; /** * Get a reference to the wrapped source. * @return {ol.source.Vector} Source. * @api */ ol.source.ImageVector.prototype.getSource = function() { return this.source_; }; /** * Get the style for features. This returns whatever was passed to the `style` * option at construction or to the `setStyle` method. * @return {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction} * Layer style. * @api stable */ ol.source.ImageVector.prototype.getStyle = function() { return this.style_; }; /** * Get the style function. * @return {ol.StyleFunction|undefined} Layer style function. * @api stable */ ol.source.ImageVector.prototype.getStyleFunction = function() { return this.styleFunction_; }; /** * @param {ol.Coordinate} center Center. * @param {number} resolution Resolution. * @param {number} pixelRatio Pixel ratio. * @param {ol.Size} size Size. * @return {!ol.Transform} Transform. * @private */ ol.source.ImageVector.prototype.getTransform_ = function(center, resolution, pixelRatio, size) { var dx1 = size[0] / 2; var dy1 = size[1] / 2; var sx = pixelRatio / resolution; var sy = -sx; var dx2 = -center[0]; var dy2 = -center[1]; return ol.transform.compose(this.transform_, dx1, dy1, sx, sy, 0, dx2, dy2); }; /** * Handle changes in image style state. * @param {ol.events.Event} event Image style change event. * @private */ ol.source.ImageVector.prototype.handleImageChange_ = function(event) { this.changed(); }; /** * @private */ ol.source.ImageVector.prototype.handleSourceChange_ = function() { // setState will trigger a CHANGE event, so we always rely // change events by calling setState. this.setState(this.source_.getState()); }; /** * @param {ol.Feature} feature Feature. * @param {number} resolution Resolution. * @param {number} pixelRatio Pixel ratio. * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group. * @return {boolean} `true` if an image is loading. * @private */ ol.source.ImageVector.prototype.renderFeature_ = function(feature, resolution, pixelRatio, replayGroup) { var styles; var styleFunction = feature.getStyleFunction(); if (styleFunction) { styles = styleFunction.call(feature, resolution); } else if (this.styleFunction_) { styles = this.styleFunction_(feature, resolution); } if (!styles) { return false; } var i, ii, loading = false; if (!Array.isArray(styles)) { styles = [styles]; } for (i = 0, ii = styles.length; i < ii; ++i) { loading = ol.renderer.vector.renderFeature( replayGroup, feature, styles[i], ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), this.handleImageChange_, this) || loading; } return loading; }; /** * Set the style for features. This can be a single style object, an array * of styles, or a function that takes a feature and resolution and returns * an array of styles. If it is `undefined` the default style is used. If * it is `null` the layer has no style (a `null` style), so only features * that have their own styles will be rendered in the layer. See * {@link ol.style} for information on the default style. * @param {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction|undefined} * style Layer style. * @api stable */ ol.source.ImageVector.prototype.setStyle = function(style) { this.style_ = style !== undefined ? style : ol.style.Style.defaultFunction; this.styleFunction_ = !style ? undefined : ol.style.Style.createFunction(this.style_); this.changed(); };