From 6c052c0dab52c98bacd0b7a781d1ab27be3a0cec Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 23 Oct 2018 09:07:27 -0600 Subject: [PATCH 01/15] Split vector layer into Canvas and WebGL implementations --- src/ol/layer/BaseVector.js | 284 ++++++++++++++++++++++++++++++++++++ src/ol/layer/Vector.js | 240 ++---------------------------- src/ol/layer/WebGLVector.js | 41 ++++++ 3 files changed, 336 insertions(+), 229 deletions(-) create mode 100644 src/ol/layer/BaseVector.js create mode 100644 src/ol/layer/WebGLVector.js diff --git a/src/ol/layer/BaseVector.js b/src/ol/layer/BaseVector.js new file mode 100644 index 0000000000..2688bdbc5a --- /dev/null +++ b/src/ol/layer/BaseVector.js @@ -0,0 +1,284 @@ +/** + * @module ol/layer/BaseVector + */ +import LayerType from '../LayerType.js'; +import Layer from './Layer.js'; +import VectorRenderType from './VectorRenderType.js'; +import {assign} from '../obj.js'; +import {createDefaultStyle, toFunction as toStyleFunction} from '../style/Style.js'; + + +/** + * @typedef {Object} Options + * @property {number} [opacity=1] Opacity (0, 1). + * @property {boolean} [visible=true] Visibility. + * @property {import("../extent.js").Extent} [extent] The bounding extent for layer rendering. The layer will not be + * rendered outside of this extent. + * @property {number} [zIndex] The z-index for layer rendering. At rendering time, the layers + * will be ordered, first by Z-index and then by position. When `undefined`, a `zIndex` of 0 is assumed + * for layers that are added to the map's `layers` collection, or `Infinity` when the layer's `setMap()` + * method was used. + * @property {number} [minResolution] The minimum resolution (inclusive) at which this layer will be + * visible. + * @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will + * be visible. + * @property {import("../render.js").OrderFunction} [renderOrder] Render order. Function to be used when sorting + * features before rendering. By default features are drawn in the order that they are created. Use + * `null` to avoid the sort, but get an undefined draw order. + * @property {number} [renderBuffer=100] The buffer in pixels around the viewport extent used by the + * renderer when getting features from the vector source for the rendering or hit-detection. + * Recommended value: the size of the largest symbol, line width or label. + * @property {import("./VectorRenderType.js").default|string} [renderMode='vector'] Render mode for vector layers: + * * `'image'`: Vector layers are rendered as images. Great performance, but point symbols and + * texts are always rotated with the view and pixels are scaled during zoom animations. + * * `'vector'`: Vector layers are rendered as vectors. Most accurate rendering even during + * animations, but slower performance. + * @property {import("../source/Vector.js").default} [source] Source. + * @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage + * this layer in its layers collection, and the layer will be rendered on top. This is useful for + * temporary layers. The standard way to add a layer to a map and have it managed by the map is to + * use {@link module:ol/Map#addLayer}. + * @property {boolean} [declutter=false] Declutter images and text. Decluttering is applied to all + * image and text styles, and the priority is defined by the z-index of the style. Lower z-index + * means higher priority. + * @property {import("../style/Style.js").StyleLike} [style] Layer style. See + * {@link module:ol/style} for default style which will be used if this is not defined. + * @property {boolean} [updateWhileAnimating=false] When set to `true` and `renderMode` + * is `vector`, feature batches will be recreated during animations. This means that no + * vectors will be shown clipped, but the setting will have a performance impact for large + * amounts of vector data. When set to `false`, batches will be recreated when no animation + * is active. + * @property {boolean} [updateWhileInteracting=false] When set to `true` and `renderMode` + * is `vector`, feature batches will be recreated during interactions. See also + * `updateWhileAnimating`. + */ + + +/** + * @enum {string} + * @private + */ +const Property = { + RENDER_ORDER: 'renderOrder' +}; + + +/** + * @classdesc + * Vector data that is rendered client-side. + * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @api + */ +class BaseVectorLayer extends Layer { + /** + * @param {Options=} opt_options Options. + */ + constructor(opt_options) { + const options = opt_options ? + opt_options : /** @type {Options} */ ({}); + + const baseOptions = assign({}, options); + + delete baseOptions.style; + delete baseOptions.renderBuffer; + delete baseOptions.updateWhileAnimating; + delete baseOptions.updateWhileInteracting; + super(baseOptions); + + /** + * @private + * @type {boolean} + */ + this.declutter_ = options.declutter !== undefined ? options.declutter : false; + + /** + * @type {number} + * @private + */ + this.renderBuffer_ = options.renderBuffer !== undefined ? + options.renderBuffer : 100; + + /** + * User provided style. + * @type {import("../style/Style.js").StyleLike} + * @private + */ + this.style_ = null; + + /** + * Style function for use within the library. + * @type {import("../style/Style.js").StyleFunction|undefined} + * @private + */ + this.styleFunction_ = undefined; + + this.setStyle(options.style); + + /** + * @type {boolean} + * @private + */ + this.updateWhileAnimating_ = options.updateWhileAnimating !== undefined ? + options.updateWhileAnimating : false; + + /** + * @type {boolean} + * @private + */ + this.updateWhileInteracting_ = options.updateWhileInteracting !== undefined ? + options.updateWhileInteracting : false; + + /** + * @private + * @type {import("./VectorTileRenderType.js").default|string} + */ + this.renderMode_ = options.renderMode || VectorRenderType.VECTOR; + + /** + * The layer type. + * @protected + * @type {import("../LayerType.js").default} + */ + this.type = LayerType.VECTOR; + + /** + * @private + * @type {import("../renderer/Layer.js").default} + */ + this.renderer_ = null; + } + + /** + * @return {boolean} Declutter. + */ + getDeclutter() { + return this.declutter_; + } + + /** + * @param {boolean} declutter Declutter. + */ + setDeclutter(declutter) { + this.declutter_ = declutter; + } + + /** + * @return {number|undefined} Render buffer. + */ + getRenderBuffer() { + return this.renderBuffer_; + } + + /** + * @return {function(import("../Feature.js").default, import("../Feature.js").default): number|null|undefined} Render + * order. + */ + getRenderOrder() { + return ( + /** @type {import("../render.js").OrderFunction|null|undefined} */ (this.get(Property.RENDER_ORDER)) + ); + } + + /** + * Get the style for features. This returns whatever was passed to the `style` + * option at construction or to the `setStyle` method. + * @return {import("../style/Style.js").StyleLike} + * Layer style. + * @api + */ + getStyle() { + return this.style_; + } + + /** + * Get the style function. + * @return {import("../style/Style.js").StyleFunction|undefined} Layer style function. + * @api + */ + getStyleFunction() { + return this.styleFunction_; + } + + /** + * @return {boolean} Whether the rendered layer should be updated while + * animating. + */ + getUpdateWhileAnimating() { + return this.updateWhileAnimating_; + } + + /** + * @return {boolean} Whether the rendered layer should be updated while + * interacting. + */ + getUpdateWhileInteracting() { + return this.updateWhileInteracting_; + } + + /** + * @param {import("../render.js").OrderFunction|null|undefined} renderOrder + * Render order. + */ + setRenderOrder(renderOrder) { + this.set(Property.RENDER_ORDER, renderOrder); + } + + /** + * 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 module:ol/style} for information on the default style. + * @param {import("../style/Style.js").default|Array|import("../style/Style.js").StyleFunction|null|undefined} style Layer style. + * @api + */ + setStyle(style) { + this.style_ = style !== undefined ? style : createDefaultStyle; + this.styleFunction_ = style === null ? + undefined : toStyleFunction(this.style_); + this.changed(); + } + + /** + * @return {import("./VectorRenderType.js").default|string} The render mode. + */ + getRenderMode() { + return this.renderMode_; + } + + /** + * Get the renderer for this layer. + * @return {import("../renderer/Layer.js").default} The layer renderer. + */ + getRenderer() { + if (!this.renderer_) { + this.renderer_ = this.createRenderer(); + } + return this.renderer_; + } + + /** + * Create a renderer for this layer. + * @return {import("../renderer/Layer.js").default} A layer renderer. + * @protected + */ + createRenderer() { + return null; + } +} + + +/** + * Return the associated {@link module:ol/source/Vector vectorsource} of the layer. + * @function + * @return {import("../source/Vector.js").default} Source. + * @api + */ +BaseVectorLayer.prototype.getSource; + + +export default BaseVectorLayer; diff --git a/src/ol/layer/Vector.js b/src/ol/layer/Vector.js index bdf0c62344..0456b04ea0 100644 --- a/src/ol/layer/Vector.js +++ b/src/ol/layer/Vector.js @@ -1,68 +1,15 @@ /** * @module ol/layer/Vector */ -import LayerType from '../LayerType.js'; -import Layer from './Layer.js'; -import VectorRenderType from './VectorRenderType.js'; -import {assign} from '../obj.js'; -import {createDefaultStyle, toFunction as toStyleFunction} from '../style/Style.js'; +import BaseVectorLayer from './BaseVector.js'; +import CanvasVectorLayerRenderer from '../renderer/canvas/VectorLayer.js'; /** - * @typedef {Object} Options - * @property {number} [opacity=1] Opacity (0, 1). - * @property {boolean} [visible=true] Visibility. - * @property {import("../extent.js").Extent} [extent] The bounding extent for layer rendering. The layer will not be - * rendered outside of this extent. - * @property {number} [zIndex] The z-index for layer rendering. At rendering time, the layers - * will be ordered, first by Z-index and then by position. When `undefined`, a `zIndex` of 0 is assumed - * for layers that are added to the map's `layers` collection, or `Infinity` when the layer's `setMap()` - * method was used. - * @property {number} [minResolution] The minimum resolution (inclusive) at which this layer will be - * visible. - * @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will - * be visible. - * @property {import("../render.js").OrderFunction} [renderOrder] Render order. Function to be used when sorting - * features before rendering. By default features are drawn in the order that they are created. Use - * `null` to avoid the sort, but get an undefined draw order. - * @property {number} [renderBuffer=100] The buffer in pixels around the viewport extent used by the - * renderer when getting features from the vector source for the rendering or hit-detection. - * Recommended value: the size of the largest symbol, line width or label. - * @property {import("./VectorRenderType.js").default|string} [renderMode='vector'] Render mode for vector layers: - * * `'image'`: Vector layers are rendered as images. Great performance, but point symbols and - * texts are always rotated with the view and pixels are scaled during zoom animations. - * * `'vector'`: Vector layers are rendered as vectors. Most accurate rendering even during - * animations, but slower performance. - * @property {import("../source/Vector.js").default} [source] Source. - * @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage - * this layer in its layers collection, and the layer will be rendered on top. This is useful for - * temporary layers. The standard way to add a layer to a map and have it managed by the map is to - * use {@link module:ol/Map#addLayer}. - * @property {boolean} [declutter=false] Declutter images and text. Decluttering is applied to all - * image and text styles, and the priority is defined by the z-index of the style. Lower z-index - * means higher priority. - * @property {import("../style/Style.js").StyleLike} [style] Layer style. See - * {@link module:ol/style} for default style which will be used if this is not defined. - * @property {boolean} [updateWhileAnimating=false] When set to `true` and `renderMode` - * is `vector`, feature batches will be recreated during animations. This means that no - * vectors will be shown clipped, but the setting will have a performance impact for large - * amounts of vector data. When set to `false`, batches will be recreated when no animation - * is active. - * @property {boolean} [updateWhileInteracting=false] When set to `true` and `renderMode` - * is `vector`, feature batches will be recreated during interactions. See also - * `updateWhileAnimating`. + * @typedef {import("./BaseVector.js").Options} Options */ -/** - * @enum {string} - * @private - */ -const Property = { - RENDER_ORDER: 'renderOrder' -}; - - /** * @classdesc * Vector data that is rendered client-side. @@ -72,188 +19,23 @@ const Property = { * * @api */ -class VectorLayer extends Layer { +class VectorLayer extends BaseVectorLayer { /** * @param {Options=} opt_options Options. */ constructor(opt_options) { - const options = opt_options ? - opt_options : /** @type {Options} */ ({}); - - const baseOptions = assign({}, options); - - delete baseOptions.style; - delete baseOptions.renderBuffer; - delete baseOptions.updateWhileAnimating; - delete baseOptions.updateWhileInteracting; - super(baseOptions); - - /** - * @private - * @type {boolean} - */ - this.declutter_ = options.declutter !== undefined ? options.declutter : false; - - /** - * @type {number} - * @private - */ - this.renderBuffer_ = options.renderBuffer !== undefined ? - options.renderBuffer : 100; - - /** - * User provided style. - * @type {import("../style/Style.js").StyleLike} - * @private - */ - this.style_ = null; - - /** - * Style function for use within the library. - * @type {import("../style/Style.js").StyleFunction|undefined} - * @private - */ - this.styleFunction_ = undefined; - - this.setStyle(options.style); - - /** - * @type {boolean} - * @private - */ - this.updateWhileAnimating_ = options.updateWhileAnimating !== undefined ? - options.updateWhileAnimating : false; - - /** - * @type {boolean} - * @private - */ - this.updateWhileInteracting_ = options.updateWhileInteracting !== undefined ? - options.updateWhileInteracting : false; - - /** - * @private - * @type {import("./VectorTileRenderType.js").default|string} - */ - this.renderMode_ = options.renderMode || VectorRenderType.VECTOR; - - /** - * The layer type. - * @protected - * @type {import("../LayerType.js").default} - */ - this.type = LayerType.VECTOR; - + super(opt_options); } /** - * @return {boolean} Declutter. - */ - getDeclutter() { - return this.declutter_; - } - - /** - * @param {boolean} declutter Declutter. - */ - setDeclutter(declutter) { - this.declutter_ = declutter; - } - - /** - * @return {number|undefined} Render buffer. - */ - getRenderBuffer() { - return this.renderBuffer_; - } - - /** - * @return {function(import("../Feature.js").default, import("../Feature.js").default): number|null|undefined} Render - * order. - */ - getRenderOrder() { - return ( - /** @type {import("../render.js").OrderFunction|null|undefined} */ (this.get(Property.RENDER_ORDER)) - ); - } - - /** - * Get the style for features. This returns whatever was passed to the `style` - * option at construction or to the `setStyle` method. - * @return {import("../style/Style.js").StyleLike} - * Layer style. - * @api - */ - getStyle() { - return this.style_; - } - - /** - * Get the style function. - * @return {import("../style/Style.js").StyleFunction|undefined} Layer style function. - * @api - */ - getStyleFunction() { - return this.styleFunction_; - } - - /** - * @return {boolean} Whether the rendered layer should be updated while - * animating. - */ - getUpdateWhileAnimating() { - return this.updateWhileAnimating_; - } - - /** - * @return {boolean} Whether the rendered layer should be updated while - * interacting. - */ - getUpdateWhileInteracting() { - return this.updateWhileInteracting_; - } - - /** - * @param {import("../render.js").OrderFunction|null|undefined} renderOrder - * Render order. - */ - setRenderOrder(renderOrder) { - this.set(Property.RENDER_ORDER, renderOrder); - } - - /** - * 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 module:ol/style} for information on the default style. - * @param {import("../style/Style.js").default|Array|import("../style/Style.js").StyleFunction|null|undefined} style Layer style. - * @api - */ - setStyle(style) { - this.style_ = style !== undefined ? style : createDefaultStyle; - this.styleFunction_ = style === null ? - undefined : toStyleFunction(this.style_); - this.changed(); - } - - /** - * @return {import("./VectorRenderType.js").default|string} The render mode. - */ - getRenderMode() { - return this.renderMode_; + * Create a renderer for this layer. + * @return {import("../renderer/Layer.js").default} A layer renderer. + * @protected + */ + createRenderer() { + return new CanvasVectorLayerRenderer(this); } } -/** - * Return the associated {@link module:ol/source/Vector vectorsource} of the layer. - * @function - * @return {import("../source/Vector.js").default} Source. - * @api - */ -VectorLayer.prototype.getSource; - - export default VectorLayer; diff --git a/src/ol/layer/WebGLVector.js b/src/ol/layer/WebGLVector.js new file mode 100644 index 0000000000..03a2c6d109 --- /dev/null +++ b/src/ol/layer/WebGLVector.js @@ -0,0 +1,41 @@ +/** + * @module ol/layer/WebGLVector + */ +import BaseVectorLayer from './BaseVector.js'; + + +/** + * @typedef {import("./BaseVector.js").Options} Options + */ + + +/** + * @classdesc + * Vector data that is rendered client-side. + * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @api + */ +class WebGLVectorLayer extends BaseVectorLayer { + /** + * @param {Options=} opt_options Options. + */ + constructor(opt_options) { + super(opt_options); + } + + /** + * Create a renderer for this layer. + * @return {import("../renderer/Layer.js").default} A layer renderer. + * @protected + */ + createRenderer() { + // TODO: rework WebGL renderers to share context + return null; + } +} + + +export default WebGLVectorLayer; From 7ffaa134c7e93fb598710b779bc11c62ee1004fa Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 23 Oct 2018 13:13:41 -0600 Subject: [PATCH 02/15] Conditionally get renderer from the layer --- src/ol/layer/BaseVector.js | 26 -------------------------- src/ol/layer/Layer.js | 27 +++++++++++++++++++++++++++ src/ol/renderer/Map.js | 14 ++++++++------ 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/ol/layer/BaseVector.js b/src/ol/layer/BaseVector.js index 2688bdbc5a..98950bd2e9 100644 --- a/src/ol/layer/BaseVector.js +++ b/src/ol/layer/BaseVector.js @@ -143,12 +143,6 @@ class BaseVectorLayer extends Layer { * @type {import("../LayerType.js").default} */ this.type = LayerType.VECTOR; - - /** - * @private - * @type {import("../renderer/Layer.js").default} - */ - this.renderer_ = null; } /** @@ -249,26 +243,6 @@ class BaseVectorLayer extends Layer { getRenderMode() { return this.renderMode_; } - - /** - * Get the renderer for this layer. - * @return {import("../renderer/Layer.js").default} The layer renderer. - */ - getRenderer() { - if (!this.renderer_) { - this.renderer_ = this.createRenderer(); - } - return this.renderer_; - } - - /** - * Create a renderer for this layer. - * @return {import("../renderer/Layer.js").default} A layer renderer. - * @protected - */ - createRenderer() { - return null; - } } diff --git a/src/ol/layer/Layer.js b/src/ol/layer/Layer.js index 8ef367acd4..9a0d413b01 100644 --- a/src/ol/layer/Layer.js +++ b/src/ol/layer/Layer.js @@ -92,6 +92,12 @@ class Layer extends BaseLayer { */ this.sourceChangeKey_ = null; + /** + * @private + * @type {import("../renderer/Layer.js").default} + */ + this.renderer_ = null; + if (options.map) { this.setMap(options.map); } @@ -215,6 +221,27 @@ class Layer extends BaseLayer { setSource(source) { this.set(LayerProperty.SOURCE, source); } + + /** + * Get the renderer for this layer. + * @return {import("../renderer/Layer.js").default} The layer renderer. + */ + getRenderer() { + if (!this.renderer_) { + this.renderer_ = this.createRenderer(); + } + return this.renderer_; + } + + /** + * Create a renderer for this layer. + * @return {import("../renderer/Layer.js").default} A layer renderer. + * @protected + */ + createRenderer() { + return null; + } + } diff --git a/src/ol/renderer/Map.js b/src/ol/renderer/Map.js index 9acca5e2e9..157dce98d5 100644 --- a/src/ol/renderer/Map.js +++ b/src/ol/renderer/Map.js @@ -217,12 +217,14 @@ class MapRenderer extends Disposable { if (layerKey in this.layerRenderers_) { return this.layerRenderers_[layerKey]; } else { - let renderer; - for (let i = 0, ii = this.layerRendererConstructors_.length; i < ii; ++i) { - const candidate = this.layerRendererConstructors_[i]; - if (candidate['handles'](layer)) { - renderer = candidate['create'](this, layer); - break; + let renderer = layer.getRenderer(); + if (!renderer) { + for (let i = 0, ii = this.layerRendererConstructors_.length; i < ii; ++i) { + const candidate = this.layerRendererConstructors_[i]; + if (candidate['handles'](layer)) { + renderer = candidate['create'](this, layer); + break; + } } } if (renderer) { From c9529b9acdd73f6c5d1826ed75f82aca713a3f5e Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 4 Nov 2018 09:19:03 -0700 Subject: [PATCH 03/15] Create the appropriate renderer for vector tile layers --- src/ol/layer/VectorTile.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/ol/layer/VectorTile.js b/src/ol/layer/VectorTile.js index 1397dd14cb..a40caa1057 100644 --- a/src/ol/layer/VectorTile.js +++ b/src/ol/layer/VectorTile.js @@ -4,8 +4,9 @@ import LayerType from '../LayerType.js'; import {assert} from '../asserts.js'; import TileProperty from './TileProperty.js'; -import VectorLayer from './Vector.js'; +import BaseVectorLayer from './BaseVector.js'; import VectorTileRenderType from './VectorTileRenderType.js'; +import CanvasVectorTileLayerRenderer from '../renderer/canvas/VectorTileLayer.js'; import {assign} from '../obj.js'; @@ -78,7 +79,7 @@ import {assign} from '../obj.js'; * @param {Options=} opt_options Options. * @api */ -class VectorTileLayer extends VectorLayer { +class VectorTileLayer extends BaseVectorLayer { /** * @param {Options=} opt_options Options. */ @@ -112,7 +113,15 @@ class VectorTileLayer extends VectorLayer { * @type {import("../LayerType.js").default} */ this.type = LayerType.VECTOR_TILE; + } + /** + * Create a renderer for this layer. + * @return {import("../renderer/Layer.js").default} A layer renderer. + * @protected + */ + createRenderer() { + return new CanvasVectorTileLayerRenderer(this); } /** From 285a610c46dfa6350b40ce587027a97e73bfbc79 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 4 Nov 2018 09:34:07 -0700 Subject: [PATCH 04/15] Base for image layers, renderer-specific image layer types --- src/ol/layer/BaseImage.js | 69 ++++++++++++++++++++++++++++++++++++++ src/ol/layer/Image.js | 51 +++++++--------------------- src/ol/layer/WebGLImage.js | 44 ++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 38 deletions(-) create mode 100644 src/ol/layer/BaseImage.js create mode 100644 src/ol/layer/WebGLImage.js diff --git a/src/ol/layer/BaseImage.js b/src/ol/layer/BaseImage.js new file mode 100644 index 0000000000..1d02eeadc5 --- /dev/null +++ b/src/ol/layer/BaseImage.js @@ -0,0 +1,69 @@ +/** + * @module ol/layer/BaseImage + */ +import LayerType from '../LayerType.js'; +import Layer from './Layer.js'; + + +/** + * @typedef {Object} Options + * @property {number} [opacity=1] Opacity (0, 1). + * @property {boolean} [visible=true] Visibility. + * @property {import("../extent.js").Extent} [extent] The bounding extent for layer rendering. The layer will not be + * rendered outside of this extent. + * @property {number} [zIndex] The z-index for layer rendering. At rendering time, the layers + * will be ordered, first by Z-index and then by position. When `undefined`, a `zIndex` of 0 is assumed + * for layers that are added to the map's `layers` collection, or `Infinity` when the layer's `setMap()` + * method was used. + * @property {number} [minResolution] The minimum resolution (inclusive) at which this layer will be + * visible. + * @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will + * be visible. + * @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage + * this layer in its layers collection, and the layer will be rendered on top. This is useful for + * temporary layers. The standard way to add a layer to a map and have it managed by the map is to + * use {@link module:ol/Map#addLayer}. + * @property {import("../source/Image.js").default} [source] Source for this layer. + */ + + +/** + * @classdesc + * Server-rendered images that are available for arbitrary extents and + * resolutions. + * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @fires import("../render/Event.js").RenderEvent + * @api + */ +class BaseImageLayer extends Layer { + + /** + * @param {Options=} opt_options Layer options. + */ + constructor(opt_options) { + const options = opt_options ? opt_options : {}; + super(options); + + /** + * The layer type. + * @protected + * @type {import("../LayerType.js").default} + */ + this.type = LayerType.IMAGE; + + } + +} + + +/** + * Return the associated {@link module:ol/source/Image source} of the image layer. + * @function + * @return {import("../source/Image.js").default} Source. + * @api + */ +BaseImageLayer.prototype.getSource; +export default BaseImageLayer; diff --git a/src/ol/layer/Image.js b/src/ol/layer/Image.js index 221c2d1a97..2a83a18cc9 100644 --- a/src/ol/layer/Image.js +++ b/src/ol/layer/Image.js @@ -1,29 +1,12 @@ /** * @module ol/layer/Image */ -import LayerType from '../LayerType.js'; -import Layer from './Layer.js'; +import BaseImageLayer from './BaseImage.js'; +import CanvasImageLayerRenderer from '../renderer/canvas/ImageLayer.js'; /** - * @typedef {Object} Options - * @property {number} [opacity=1] Opacity (0, 1). - * @property {boolean} [visible=true] Visibility. - * @property {import("../extent.js").Extent} [extent] The bounding extent for layer rendering. The layer will not be - * rendered outside of this extent. - * @property {number} [zIndex] The z-index for layer rendering. At rendering time, the layers - * will be ordered, first by Z-index and then by position. When `undefined`, a `zIndex` of 0 is assumed - * for layers that are added to the map's `layers` collection, or `Infinity` when the layer's `setMap()` - * method was used. - * @property {number} [minResolution] The minimum resolution (inclusive) at which this layer will be - * visible. - * @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will - * be visible. - * @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage - * this layer in its layers collection, and the layer will be rendered on top. This is useful for - * temporary layers. The standard way to add a layer to a map and have it managed by the map is to - * use {@link module:ol/Map#addLayer}. - * @property {import("../source/Image.js").default} [source] Source for this layer. + * @typedef {import("./BaseImage.js").Options} Options */ @@ -38,32 +21,24 @@ import Layer from './Layer.js'; * @fires import("../render/Event.js").RenderEvent * @api */ -class ImageLayer extends Layer { +class ImageLayer extends BaseImageLayer { /** * @param {Options=} opt_options Layer options. */ constructor(opt_options) { - const options = opt_options ? opt_options : {}; - super(options); - - /** - * The layer type. - * @protected - * @type {import("../LayerType.js").default} - */ - this.type = LayerType.IMAGE; + super(opt_options); + } + /** + * Create a renderer for this layer. + * @return {import("../renderer/Layer.js").default} A layer renderer. + * @protected + */ + createRenderer() { + return new CanvasImageLayerRenderer(this); } } - -/** - * Return the associated {@link module:ol/source/Image source} of the image layer. - * @function - * @return {import("../source/Image.js").default} Source. - * @api - */ -ImageLayer.prototype.getSource; export default ImageLayer; diff --git a/src/ol/layer/WebGLImage.js b/src/ol/layer/WebGLImage.js new file mode 100644 index 0000000000..cb8fa40ecb --- /dev/null +++ b/src/ol/layer/WebGLImage.js @@ -0,0 +1,44 @@ +/** + * @module ol/layer/WebGLImage + */ +import BaseImageLayer from './BaseImage.js'; + + +/** + * @typedef {import("./BaseImage.js").Options} Options + */ + + +/** + * @classdesc + * Server-rendered images that are available for arbitrary extents and + * resolutions. + * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @fires import("../render/Event.js").RenderEvent + * @api + */ +class WebGLImageLayer extends BaseImageLayer { + + /** + * @param {Options=} opt_options Layer options. + */ + constructor(opt_options) { + super(opt_options); + } + + /** + * Create a renderer for this layer. + * @return {import("../renderer/Layer.js").default} A layer renderer. + * @protected + */ + createRenderer() { + // TODO: rework WebGL renderers to share context + return null; + } + +} + +export default WebGLImageLayer; From ae1f3afd66cbd96368789fd3ab886362d2138f46 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 4 Nov 2018 09:42:28 -0700 Subject: [PATCH 05/15] Base for tile layers, renderer-specific tile layer types --- src/ol/layer/BaseTile.js | 122 ++++++++++++++++++++++++++++++++++++++ src/ol/layer/Tile.js | 104 ++++---------------------------- src/ol/layer/WebGLTile.js | 43 ++++++++++++++ 3 files changed, 178 insertions(+), 91 deletions(-) create mode 100644 src/ol/layer/BaseTile.js create mode 100644 src/ol/layer/WebGLTile.js diff --git a/src/ol/layer/BaseTile.js b/src/ol/layer/BaseTile.js new file mode 100644 index 0000000000..cc9f876093 --- /dev/null +++ b/src/ol/layer/BaseTile.js @@ -0,0 +1,122 @@ +/** + * @module ol/layer/BaseTile + */ +import LayerType from '../LayerType.js'; +import Layer from './Layer.js'; +import TileProperty from './TileProperty.js'; +import {assign} from '../obj.js'; + + +/** + * @typedef {Object} Options + * @property {number} [opacity=1] Opacity (0, 1). + * @property {boolean} [visible=true] Visibility. + * @property {import("../extent.js").Extent} [extent] The bounding extent for layer rendering. The layer will not be + * rendered outside of this extent. + * @property {number} [zIndex] The z-index for layer rendering. At rendering time, the layers + * will be ordered, first by Z-index and then by position. When `undefined`, a `zIndex` of 0 is assumed + * for layers that are added to the map's `layers` collection, or `Infinity` when the layer's `setMap()` + * method was used. + * @property {number} [minResolution] The minimum resolution (inclusive) at which this layer will be + * visible. + * @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will + * be visible. + * @property {number} [preload=0] Preload. Load low-resolution tiles up to `preload` levels. `0` + * means no preloading. + * @property {import("../source/Tile.js").default} [source] Source for this layer. + * @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage + * this layer in its layers collection, and the layer will be rendered on top. This is useful for + * temporary layers. The standard way to add a layer to a map and have it managed by the map is to + * use {@link module:ol/Map#addLayer}. + * @property {boolean} [useInterimTilesOnError=true] Use interim tiles on error. + */ + + +/** + * @classdesc + * For layer sources that provide pre-rendered, tiled images in grids that are + * organized by zoom levels for specific resolutions. + * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @api + */ +class BaseTileLayer extends Layer { + /** + * @param {Options=} opt_options Tile layer options. + */ + constructor(opt_options) { + const options = opt_options ? opt_options : {}; + + const baseOptions = assign({}, options); + + delete baseOptions.preload; + delete baseOptions.useInterimTilesOnError; + super(baseOptions); + + this.setPreload(options.preload !== undefined ? options.preload : 0); + this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? + options.useInterimTilesOnError : true); + + /** + * The layer type. + * @protected + * @type {import("../LayerType.js").default} + */ + this.type = LayerType.TILE; + + } + + /** + * Return the level as number to which we will preload tiles up to. + * @return {number} The level to preload tiles up to. + * @observable + * @api + */ + getPreload() { + return /** @type {number} */ (this.get(TileProperty.PRELOAD)); + } + + /** + * Set the level as number to which we will preload tiles up to. + * @param {number} preload The level to preload tiles up to. + * @observable + * @api + */ + setPreload(preload) { + this.set(TileProperty.PRELOAD, preload); + } + + /** + * Whether we use interim tiles on error. + * @return {boolean} Use interim tiles on error. + * @observable + * @api + */ + getUseInterimTilesOnError() { + return /** @type {boolean} */ (this.get(TileProperty.USE_INTERIM_TILES_ON_ERROR)); + } + + /** + * Set whether we use interim tiles on error. + * @param {boolean} useInterimTilesOnError Use interim tiles on error. + * @observable + * @api + */ + setUseInterimTilesOnError(useInterimTilesOnError) { + this.set(TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError); + } +} + + +/** + * Return the associated {@link module:ol/source/Tile tilesource} of the layer. + * @function + * @return {import("../source/Tile.js").default} Source. + * @api + */ +BaseTileLayer.prototype.getSource; + + +export default BaseTileLayer; diff --git a/src/ol/layer/Tile.js b/src/ol/layer/Tile.js index 36833297cd..6e25f2af6b 100644 --- a/src/ol/layer/Tile.js +++ b/src/ol/layer/Tile.js @@ -1,36 +1,15 @@ /** * @module ol/layer/Tile */ -import LayerType from '../LayerType.js'; -import Layer from './Layer.js'; -import TileProperty from './TileProperty.js'; -import {assign} from '../obj.js'; +import BaseTileLayer from './BaseTile.js'; +import CanvasTileLayerRenderer from '../renderer/canvas/TileLayer.js'; /** - * @typedef {Object} Options - * @property {number} [opacity=1] Opacity (0, 1). - * @property {boolean} [visible=true] Visibility. - * @property {import("../extent.js").Extent} [extent] The bounding extent for layer rendering. The layer will not be - * rendered outside of this extent. - * @property {number} [zIndex] The z-index for layer rendering. At rendering time, the layers - * will be ordered, first by Z-index and then by position. When `undefined`, a `zIndex` of 0 is assumed - * for layers that are added to the map's `layers` collection, or `Infinity` when the layer's `setMap()` - * method was used. - * @property {number} [minResolution] The minimum resolution (inclusive) at which this layer will be - * visible. - * @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will - * be visible. - * @property {number} [preload=0] Preload. Load low-resolution tiles up to `preload` levels. `0` - * means no preloading. - * @property {import("../source/Tile.js").default} [source] Source for this layer. - * @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage - * this layer in its layers collection, and the layer will be rendered on top. This is useful for - * temporary layers. The standard way to add a layer to a map and have it managed by the map is to - * use {@link module:ol/Map#addLayer}. - * @property {boolean} [useInterimTilesOnError=true] Use interim tiles on error. + * @typedef {import("./BaseTile.js").Options} Options */ + /** * @classdesc * For layer sources that provide pre-rendered, tiled images in grids that are @@ -41,81 +20,24 @@ import {assign} from '../obj.js'; * * @api */ -class TileLayer extends Layer { +class TileLayer extends BaseTileLayer { + /** * @param {Options=} opt_options Tile layer options. */ constructor(opt_options) { - const options = opt_options ? opt_options : {}; - - const baseOptions = assign({}, options); - - delete baseOptions.preload; - delete baseOptions.useInterimTilesOnError; - super(baseOptions); - - this.setPreload(options.preload !== undefined ? options.preload : 0); - this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? - options.useInterimTilesOnError : true); - - /** - * The layer type. - * @protected - * @type {import("../LayerType.js").default} - */ - this.type = LayerType.TILE; - + super(opt_options); } /** - * Return the level as number to which we will preload tiles up to. - * @return {number} The level to preload tiles up to. - * @observable - * @api - */ - getPreload() { - return /** @type {number} */ (this.get(TileProperty.PRELOAD)); + * Create a renderer for this layer. + * @return {import("../renderer/Layer.js").default} A layer renderer. + * @protected + */ + createRenderer() { + return new CanvasTileLayerRenderer(this); } - /** - * Set the level as number to which we will preload tiles up to. - * @param {number} preload The level to preload tiles up to. - * @observable - * @api - */ - setPreload(preload) { - this.set(TileProperty.PRELOAD, preload); - } - - /** - * Whether we use interim tiles on error. - * @return {boolean} Use interim tiles on error. - * @observable - * @api - */ - getUseInterimTilesOnError() { - return /** @type {boolean} */ (this.get(TileProperty.USE_INTERIM_TILES_ON_ERROR)); - } - - /** - * Set whether we use interim tiles on error. - * @param {boolean} useInterimTilesOnError Use interim tiles on error. - * @observable - * @api - */ - setUseInterimTilesOnError(useInterimTilesOnError) { - this.set(TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError); - } } - -/** - * Return the associated {@link module:ol/source/Tile tilesource} of the layer. - * @function - * @return {import("../source/Tile.js").default} Source. - * @api - */ -TileLayer.prototype.getSource; - - export default TileLayer; diff --git a/src/ol/layer/WebGLTile.js b/src/ol/layer/WebGLTile.js new file mode 100644 index 0000000000..a40095d952 --- /dev/null +++ b/src/ol/layer/WebGLTile.js @@ -0,0 +1,43 @@ +/** + * @module ol/layer/WebGLTile + */ +import BaseTileLayer from './BaseTile.js'; + + +/** + * @typedef {import("./BaseTile.js").Options} Options + */ + + +/** + * @classdesc + * For layer sources that provide pre-rendered, tiled images in grids that are + * organized by zoom levels for specific resolutions. + * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @api + */ +class WebGLTileLayer extends BaseTileLayer { + + /** + * @param {Options=} opt_options Tile layer options. + */ + constructor(opt_options) { + super(opt_options); + } + + /** + * Create a renderer for this layer. + * @return {import("../renderer/Layer.js").default} A layer renderer. + * @protected + */ + createRenderer() { + // TODO: rework WebGL renderers to share context + return null; + } + +} + +export default WebGLTileLayer; From 2ec509fbca5a383c3b64faee5be4789699edfe5a Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 4 Nov 2018 11:25:36 -0700 Subject: [PATCH 06/15] Add an image vector layer for rendering vectors to an image --- examples/drag-and-drop-image-vector.html | 2 +- examples/drag-and-drop-image-vector.js | 5 +- examples/image-vector-layer.html | 6 +- examples/image-vector-layer.js | 4 +- src/ol/layer.js | 1 + src/ol/layer/BaseVector.js | 32 +- src/ol/layer/Heatmap.js | 5 - src/ol/layer/VectorImage.js | 40 +++ src/ol/layer/VectorRenderType.js | 18 -- src/ol/layer/VectorTile.js | 25 +- src/ol/renderer/canvas/ImageLayer.js | 92 +----- src/ol/renderer/canvas/VectorImageLayer.js | 129 ++++++++ src/ol/renderer/canvas/VectorTileLayer.js | 6 +- src/ol/source/Raster.js | 57 +--- test/rendering/ol/layer/vector.test.js | 238 --------------- test/rendering/ol/layer/vectorimage.test.js | 289 ++++++++++++++++++ .../ol/renderer/canvas/imagelayer.test.js | 34 +-- .../ol/renderer/canvas/vectorimage.test.js | 21 ++ test/spec/ol/source/raster.test.js | 5 +- 19 files changed, 543 insertions(+), 466 deletions(-) create mode 100644 src/ol/layer/VectorImage.js delete mode 100644 src/ol/layer/VectorRenderType.js create mode 100644 src/ol/renderer/canvas/VectorImageLayer.js create mode 100644 test/rendering/ol/layer/vectorimage.test.js create mode 100644 test/spec/ol/renderer/canvas/vectorimage.test.js diff --git a/examples/drag-and-drop-image-vector.html b/examples/drag-and-drop-image-vector.html index 2635bb9e1a..f6f791278f 100644 --- a/examples/drag-and-drop-image-vector.html +++ b/examples/drag-and-drop-image-vector.html @@ -3,7 +3,7 @@ layout: example.html title: Drag-and-Drop Image Vector shortdesc: Example of using the drag-and-drop interaction with image vector rendering. docs: > - Example of using the drag-and-drop interaction with an `ol/layer/Vector` with `renderMode: 'image'`. Drag and drop GPX, GeoJSON, IGC, KML, or TopoJSON files on to the map. Each file is rendered to an image on the client. + Example of using the drag-and-drop interaction with an `ol/layer/VectorImage` layer. Drag and drop GPX, GeoJSON, IGC, KML, or TopoJSON files on to the map. Each file is rendered to an image on the client. tags: "drag-and-drop-image-vector, gpx, geojson, igc, kml, topojson, vector, image" cloak: - key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5 diff --git a/examples/drag-and-drop-image-vector.js b/examples/drag-and-drop-image-vector.js index bfbe18b276..6b0fb4a3c2 100644 --- a/examples/drag-and-drop-image-vector.js +++ b/examples/drag-and-drop-image-vector.js @@ -2,7 +2,7 @@ import Map from '../src/ol/Map.js'; import View from '../src/ol/View.js'; import {GPX, GeoJSON, IGC, KML, TopoJSON} from '../src/ol/format.js'; import {defaults as defaultInteractions, DragAndDrop} from '../src/ol/interaction.js'; -import {Vector as VectorLayer, Tile as TileLayer} from '../src/ol/layer.js'; +import {VectorImage as VectorImageLayer, Tile as TileLayer} from '../src/ol/layer.js'; import {BingMaps, Vector as VectorSource} from '../src/ol/source.js'; const dragAndDropInteraction = new DragAndDrop({ @@ -36,8 +36,7 @@ dragAndDropInteraction.on('addfeatures', function(event) { const vectorSource = new VectorSource({ features: event.features }); - map.addLayer(new VectorLayer({ - renderMode: 'image', + map.addLayer(new VectorImageLayer({ source: vectorSource })); map.getView().fit(vectorSource.getExtent()); diff --git a/examples/image-vector-layer.html b/examples/image-vector-layer.html index 99613624d3..03350b09c1 100644 --- a/examples/image-vector-layer.html +++ b/examples/image-vector-layer.html @@ -1,9 +1,9 @@ --- layout: example.html -title: Image Vector Layer -shortdesc: Example of an image vector layer. +title: Vector Image Layer +shortdesc: Example of rendering vector data as an image layer. docs: > -

This example uses ol/layer/Vector with `renderMode: 'image'`. This mode results in faster rendering during interaction and animations, at the cost of less accurate rendering.

+

This example uses ol/layer/VectorImage for faster rendering during interaction and animations, at the cost of less accurate rendering.

tags: "vector, image" ---
diff --git a/examples/image-vector-layer.js b/examples/image-vector-layer.js index 768ffa5198..16966e28ba 100644 --- a/examples/image-vector-layer.js +++ b/examples/image-vector-layer.js @@ -1,6 +1,7 @@ import Map from '../src/ol/Map.js'; import View from '../src/ol/View.js'; import GeoJSON from '../src/ol/format/GeoJSON.js'; +import VectorImageLayer from '../src/ol/layer/VectorImage.js'; import VectorLayer from '../src/ol/layer/Vector.js'; import VectorSource from '../src/ol/source/Vector.js'; import {Fill, Stroke, Style, Text} from '../src/ol/style.js'; @@ -19,8 +20,7 @@ const style = new Style({ const map = new Map({ layers: [ - new VectorLayer({ - renderMode: 'image', + new VectorImageLayer({ source: new VectorSource({ url: 'data/geojson/countries.geojson', format: new GeoJSON() diff --git a/src/ol/layer.js b/src/ol/layer.js index c218524084..7da542afe6 100644 --- a/src/ol/layer.js +++ b/src/ol/layer.js @@ -9,4 +9,5 @@ export {default as Image} from './layer/Image.js'; export {default as Layer} from './layer/Layer.js'; export {default as Tile} from './layer/Tile.js'; export {default as Vector} from './layer/Vector.js'; +export {default as VectorImage} from './layer/VectorImage.js'; export {default as VectorTile} from './layer/VectorTile.js'; diff --git a/src/ol/layer/BaseVector.js b/src/ol/layer/BaseVector.js index 98950bd2e9..57dc79eb90 100644 --- a/src/ol/layer/BaseVector.js +++ b/src/ol/layer/BaseVector.js @@ -3,7 +3,6 @@ */ import LayerType from '../LayerType.js'; import Layer from './Layer.js'; -import VectorRenderType from './VectorRenderType.js'; import {assign} from '../obj.js'; import {createDefaultStyle, toFunction as toStyleFunction} from '../style/Style.js'; @@ -28,11 +27,6 @@ import {createDefaultStyle, toFunction as toStyleFunction} from '../style/Style. * @property {number} [renderBuffer=100] The buffer in pixels around the viewport extent used by the * renderer when getting features from the vector source for the rendering or hit-detection. * Recommended value: the size of the largest symbol, line width or label. - * @property {import("./VectorRenderType.js").default|string} [renderMode='vector'] Render mode for vector layers: - * * `'image'`: Vector layers are rendered as images. Great performance, but point symbols and - * texts are always rotated with the view and pixels are scaled during zoom animations. - * * `'vector'`: Vector layers are rendered as vectors. Most accurate rendering even during - * animations, but slower performance. * @property {import("../source/Vector.js").default} [source] Source. * @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage * this layer in its layers collection, and the layer will be rendered on top. This is useful for @@ -43,14 +37,12 @@ import {createDefaultStyle, toFunction as toStyleFunction} from '../style/Style. * means higher priority. * @property {import("../style/Style.js").StyleLike} [style] Layer style. See * {@link module:ol/style} for default style which will be used if this is not defined. - * @property {boolean} [updateWhileAnimating=false] When set to `true` and `renderMode` - * is `vector`, feature batches will be recreated during animations. This means that no - * vectors will be shown clipped, but the setting will have a performance impact for large - * amounts of vector data. When set to `false`, batches will be recreated when no animation - * is active. - * @property {boolean} [updateWhileInteracting=false] When set to `true` and `renderMode` - * is `vector`, feature batches will be recreated during interactions. See also - * `updateWhileAnimating`. + * @property {boolean} [updateWhileAnimating=false] When set to `true`, feature batches will + * be recreated during animations. This means that no vectors will be shown clipped, but the + * setting will have a performance impact for large amounts of vector data. When set to `false`, + * batches will be recreated when no animation is active. + * @property {boolean} [updateWhileInteracting=false] When set to `true`, feature batches will + * be recreated during interactions. See also `updateWhileAnimating`. */ @@ -131,12 +123,6 @@ class BaseVectorLayer extends Layer { this.updateWhileInteracting_ = options.updateWhileInteracting !== undefined ? options.updateWhileInteracting : false; - /** - * @private - * @type {import("./VectorTileRenderType.js").default|string} - */ - this.renderMode_ = options.renderMode || VectorRenderType.VECTOR; - /** * The layer type. * @protected @@ -237,12 +223,6 @@ class BaseVectorLayer extends Layer { this.changed(); } - /** - * @return {import("./VectorRenderType.js").default|string} The render mode. - */ - getRenderMode() { - return this.renderMode_; - } } diff --git a/src/ol/layer/Heatmap.js b/src/ol/layer/Heatmap.js index db062774c7..16f642f9dc 100644 --- a/src/ol/layer/Heatmap.js +++ b/src/ol/layer/Heatmap.js @@ -34,11 +34,6 @@ import Style from '../style/Style.js'; * @property {string|function(import("../Feature.js").default):number} [weight='weight'] The feature * attribute to use for the weight or a function that returns a weight from a feature. Weight values * should range from 0 to 1 (and values outside will be clamped to that range). - * @property {import("./VectorRenderType.js").default|string} [renderMode='vector'] Render mode for vector layers: - * * `'image'`: Vector layers are rendered as images. Great performance, but point symbols and - * texts are always rotated with the view and pixels are scaled during zoom animations. - * * `'vector'`: Vector layers are rendered as vectors. Most accurate rendering even during - * animations, but slower performance. * @property {import("../source/Vector.js").default} [source] Source. */ diff --git a/src/ol/layer/VectorImage.js b/src/ol/layer/VectorImage.js new file mode 100644 index 0000000000..42678c778d --- /dev/null +++ b/src/ol/layer/VectorImage.js @@ -0,0 +1,40 @@ +/** + * @module ol/layer/VectorImage + */ +import BaseVectorLayer from './BaseVector.js'; +import CanvasVectorImageLayerRenderer from '../renderer/canvas/VectorImageLayer.js'; + +/** + * @typedef {import("./BaseVector.js").Options} Options + */ + + +/** + * @classdesc + * Vector data that is rendered client-side. + * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @api + */ +class VectorImageLayer extends BaseVectorLayer { + /** + * @param {Options=} opt_options Options. + */ + constructor(opt_options) { + super(opt_options); + } + + /** + * Create a renderer for this layer. + * @return {import("../renderer/Layer.js").default} A layer renderer. + * @protected + */ + createRenderer() { + return new CanvasVectorImageLayerRenderer(this); + } +} + + +export default VectorImageLayer; diff --git a/src/ol/layer/VectorRenderType.js b/src/ol/layer/VectorRenderType.js deleted file mode 100644 index 03f4c87006..0000000000 --- a/src/ol/layer/VectorRenderType.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @module ol/layer/VectorRenderType - */ - -/** - * @enum {string} - * Render mode for vector layers: - * * `'image'`: Vector layers are rendered as images. Great performance, but - * point symbols and texts are always rotated with the view and pixels are - * scaled during zoom animations. - * * `'vector'`: Vector layers are rendered as vectors. Most accurate rendering - * even during animations, but slower performance. - * @api - */ -export default { - IMAGE: 'image', - VECTOR: 'vector' -}; diff --git a/src/ol/layer/VectorTile.js b/src/ol/layer/VectorTile.js index a40caa1057..7bafcc3943 100644 --- a/src/ol/layer/VectorTile.js +++ b/src/ol/layer/VectorTile.js @@ -86,22 +86,28 @@ class VectorTileLayer extends BaseVectorLayer { constructor(opt_options) { const options = opt_options ? opt_options : {}; + const baseOptions = /** @type {Object} */ (assign({}, options)); + delete baseOptions.preload; + delete baseOptions.useInterimTilesOnError; + + super(/** @type {import("./Vector.js").Options} */ (baseOptions)); + let renderMode = options.renderMode || VectorTileRenderType.HYBRID; assert(renderMode == undefined || renderMode == VectorTileRenderType.IMAGE || renderMode == VectorTileRenderType.HYBRID || renderMode == VectorTileRenderType.VECTOR, 28); // `renderMode` must be `'image'`, `'hybrid'` or `'vector'` + if (options.declutter && renderMode == VectorTileRenderType.IMAGE) { renderMode = VectorTileRenderType.HYBRID; } - options.renderMode = renderMode; - const baseOptions = /** @type {Object} */ (assign({}, options)); - delete baseOptions.preload; - delete baseOptions.useInterimTilesOnError; - - super(/** @type {import("./Vector.js").Options} */ (baseOptions)); + /** + * @private + * @type {VectorTileRenderType} + */ + this.renderMode_ = renderMode; this.setPreload(options.preload ? options.preload : 0); this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? @@ -124,6 +130,13 @@ class VectorTileLayer extends BaseVectorLayer { return new CanvasVectorTileLayerRenderer(this); } + /** + * @return {VectorTileRenderType} The render mode. + */ + getRenderMode() { + return this.renderMode_; + } + /** * Return the level as number to which we will preload tiles up to. * @return {number} The level to preload tiles up to. diff --git a/src/ol/renderer/canvas/ImageLayer.js b/src/ol/renderer/canvas/ImageLayer.js index 9436725087..677e7e48a8 100644 --- a/src/ol/renderer/canvas/ImageLayer.js +++ b/src/ol/renderer/canvas/ImageLayer.js @@ -2,14 +2,9 @@ * @module ol/renderer/canvas/ImageLayer */ import {ENABLE_RASTER_REPROJECTION} from '../../reproj/common.js'; -import ImageCanvas from '../../ImageCanvas.js'; import LayerType from '../../LayerType.js'; import ViewHint from '../../ViewHint.js'; -import {equals} from '../../array.js'; -import {getHeight, getIntersection, getWidth, isEmpty} from '../../extent.js'; -import VectorRenderType from '../../layer/VectorRenderType.js'; -import {assign} from '../../obj.js'; -import {layerRendererConstructors} from './Map.js'; +import {getIntersection, isEmpty} from '../../extent.js'; import IntermediateCanvasRenderer from './IntermediateCanvas.js'; import {create as createTransform, compose as composeTransform} from '../../transform.js'; @@ -21,55 +16,23 @@ import {create as createTransform, compose as composeTransform} from '../../tran class CanvasImageLayerRenderer extends IntermediateCanvasRenderer { /** - * @param {import("../../layer/Image.js").default|import("../../layer/Vector.js").default} imageLayer Image or vector layer. + * @param {import("../../layer/Image.js").default} imageLayer Image layer. */ constructor(imageLayer) { - super(imageLayer); /** - * @private + * @protected * @type {?import("../../ImageBase.js").default} */ this.image_ = null; /** - * @private + * @protected * @type {import("../../transform.js").Transform} */ this.imageTransform_ = createTransform(); - /** - * @type {!Array} - */ - this.skippedFeatures_ = []; - - /** - * @private - * @type {import("./VectorLayer.js").default} - */ - this.vectorRenderer_ = null; - - if (imageLayer.getType() === LayerType.VECTOR) { - for (let i = 0, ii = layerRendererConstructors.length; i < ii; ++i) { - const ctor = layerRendererConstructors[i]; - if (ctor !== CanvasImageLayerRenderer && ctor['handles'](imageLayer)) { - this.vectorRenderer_ = /** @type {import("./VectorLayer.js").default} */ (new ctor(imageLayer)); - break; - } - } - } - - } - - /** - * @inheritDoc - */ - disposeInternal() { - if (this.vectorRenderer_) { - this.vectorRenderer_.dispose(); - } - super.disposeInternal(); } /** @@ -103,9 +66,8 @@ class CanvasImageLayerRenderer extends IntermediateCanvasRenderer { const hints = frameState.viewHints; - const vectorRenderer = this.vectorRenderer_; let renderedExtent = frameState.extent; - if (!vectorRenderer && layerState.extent !== undefined) { + if (layerState.extent !== undefined) { renderedExtent = getIntersection(renderedExtent, layerState.extent); } @@ -118,37 +80,9 @@ class CanvasImageLayerRenderer extends IntermediateCanvasRenderer { projection = sourceProjection; } } - let skippedFeatures = this.skippedFeatures_; - if (vectorRenderer) { - const context = vectorRenderer.context; - const imageFrameState = /** @type {import("../../PluggableMap.js").FrameState} */ (assign({}, frameState, { - size: [ - getWidth(renderedExtent) / viewResolution, - getHeight(renderedExtent) / viewResolution - ], - viewState: /** @type {import("../../View.js").State} */ (assign({}, frameState.viewState, { - rotation: 0 - })) - })); - const newSkippedFeatures = Object.keys(imageFrameState.skippedFeatureUids).sort(); - image = new ImageCanvas(renderedExtent, viewResolution, pixelRatio, context.canvas, function(callback) { - if (vectorRenderer.prepareFrame(imageFrameState, layerState) && - (vectorRenderer.replayGroupChanged || - !equals(skippedFeatures, newSkippedFeatures))) { - context.canvas.width = imageFrameState.size[0] * pixelRatio; - context.canvas.height = imageFrameState.size[1] * pixelRatio; - vectorRenderer.compose(context, imageFrameState, layerState); - skippedFeatures = newSkippedFeatures; - callback(); - } - }); - } else { - image = imageSource.getImage( - renderedExtent, viewResolution, pixelRatio, projection); - } + const image = imageSource.getImage(renderedExtent, viewResolution, pixelRatio, projection); if (image && this.loadImage(image)) { this.image_ = image; - this.skippedFeatures_ = skippedFeatures; } } @@ -177,16 +111,6 @@ class CanvasImageLayerRenderer extends IntermediateCanvasRenderer { return !!this.image_; } - /** - * @inheritDoc - */ - forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback) { - if (this.vectorRenderer_) { - return this.vectorRenderer_.forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback); - } else { - return super.forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback); - } - } } @@ -196,9 +120,7 @@ class CanvasImageLayerRenderer extends IntermediateCanvasRenderer { * @return {boolean} The renderer can render the layer. */ CanvasImageLayerRenderer['handles'] = function(layer) { - return layer.getType() === LayerType.IMAGE || - layer.getType() === LayerType.VECTOR && - /** @type {import("../../layer/Vector.js").default} */ (layer).getRenderMode() === VectorRenderType.IMAGE; + return layer.getType() === LayerType.IMAGE; }; diff --git a/src/ol/renderer/canvas/VectorImageLayer.js b/src/ol/renderer/canvas/VectorImageLayer.js new file mode 100644 index 0000000000..034018f793 --- /dev/null +++ b/src/ol/renderer/canvas/VectorImageLayer.js @@ -0,0 +1,129 @@ +/** + * @module ol/renderer/canvas/ImageLayer + */ +import ImageCanvas from '../../ImageCanvas.js'; +import ViewHint from '../../ViewHint.js'; +import {equals} from '../../array.js'; +import {getHeight, getWidth, isEmpty} from '../../extent.js'; +import {assign} from '../../obj.js'; +import CanvasImageLayerRenderer from './ImageLayer.js'; +import {compose as composeTransform} from '../../transform.js'; +import CanvasVectorLayerRenderer from './VectorLayer.js'; + +/** + * @classdesc + * Canvas renderer for image layers. + * @api + */ +class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer { + + /** + * @param {import("../../layer/VectorImage.js").default} layer Vector image layer. + */ + constructor(layer) { + super(layer); + + /** + * @type {!Array} + */ + this.skippedFeatures_ = []; + + /** + * @private + * @type {import("./VectorLayer.js").default} + */ + this.vectorRenderer_ = new CanvasVectorLayerRenderer(layer); + + } + + /** + * @inheritDoc + */ + disposeInternal() { + this.vectorRenderer_.dispose(); + super.disposeInternal(); + } + + /** + * @inheritDoc + */ + prepareFrame(frameState, layerState) { + const pixelRatio = frameState.pixelRatio; + const size = frameState.size; + const viewState = frameState.viewState; + const viewCenter = viewState.center; + const viewResolution = viewState.resolution; + + const hints = frameState.viewHints; + const vectorRenderer = this.vectorRenderer_; + const renderedExtent = frameState.extent; + + if (!hints[ViewHint.ANIMATING] && !hints[ViewHint.INTERACTING] && !isEmpty(renderedExtent)) { + let skippedFeatures = this.skippedFeatures_; + const context = vectorRenderer.context; + const imageFrameState = /** @type {import("../../PluggableMap.js").FrameState} */ (assign({}, frameState, { + size: [ + getWidth(renderedExtent) / viewResolution, + getHeight(renderedExtent) / viewResolution + ], + viewState: /** @type {import("../../View.js").State} */ (assign({}, frameState.viewState, { + rotation: 0 + })) + })); + const newSkippedFeatures = Object.keys(imageFrameState.skippedFeatureUids).sort(); + const image = new ImageCanvas(renderedExtent, viewResolution, pixelRatio, context.canvas, function(callback) { + if (vectorRenderer.prepareFrame(imageFrameState, layerState) && + (vectorRenderer.replayGroupChanged || + !equals(skippedFeatures, newSkippedFeatures))) { + context.canvas.width = imageFrameState.size[0] * pixelRatio; + context.canvas.height = imageFrameState.size[1] * pixelRatio; + vectorRenderer.compose(context, imageFrameState, layerState); + skippedFeatures = newSkippedFeatures; + callback(); + } + }); + if (this.loadImage(image)) { + this.image_ = image; + this.skippedFeatures_ = skippedFeatures; + } + } + + if (this.image_) { + const image = this.image_; + const imageExtent = image.getExtent(); + const imageResolution = image.getResolution(); + const imagePixelRatio = image.getPixelRatio(); + const scale = pixelRatio * imageResolution / + (viewResolution * imagePixelRatio); + const transform = composeTransform(this.imageTransform_, + pixelRatio * size[0] / 2, pixelRatio * size[1] / 2, + scale, scale, + 0, + imagePixelRatio * (imageExtent[0] - viewCenter[0]) / imageResolution, + imagePixelRatio * (viewCenter[1] - imageExtent[3]) / imageResolution); + composeTransform(this.coordinateToCanvasPixelTransform, + pixelRatio * size[0] / 2 - transform[4], pixelRatio * size[1] / 2 - transform[5], + pixelRatio / viewResolution, -pixelRatio / viewResolution, + 0, + -viewCenter[0], -viewCenter[1]); + + this.renderedResolution = imageResolution * pixelRatio / imagePixelRatio; + } + + return !!this.image_; + } + + /** + * @inheritDoc + */ + forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback) { + if (this.vectorRenderer_) { + return this.vectorRenderer_.forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback); + } else { + return super.forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback); + } + } +} + + +export default CanvasVectorImageLayerRenderer; diff --git a/src/ol/renderer/canvas/VectorTileLayer.js b/src/ol/renderer/canvas/VectorTileLayer.js index 60570990ec..01d2c73f39 100644 --- a/src/ol/renderer/canvas/VectorTileLayer.js +++ b/src/ol/renderer/canvas/VectorTileLayer.js @@ -134,7 +134,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { * @inheritDoc */ prepareFrame(frameState, layerState) { - const layer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer()); + const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer()); const layerRevision = layer.getRevision(); if (this.renderedLayerRevision_ != layerRevision) { this.renderedTiles.length = 0; @@ -328,7 +328,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { * @inheritDoc */ postCompose(context, frameState, layerState) { - const layer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer()); + const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer()); const renderMode = layer.getRenderMode(); if (renderMode != VectorTileRenderType.IMAGE) { const declutterReplays = layer.getDeclutter() ? {} : null; @@ -447,7 +447,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { * @private */ renderTileImage_(tile, pixelRatio, projection) { - const layer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer()); + const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer()); const replayState = tile.getReplayState(layer); const revision = layer.getRevision(); const replays = IMAGE_REPLAYS[layer.getRenderMode()]; diff --git a/src/ol/source/Raster.js b/src/ol/source/Raster.js index 816d01db60..aa952010fb 100644 --- a/src/ol/source/Raster.js +++ b/src/ol/source/Raster.js @@ -10,15 +10,14 @@ import Event from '../events/Event.js'; import EventType from '../events/EventType.js'; import {Processor} from 'pixelworks/lib/index'; import {equals, getCenter, getHeight, getWidth} from '../extent.js'; -import LayerType from '../LayerType.js'; import ImageLayer from '../layer/Image.js'; import TileLayer from '../layer/Tile.js'; import {assign} from '../obj.js'; -import CanvasImageLayerRenderer from '../renderer/canvas/ImageLayer.js'; -import CanvasTileLayerRenderer from '../renderer/canvas/TileLayer.js'; -import ImageSource from './Image.js'; -import SourceState from './State.js'; import {create as createTransform} from '../transform.js'; +import ImageSource from './Image.js'; +import TileSource from './Tile.js'; +import SourceState from './State.js'; +import Source from './Source.js'; /** @@ -113,7 +112,7 @@ class RasterSourceEvent extends Event { /** * @typedef {Object} Options * @property {Array} sources Input - * sources or layers. Vector layers must be configured with `renderMode: 'image'`. + * sources or layers. For vector data, use an VectorImage layer. * @property {Operation} [operation] Raster operation. * The operation will be called with data from input sources * and the output will be assigned to the raster source. @@ -490,42 +489,18 @@ function createRenderers(sources) { * @return {import("../renderer/canvas/Layer.js").default} The renderer. */ function createRenderer(layerOrSource) { - const tileSource = /** @type {import("./Tile.js").default} */ (layerOrSource); - const imageSource = /** @type {import("./Image.js").default} */ (layerOrSource); - const layer = /** @type {import("../layer/Layer.js").default} */ (layerOrSource); - let renderer = null; - if (typeof tileSource.getTile === 'function') { - renderer = createTileRenderer(tileSource); - } else if (typeof imageSource.getImage === 'function') { - renderer = createImageRenderer(imageSource); - } else if (layer.getType() === LayerType.TILE) { - renderer = new CanvasTileLayerRenderer(/** @type {import("../layer/Tile.js").default} */ (layer)); - } else if (layer.getType() == LayerType.IMAGE || layer.getType() == LayerType.VECTOR) { - renderer = new CanvasImageLayerRenderer(/** @type {import("../layer/Image.js").default} */ (layer)); + // @type {import("../layer/Layer.js").default} + let layer; + if (layerOrSource instanceof Source) { + if (layerOrSource instanceof TileSource) { + layer = new TileLayer({source: layerOrSource}); + } else if (layerOrSource instanceof ImageSource) { + layer = new ImageLayer({source: layerOrSource}); + } + } else { + layer = layerOrSource; } - return renderer; -} - - -/** - * Create an image renderer for the provided source. - * @param {import("./Image.js").default} source The source. - * @return {import("../renderer/canvas/Layer.js").default} The renderer. - */ -function createImageRenderer(source) { - const layer = new ImageLayer({source: source}); - return new CanvasImageLayerRenderer(layer); -} - - -/** - * Create a tile renderer for the provided source. - * @param {import("./Tile.js").default} source The source. - * @return {import("../renderer/canvas/Layer.js").default} The renderer. - */ -function createTileRenderer(source) { - const layer = new TileLayer({source: source}); - return new CanvasTileLayerRenderer(layer); + return layer ? /** @type {import("../renderer/canvas/Layer.js").default} */ (layer.createRenderer()) : null; } diff --git a/test/rendering/ol/layer/vector.test.js b/test/rendering/ol/layer/vector.test.js index ce3a1ac883..963e97b09f 100644 --- a/test/rendering/ol/layer/vector.test.js +++ b/test/rendering/ol/layer/vector.test.js @@ -99,33 +99,6 @@ describe('ol.rendering.layer.Vector', function() { }); }); - it('renders opacity correctly with renderMode: \'image\'', function(done) { - createMap('canvas'); - const smallLine = new Feature(new LineString([ - [center[0], center[1] - 1], - [center[0], center[1] + 1] - ])); - smallLine.setStyle(new Style({ - zIndex: -99, - stroke: new Stroke({width: 75, color: 'red'}) - })); - source.addFeature(smallLine); - addPolygon(100); - addCircle(200); - addPolygon(250); - addCircle(500); - addPolygon(600); - addPolygon(720); - map.addLayer(new VectorLayer({ - renerMode: 'image', - source: source - })); - map.once('postrender', function() { - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas.png', - 17, done); - }); - }); - it('renders transparent layers correctly with the canvas renderer', function(done) { createMap('canvas'); const smallLine = new Feature(new LineString([ @@ -165,46 +138,6 @@ describe('ol.rendering.layer.Vector', function() { }); }); - it('renders transparent layers correctly with renderMode: \'image\'', function(done) { - createMap('canvas'); - const smallLine = new Feature(new LineString([ - [center[0], center[1] - 1], - [center[0], center[1] + 1] - ])); - smallLine.setStyle([ - new Style({ - stroke: new Stroke({width: 75, color: 'red'}) - }), - new Style({ - stroke: new Stroke({width: 45, color: 'white'}) - }) - ]); - source.addFeature(smallLine); - const smallLine2 = new Feature(new LineString([ - [center[0], center[1] - 1000], - [center[0], center[1] + 1000] - ])); - smallLine2.setStyle([ - new Style({ - stroke: new Stroke({width: 35, color: 'blue'}) - }), - new Style({ - stroke: new Stroke({width: 15, color: 'green'}) - }) - ]); - source.addFeature(smallLine2); - - map.addLayer(new VectorLayer({ - renderMode: 'image', - source: source, - opacity: 0.5 - })); - map.once('postrender', function() { - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-transparent.png', - 7, done); - }); - }); - it('renders rotation correctly with the canvas renderer', function(done) { createMap('canvas'); map.getView().setRotation(Math.PI + Math.PI / 4); @@ -225,53 +158,6 @@ describe('ol.rendering.layer.Vector', function() { }); }); - it('renders rotation correctly with renderMode: \'image\'', function(done) { - createMap('canvas'); - map.getView().setRotation(Math.PI + Math.PI / 4); - addPolygon(300); - addCircle(500); - map.addLayer(new VectorLayer({ - renderMode: 'image', - source: source, - style: new Style({ - stroke: new Stroke({ - width: 2, - color: 'black' - }) - }) - })); - map.once('postrender', function() { - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-rotated.png', - 2.9, done); - }); - }); - - it('unskips features correctly with renderMode: \'image\'', function(done) { - createMap('canvas'); - addCircle(500); - addPolygon(300); - map.skipFeature(source.getFeatures()[1]); - map.addLayer(new VectorLayer({ - renderMode: 'image', - source: source, - style: new Style({ - fill: new Fill({ - color: 'rgba(255,0,0,0.5)' - }), - stroke: new Stroke({ - width: 2, - color: 'black' - }) - }) - })); - map.renderSync(); - map.unskipFeature(source.getFeatures()[1]); - map.once('postrender', function() { - expectResemble(map, 'rendering/ol/layer/expected/vector.png', - IMAGE_TOLERANCE, done); - }); - }); - it('renders fill/stroke batches correctly with the canvas renderer', function(done) { createMap('canvas'); source = new VectorSource({ @@ -622,47 +508,6 @@ describe('ol.rendering.layer.Vector', function() { }); }); - it('declutters text with renderMode: \'image\'', function(done) { - createMap('canvas'); - const layer = new VectorLayer({ - renderMode: 'image', - source: source - }); - map.addLayer(layer); - - const centerFeature = new Feature({ - geometry: new Point(center), - text: 'center' - }); - source.addFeature(centerFeature); - source.addFeature(new Feature({ - geometry: new Point([center[0] - 540, center[1]]), - text: 'west' - })); - source.addFeature(new Feature({ - geometry: new Point([center[0] + 540, center[1]]), - text: 'east' - })); - - layer.setDeclutter(true); - layer.setStyle(function(feature) { - return new Style({ - text: new Text({ - text: feature.get('text'), - font: '12px sans-serif' - }) - }); - }); - - map.once('postrender', function() { - const hitDetected = map.getFeaturesAtPixel([42, 42]); - expect(hitDetected).to.have.length(1); - expect(hitDetected[0]).to.equal(centerFeature); - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-declutter.png', - 2.2, done); - }); - }); - it('declutters text and respects z-index', function(done) { createMap('canvas'); const layer = new VectorLayer({ @@ -742,46 +587,6 @@ describe('ol.rendering.layer.Vector', function() { }); }); - it('declutters images with renderMode: \'image\'', function(done) { - createMap('canvas'); - const layer = new VectorLayer({ - renderMode: 'image', - source: source - }); - map.addLayer(layer); - - const centerFeature = new Feature({ - geometry: new Point(center) - }); - source.addFeature(centerFeature); - source.addFeature(new Feature({ - geometry: new Point([center[0] - 540, center[1]]) - })); - source.addFeature(new Feature({ - geometry: new Point([center[0] + 540, center[1]]) - })); - - layer.setDeclutter(true); - layer.setStyle(function(feature) { - return new Style({ - image: new CircleStyle({ - radius: 15, - stroke: new Stroke({ - color: 'blue' - }) - }) - }); - }); - - map.once('postrender', function() { - const hitDetected = map.getFeaturesAtPixel([40, 40]); - expect(hitDetected).to.have.length(1); - expect(hitDetected[0]).to.equal(centerFeature); - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-declutter-image.png', - IMAGE_TOLERANCE, done); - }); - }); - it('declutters images and respects z-index', function(done) { createMap('canvas'); const layer = new VectorLayer({ @@ -908,49 +713,6 @@ describe('ol.rendering.layer.Vector', function() { }); }); - it('declutters text along lines and images with renderMode: \'image\'', function(done) { - createMap('canvas'); - const layer = new VectorLayer({ - source: source - }); - map.addLayer(layer); - - const point = new Feature(new Point(center)); - point.setStyle(new Style({ - image: new CircleStyle({ - radius: 8, - stroke: new Stroke({ - color: 'blue' - }) - }) - })); - const line = new Feature(new LineString([ - [center[0] - 650, center[1] - 200], - [center[0] + 650, center[1] - 200] - ])); - line.setStyle(new Style({ - stroke: new Stroke({ - color: '#CCC', - width: 12 - }), - text: new Text({ - placement: 'line', - text: 'east-west', - font: '12px sans-serif' - }) - })); - - source.addFeature(point); - source.addFeature(line); - - layer.setDeclutter(true); - - map.once('postrender', function() { - expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-declutter-line.png', - IMAGE_TOLERANCE, done); - }); - }); - it('declutters text along lines and images with z-index', function(done) { createMap('canvas'); const layer = new VectorLayer({ diff --git a/test/rendering/ol/layer/vectorimage.test.js b/test/rendering/ol/layer/vectorimage.test.js new file mode 100644 index 0000000000..036f0f0654 --- /dev/null +++ b/test/rendering/ol/layer/vectorimage.test.js @@ -0,0 +1,289 @@ +import Circle from '../../../../src/ol/geom/Circle.js'; +import CircleStyle from '../../../../src/ol/style/Circle.js'; +import Feature from '../../../../src/ol/Feature.js'; +import Fill from '../../../../src/ol/style/Fill.js'; +import LineString from '../../../../src/ol/geom/LineString.js'; +import Map from '../../../../src/ol/Map.js'; +import Point from '../../../../src/ol/geom/Point.js'; +import Polygon from '../../../../src/ol/geom/Polygon.js'; +import Stroke from '../../../../src/ol/style/Stroke.js'; +import Style from '../../../../src/ol/style/Style.js'; +import Text from '../../../../src/ol/style/Text.js'; +import VectorImageLayer from '../../../../src/ol/layer/VectorImage.js'; +import VectorSource from '../../../../src/ol/source/Vector.js'; +import View from '../../../../src/ol/View.js'; + +describe('ol.rendering.layer.VectorImage', function() { + + const center = [1825927.7316762917, 6143091.089223046]; + + let map, source; + function createMap(renderer) { + source = new VectorSource(); + map = new Map({ + pixelRatio: 1, + target: createMapDiv(80, 80), + renderer: renderer, + view: new View({ + center: center, + zoom: 13 + }) + }); + } + + afterEach(function() { + if (map) { + disposeMap(map); + } + map = null; + }); + + function addCircle(r) { + source.addFeature(new Feature(new Circle(center, r))); + } + + function addPolygon(r) { + source.addFeature(new Feature(new Polygon([ + [ + [center[0] - r, center[1] - r], + [center[0] + r, center[1] - r], + [center[0] + r, center[1] + r], + [center[0] - r, center[1] + r], + [center[0] - r, center[1] - r] + ] + ]))); + } + + it('renders opacity correctly', function(done) { + createMap('canvas'); + const smallLine = new Feature(new LineString([ + [center[0], center[1] - 1], + [center[0], center[1] + 1] + ])); + smallLine.setStyle(new Style({ + zIndex: -99, + stroke: new Stroke({width: 75, color: 'red'}) + })); + source.addFeature(smallLine); + addPolygon(100); + addCircle(200); + addPolygon(250); + addCircle(500); + addPolygon(600); + addPolygon(720); + map.addLayer(new VectorImageLayer({ + source: source + })); + map.once('postrender', function() { + expectResemble(map, 'rendering/ol/layer/expected/vector-canvas.png', + 17, done); + }); + }); + + it('renders transparent layers correctly', function(done) { + createMap('canvas'); + const smallLine = new Feature(new LineString([ + [center[0], center[1] - 1], + [center[0], center[1] + 1] + ])); + smallLine.setStyle([ + new Style({ + stroke: new Stroke({width: 75, color: 'red'}) + }), + new Style({ + stroke: new Stroke({width: 45, color: 'white'}) + }) + ]); + source.addFeature(smallLine); + const smallLine2 = new Feature(new LineString([ + [center[0], center[1] - 1000], + [center[0], center[1] + 1000] + ])); + smallLine2.setStyle([ + new Style({ + stroke: new Stroke({width: 35, color: 'blue'}) + }), + new Style({ + stroke: new Stroke({width: 15, color: 'green'}) + }) + ]); + source.addFeature(smallLine2); + + map.addLayer(new VectorImageLayer({ + source: source, + opacity: 0.5 + })); + map.once('postrender', function() { + expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-transparent.png', + 7, done); + }); + }); + + it('renders rotation correctly', function(done) { + createMap('canvas'); + map.getView().setRotation(Math.PI + Math.PI / 4); + addPolygon(300); + addCircle(500); + map.addLayer(new VectorImageLayer({ + source: source, + style: new Style({ + stroke: new Stroke({ + width: 2, + color: 'black' + }) + }) + })); + map.once('postrender', function() { + expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-rotated.png', + 2.9, done); + }); + }); + + it('unskips features correctly', function(done) { + createMap('canvas'); + addCircle(500); + addPolygon(300); + map.skipFeature(source.getFeatures()[1]); + map.addLayer(new VectorImageLayer({ + source: source, + style: new Style({ + fill: new Fill({ + color: 'rgba(255,0,0,0.5)' + }), + stroke: new Stroke({ + width: 2, + color: 'black' + }) + }) + })); + map.renderSync(); + map.unskipFeature(source.getFeatures()[1]); + map.once('postrender', function() { + expectResemble(map, 'rendering/ol/layer/expected/vector.png', + IMAGE_TOLERANCE, done); + }); + }); + + it('declutters text', function(done) { + createMap('canvas'); + const layer = new VectorImageLayer({ + source: source + }); + map.addLayer(layer); + + const centerFeature = new Feature({ + geometry: new Point(center), + text: 'center' + }); + source.addFeature(centerFeature); + source.addFeature(new Feature({ + geometry: new Point([center[0] - 540, center[1]]), + text: 'west' + })); + source.addFeature(new Feature({ + geometry: new Point([center[0] + 540, center[1]]), + text: 'east' + })); + + layer.setDeclutter(true); + layer.setStyle(function(feature) { + return new Style({ + text: new Text({ + text: feature.get('text'), + font: '12px sans-serif' + }) + }); + }); + + map.once('postrender', function() { + const hitDetected = map.getFeaturesAtPixel([42, 42]); + expect(hitDetected).to.have.length(1); + expect(hitDetected[0]).to.equal(centerFeature); + expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-declutter.png', + 2.2, done); + }); + }); + + it('declutters images', function(done) { + createMap('canvas'); + const layer = new VectorImageLayer({ + source: source + }); + map.addLayer(layer); + + const centerFeature = new Feature({ + geometry: new Point(center) + }); + source.addFeature(centerFeature); + source.addFeature(new Feature({ + geometry: new Point([center[0] - 540, center[1]]) + })); + source.addFeature(new Feature({ + geometry: new Point([center[0] + 540, center[1]]) + })); + + layer.setDeclutter(true); + layer.setStyle(function(feature) { + return new Style({ + image: new CircleStyle({ + radius: 15, + stroke: new Stroke({ + color: 'blue' + }) + }) + }); + }); + + map.once('postrender', function() { + const hitDetected = map.getFeaturesAtPixel([40, 40]); + expect(hitDetected).to.have.length(1); + expect(hitDetected[0]).to.equal(centerFeature); + expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-declutter-image.png', + IMAGE_TOLERANCE, done); + }); + }); + + it('declutters text along lines and images', function(done) { + createMap('canvas'); + const layer = new VectorImageLayer({ + source: source + }); + map.addLayer(layer); + + const point = new Feature(new Point(center)); + point.setStyle(new Style({ + image: new CircleStyle({ + radius: 8, + stroke: new Stroke({ + color: 'blue' + }) + }) + })); + const line = new Feature(new LineString([ + [center[0] - 650, center[1] - 200], + [center[0] + 650, center[1] - 200] + ])); + line.setStyle(new Style({ + stroke: new Stroke({ + color: '#CCC', + width: 12 + }), + text: new Text({ + placement: 'line', + text: 'east-west', + font: '12px sans-serif' + }) + })); + + source.addFeature(point); + source.addFeature(line); + + layer.setDeclutter(true); + + map.once('postrender', function() { + expectResemble(map, 'rendering/ol/layer/expected/vector-canvas-declutter-line.png', + IMAGE_TOLERANCE, done); + }); + }); + +}); diff --git a/test/spec/ol/renderer/canvas/imagelayer.test.js b/test/spec/ol/renderer/canvas/imagelayer.test.js index 3068bec1f2..3e7ca3a1c8 100644 --- a/test/spec/ol/renderer/canvas/imagelayer.test.js +++ b/test/spec/ol/renderer/canvas/imagelayer.test.js @@ -1,45 +1,16 @@ import Map from '../../../../../src/ol/Map.js'; import View from '../../../../../src/ol/View.js'; import ImageLayer from '../../../../../src/ol/layer/Image.js'; -import VectorLayer from '../../../../../src/ol/layer/Vector.js'; +import VectorImageLayer from '../../../../../src/ol/layer/VectorImage.js'; import Feature from '../../../../../src/ol/Feature.js'; import Point from '../../../../../src/ol/geom/Point.js'; import Projection from '../../../../../src/ol/proj/Projection.js'; import Static from '../../../../../src/ol/source/ImageStatic.js'; import VectorSource from '../../../../../src/ol/source/Vector.js'; -import CanvasImageLayerRenderer from '../../../../../src/ol/renderer/canvas/ImageLayer.js'; -import CanvasVectorLayerRenderer from '../../../../../src/ol/renderer/canvas/VectorLayer.js'; describe('ol.renderer.canvas.ImageLayer', function() { - describe('#dispose()', function() { - let layer, imageRenderer, vectorRenderer; - - beforeEach(function() { - layer = new VectorLayer({ - renderMode: 'image', - source: new VectorSource() - }); - imageRenderer = new CanvasImageLayerRenderer(layer); - vectorRenderer = new CanvasVectorLayerRenderer(layer); - imageRenderer.vectorRenderer_ = vectorRenderer; - }); - - afterEach(function() { - imageRenderer.dispose(); - vectorRenderer.dispose(); - layer.dispose(); - }); - - it('cleans up CanvasVectorRenderer', function() { - const vectorRenderer = imageRenderer.vectorRenderer_; - const spy = sinon.spy(vectorRenderer, 'dispose'); - imageRenderer.dispose(); - expect(spy.called).to.be(true); - }); - }); - describe('#forEachLayerAtCoordinate', function() { let map, target, source; @@ -99,8 +70,7 @@ describe('ol.renderer.canvas.ImageLayer', function() { let map, div, layer; beforeEach(function() { - layer = new VectorLayer({ - renderMode: 'image', + layer = new VectorImageLayer({ source: new VectorSource({ features: [new Feature(new Point([0, 0]))] }) diff --git a/test/spec/ol/renderer/canvas/vectorimage.test.js b/test/spec/ol/renderer/canvas/vectorimage.test.js new file mode 100644 index 0000000000..5948f40a44 --- /dev/null +++ b/test/spec/ol/renderer/canvas/vectorimage.test.js @@ -0,0 +1,21 @@ +import VectorImageLayer from '../../../../../src/ol/layer/VectorImage.js'; +import VectorSource from '../../../../../src/ol/source/Vector.js'; +import CanvasVectorImageLayerRenderer from '../../../../../src/ol/renderer/canvas/VectorImageLayer.js'; + +describe('ol/renderer/canvas/VectorImageLayer', function() { + + describe('#dispose()', function() { + + it('cleans up CanvasVectorRenderer', function() { + const layer = new VectorImageLayer({ + source: new VectorSource() + }); + const renderer = new CanvasVectorImageLayerRenderer(layer); + const spy = sinon.spy(renderer.vectorRenderer_, 'dispose'); + renderer.dispose(); + expect(spy.called).to.be(true); + }); + + }); + +}); diff --git a/test/spec/ol/source/raster.test.js b/test/spec/ol/source/raster.test.js index 5f4d3928ad..510b357750 100644 --- a/test/spec/ol/source/raster.test.js +++ b/test/spec/ol/source/raster.test.js @@ -2,7 +2,7 @@ import Map from '../../../../src/ol/Map.js'; import TileState from '../../../../src/ol/TileState.js'; import View from '../../../../src/ol/View.js'; import ImageLayer from '../../../../src/ol/layer/Image.js'; -import VectorLayer from '../../../../src/ol/layer/Vector.js'; +import VectorImageLayer from '../../../../src/ol/layer/VectorImage.js'; import Projection from '../../../../src/ol/proj/Projection.js'; import Static from '../../../../src/ol/source/ImageStatic.js'; import RasterSource from '../../../../src/ol/source/Raster.js'; @@ -47,8 +47,7 @@ where('Uint8ClampedArray').describe('ol.source.Raster', function() { imageExtent: extent }); - blueSource = new VectorLayer({ - renderMode: 'image', + blueSource = new VectorImageLayer({ source: new VectorSource({ features: [new Feature(new Point([0, 0]))] }), From 7374e32007ee728fc29bd0d2bd2783ee882a629c Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 4 Nov 2018 18:55:04 -0700 Subject: [PATCH 07/15] Canvas layer renderers are now dependencies of layers --- src/ol/Map.js | 13 +------------ src/ol/renderer/canvas/ImageLayer.js | 22 ---------------------- src/ol/renderer/canvas/Map.js | 19 +------------------ src/ol/renderer/canvas/TileLayer.js | 22 ---------------------- src/ol/renderer/canvas/VectorLayer.js | 22 ---------------------- src/ol/renderer/canvas/VectorTileLayer.js | 22 ---------------------- 6 files changed, 2 insertions(+), 118 deletions(-) diff --git a/src/ol/Map.js b/src/ol/Map.js index 8ef2a8111c..56a42f4a03 100644 --- a/src/ol/Map.js +++ b/src/ol/Map.js @@ -5,11 +5,7 @@ import PluggableMap from './PluggableMap.js'; import {defaults as defaultControls} from './control/util.js'; import {defaults as defaultInteractions} from './interaction.js'; import {assign} from './obj.js'; -import CanvasImageLayerRenderer from './renderer/canvas/ImageLayer.js'; import CanvasMapRenderer from './renderer/canvas/Map.js'; -import CanvasTileLayerRenderer from './renderer/canvas/TileLayer.js'; -import CanvasVectorLayerRenderer from './renderer/canvas/VectorLayer.js'; -import CanvasVectorTileLayerRenderer from './renderer/canvas/VectorTileLayer.js'; /** * @classdesc @@ -80,14 +76,7 @@ class Map extends PluggableMap { } createRenderer() { - const renderer = new CanvasMapRenderer(this); - renderer.registerLayerRenderers([ - CanvasImageLayerRenderer, - CanvasTileLayerRenderer, - CanvasVectorLayerRenderer, - CanvasVectorTileLayerRenderer - ]); - return renderer; + return new CanvasMapRenderer(this); } } diff --git a/src/ol/renderer/canvas/ImageLayer.js b/src/ol/renderer/canvas/ImageLayer.js index 677e7e48a8..1b0c79cb35 100644 --- a/src/ol/renderer/canvas/ImageLayer.js +++ b/src/ol/renderer/canvas/ImageLayer.js @@ -2,7 +2,6 @@ * @module ol/renderer/canvas/ImageLayer */ import {ENABLE_RASTER_REPROJECTION} from '../../reproj/common.js'; -import LayerType from '../../LayerType.js'; import ViewHint from '../../ViewHint.js'; import {getIntersection, isEmpty} from '../../extent.js'; import IntermediateCanvasRenderer from './IntermediateCanvas.js'; @@ -114,25 +113,4 @@ class CanvasImageLayerRenderer extends IntermediateCanvasRenderer { } -/** - * Determine if this renderer handles the provided layer. - * @param {import("../../layer/Layer.js").default} layer The candidate layer. - * @return {boolean} The renderer can render the layer. - */ -CanvasImageLayerRenderer['handles'] = function(layer) { - return layer.getType() === LayerType.IMAGE; -}; - - -/** - * Create a layer renderer. - * @param {import("../Map.js").default} mapRenderer The map renderer. - * @param {import("../../layer/Layer.js").default} layer The layer to be rendererd. - * @return {CanvasImageLayerRenderer} The layer renderer. - */ -CanvasImageLayerRenderer['create'] = function(mapRenderer, layer) { - return new CanvasImageLayerRenderer(/** @type {import("../../layer/Image.js").default} */ (layer)); -}; - - export default CanvasImageLayerRenderer; diff --git a/src/ol/renderer/canvas/Map.js b/src/ol/renderer/canvas/Map.js index 9d15a8a71d..d8aa1e3b7e 100644 --- a/src/ol/renderer/canvas/Map.js +++ b/src/ol/renderer/canvas/Map.js @@ -2,7 +2,7 @@ * @module ol/renderer/canvas/Map */ import {create as createTransform, apply as applyTransform, compose as composeTransform} from '../../transform.js'; -import {includes, stableSort} from '../../array.js'; +import {stableSort} from '../../array.js'; import {CLASS_UNSELECTABLE} from '../../css.js'; import {createCanvasContext2D} from '../../dom.js'; import {visibleAtResolution} from '../../layer/Layer.js'; @@ -14,11 +14,6 @@ import MapRenderer, {sortByZIndex} from '../Map.js'; import SourceState from '../../source/State.js'; -/** - * @type {Array} - */ -export const layerRendererConstructors = []; - /** * @classdesc * Canvas map renderer. @@ -204,18 +199,6 @@ class CanvasMapRenderer extends MapRenderer { return undefined; } - /** - * @inheritDoc - */ - registerLayerRenderers(constructors) { - super.registerLayerRenderers(constructors); - for (let i = 0, ii = constructors.length; i < ii; ++i) { - const ctor = constructors[i]; - if (!includes(layerRendererConstructors, ctor)) { - layerRendererConstructors.push(ctor); - } - } - } } diff --git a/src/ol/renderer/canvas/TileLayer.js b/src/ol/renderer/canvas/TileLayer.js index 4299e4d39f..70ff809387 100644 --- a/src/ol/renderer/canvas/TileLayer.js +++ b/src/ol/renderer/canvas/TileLayer.js @@ -2,7 +2,6 @@ * @module ol/renderer/canvas/TileLayer */ import {getUid} from '../../util.js'; -import LayerType from '../../LayerType.js'; import TileRange from '../../TileRange.js'; import TileState from '../../TileState.js'; import ViewHint from '../../ViewHint.js'; @@ -369,27 +368,6 @@ class CanvasTileLayerRenderer extends IntermediateCanvasRenderer { } -/** - * Determine if this renderer handles the provided layer. - * @param {import("../../layer/Layer.js").default} layer The candidate layer. - * @return {boolean} The renderer can render the layer. - */ -CanvasTileLayerRenderer['handles'] = function(layer) { - return layer.getType() === LayerType.TILE; -}; - - -/** - * Create a layer renderer. - * @param {import("../Map.js").default} mapRenderer The map renderer. - * @param {import("../../layer/Layer.js").default} layer The layer to be rendererd. - * @return {CanvasTileLayerRenderer} The layer renderer. - */ -CanvasTileLayerRenderer['create'] = function(mapRenderer, layer) { - return new CanvasTileLayerRenderer(/** @type {import("../../layer/Tile.js").default} */ (layer)); -}; - - /** * @function * @return {import("../../layer/Tile.js").default|import("../../layer/VectorTile.js").default} diff --git a/src/ol/renderer/canvas/VectorLayer.js b/src/ol/renderer/canvas/VectorLayer.js index 5ecc53a56d..ea30d981e2 100644 --- a/src/ol/renderer/canvas/VectorLayer.js +++ b/src/ol/renderer/canvas/VectorLayer.js @@ -2,7 +2,6 @@ * @module ol/renderer/canvas/VectorLayer */ import {getUid} from '../../util.js'; -import LayerType from '../../LayerType.js'; import ViewHint from '../../ViewHint.js'; import {createCanvasContext2D} from '../../dom.js'; import {listen, unlisten} from '../../events.js'; @@ -413,25 +412,4 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { } -/** - * Determine if this renderer handles the provided layer. - * @param {import("../../layer/Layer.js").default} layer The candidate layer. - * @return {boolean} The renderer can render the layer. - */ -CanvasVectorLayerRenderer['handles'] = function(layer) { - return layer.getType() === LayerType.VECTOR; -}; - - -/** - * Create a layer renderer. - * @param {import("../Map.js").default} mapRenderer The map renderer. - * @param {import("../../layer/Layer.js").default} layer The layer to be rendererd. - * @return {CanvasVectorLayerRenderer} The layer renderer. - */ -CanvasVectorLayerRenderer['create'] = function(mapRenderer, layer) { - return new CanvasVectorLayerRenderer(/** @type {import("../../layer/Vector.js").default} */ (layer)); -}; - - export default CanvasVectorLayerRenderer; diff --git a/src/ol/renderer/canvas/VectorTileLayer.js b/src/ol/renderer/canvas/VectorTileLayer.js index 01d2c73f39..f4126a8518 100644 --- a/src/ol/renderer/canvas/VectorTileLayer.js +++ b/src/ol/renderer/canvas/VectorTileLayer.js @@ -2,7 +2,6 @@ * @module ol/renderer/canvas/VectorTileLayer */ import {getUid} from '../../util.js'; -import LayerType from '../../LayerType.js'; import TileState from '../../TileState.js'; import ViewHint from '../../ViewHint.js'; import {createCanvasContext2D} from '../../dom.js'; @@ -481,25 +480,4 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { } -/** - * Determine if this renderer handles the provided layer. - * @param {import("../../layer/Layer.js").default} layer The candidate layer. - * @return {boolean} The renderer can render the layer. - */ -CanvasVectorTileLayerRenderer['handles'] = function(layer) { - return layer.getType() === LayerType.VECTOR_TILE; -}; - - -/** - * Create a layer renderer. - * @param {import("../Map.js").default} mapRenderer The map renderer. - * @param {import("../../layer/Layer.js").default} layer The layer to be rendererd. - * @return {CanvasVectorTileLayerRenderer} The layer renderer. - */ -CanvasVectorTileLayerRenderer['create'] = function(mapRenderer, layer) { - return new CanvasVectorTileLayerRenderer(/** @type {import("../../layer/VectorTile.js").default} */ (layer)); -}; - - export default CanvasVectorTileLayerRenderer; From f6b838c6358af3166be89011f54815407c1ae226 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 4 Nov 2018 19:42:06 -0700 Subject: [PATCH 08/15] Make webgl layer renderers dependencies of webgl layers --- src/ol/WebGLMap.js | 11 +------ src/ol/layer/Image.js | 3 +- src/ol/layer/Layer.js | 8 +++-- src/ol/layer/Tile.js | 3 +- src/ol/layer/Vector.js | 3 +- src/ol/layer/VectorImage.js | 3 +- src/ol/layer/VectorTile.js | 3 +- src/ol/layer/WebGLImage.js | 7 ++-- src/ol/layer/WebGLTile.js | 8 ++--- src/ol/layer/WebGLVector.js | 7 ++-- src/ol/renderer/Map.js | 48 +++++++--------------------- src/ol/renderer/webgl/ImageLayer.js | 25 --------------- src/ol/renderer/webgl/TileLayer.js | 25 --------------- src/ol/renderer/webgl/VectorLayer.js | 25 --------------- 14 files changed, 40 insertions(+), 139 deletions(-) diff --git a/src/ol/WebGLMap.js b/src/ol/WebGLMap.js index 5db5371194..8d7005125e 100644 --- a/src/ol/WebGLMap.js +++ b/src/ol/WebGLMap.js @@ -5,10 +5,7 @@ import PluggableMap from './PluggableMap.js'; import {defaults as defaultControls} from './control.js'; import {defaults as defaultInteractions} from './interaction.js'; import {assign} from './obj.js'; -import WebGLImageLayerRenderer from './renderer/webgl/ImageLayer.js'; import WebGLMapRenderer from './renderer/webgl/Map.js'; -import WebGLTileLayerRenderer from './renderer/webgl/TileLayer.js'; -import WebGLVectorLayerRenderer from './renderer/webgl/VectorLayer.js'; /** @@ -80,13 +77,7 @@ class WebGLMap extends PluggableMap { } createRenderer() { - const renderer = new WebGLMapRenderer(this); - renderer.registerLayerRenderers([ - WebGLImageLayerRenderer, - WebGLTileLayerRenderer, - WebGLVectorLayerRenderer - ]); - return renderer; + return new WebGLMapRenderer(this); } } diff --git a/src/ol/layer/Image.js b/src/ol/layer/Image.js index 2a83a18cc9..ad6b8025c8 100644 --- a/src/ol/layer/Image.js +++ b/src/ol/layer/Image.js @@ -32,10 +32,11 @@ class ImageLayer extends BaseImageLayer { /** * Create a renderer for this layer. + * @param {import("../renderer/Map.js").default} mapRenderer The map renderer. * @return {import("../renderer/Layer.js").default} A layer renderer. * @protected */ - createRenderer() { + createRenderer(mapRenderer) { return new CanvasImageLayerRenderer(this); } diff --git a/src/ol/layer/Layer.js b/src/ol/layer/Layer.js index 9a0d413b01..678860e0d8 100644 --- a/src/ol/layer/Layer.js +++ b/src/ol/layer/Layer.js @@ -224,21 +224,23 @@ class Layer extends BaseLayer { /** * Get the renderer for this layer. + * @param {import("../renderer/Map.js").default} mapRenderer The map renderer. * @return {import("../renderer/Layer.js").default} The layer renderer. */ - getRenderer() { + getRenderer(mapRenderer) { if (!this.renderer_) { - this.renderer_ = this.createRenderer(); + this.renderer_ = this.createRenderer(mapRenderer); } return this.renderer_; } /** * Create a renderer for this layer. + * @param {import("../renderer/Map.js").default} mapRenderer The map renderer. * @return {import("../renderer/Layer.js").default} A layer renderer. * @protected */ - createRenderer() { + createRenderer(mapRenderer) { return null; } diff --git a/src/ol/layer/Tile.js b/src/ol/layer/Tile.js index 6e25f2af6b..fbd6606a15 100644 --- a/src/ol/layer/Tile.js +++ b/src/ol/layer/Tile.js @@ -31,10 +31,11 @@ class TileLayer extends BaseTileLayer { /** * Create a renderer for this layer. + * @param {import("../renderer/Map.js").default} mapRenderer The map renderer. * @return {import("../renderer/Layer.js").default} A layer renderer. * @protected */ - createRenderer() { + createRenderer(mapRenderer) { return new CanvasTileLayerRenderer(this); } diff --git a/src/ol/layer/Vector.js b/src/ol/layer/Vector.js index 0456b04ea0..1458890db2 100644 --- a/src/ol/layer/Vector.js +++ b/src/ol/layer/Vector.js @@ -29,10 +29,11 @@ class VectorLayer extends BaseVectorLayer { /** * Create a renderer for this layer. + * @param {import("../renderer/Map.js").default} mapRenderer The map renderer. * @return {import("../renderer/Layer.js").default} A layer renderer. * @protected */ - createRenderer() { + createRenderer(mapRenderer) { return new CanvasVectorLayerRenderer(this); } } diff --git a/src/ol/layer/VectorImage.js b/src/ol/layer/VectorImage.js index 42678c778d..3c92e77398 100644 --- a/src/ol/layer/VectorImage.js +++ b/src/ol/layer/VectorImage.js @@ -28,10 +28,11 @@ class VectorImageLayer extends BaseVectorLayer { /** * Create a renderer for this layer. + * @param {import("../renderer/Map.js").default} mapRenderer The map renderer. * @return {import("../renderer/Layer.js").default} A layer renderer. * @protected */ - createRenderer() { + createRenderer(mapRenderer) { return new CanvasVectorImageLayerRenderer(this); } } diff --git a/src/ol/layer/VectorTile.js b/src/ol/layer/VectorTile.js index 7bafcc3943..5938b2098e 100644 --- a/src/ol/layer/VectorTile.js +++ b/src/ol/layer/VectorTile.js @@ -123,10 +123,11 @@ class VectorTileLayer extends BaseVectorLayer { /** * Create a renderer for this layer. + * @param {import("../renderer/Map.js").default} mapRenderer The map renderer. * @return {import("../renderer/Layer.js").default} A layer renderer. * @protected */ - createRenderer() { + createRenderer(mapRenderer) { return new CanvasVectorTileLayerRenderer(this); } diff --git a/src/ol/layer/WebGLImage.js b/src/ol/layer/WebGLImage.js index cb8fa40ecb..45e7fe4d0b 100644 --- a/src/ol/layer/WebGLImage.js +++ b/src/ol/layer/WebGLImage.js @@ -2,6 +2,7 @@ * @module ol/layer/WebGLImage */ import BaseImageLayer from './BaseImage.js'; +import WebGLImageLayerRenderer from '../renderer/webgl/ImageLayer.js'; /** @@ -31,12 +32,12 @@ class WebGLImageLayer extends BaseImageLayer { /** * Create a renderer for this layer. + * @param {import("../renderer/webgl/Map.js").default} mapRenderer The map renderer. * @return {import("../renderer/Layer.js").default} A layer renderer. * @protected */ - createRenderer() { - // TODO: rework WebGL renderers to share context - return null; + createRenderer(mapRenderer) { + return new WebGLImageLayerRenderer(mapRenderer, this); } } diff --git a/src/ol/layer/WebGLTile.js b/src/ol/layer/WebGLTile.js index a40095d952..1b034b1857 100644 --- a/src/ol/layer/WebGLTile.js +++ b/src/ol/layer/WebGLTile.js @@ -2,7 +2,7 @@ * @module ol/layer/WebGLTile */ import BaseTileLayer from './BaseTile.js'; - +import WebGLTileLayerRenderer from '../renderer/webgl/TileLayer.js'; /** * @typedef {import("./BaseTile.js").Options} Options @@ -30,12 +30,12 @@ class WebGLTileLayer extends BaseTileLayer { /** * Create a renderer for this layer. + * @param {import("../renderer/webgl/Map.js").default} mapRenderer The map renderer. * @return {import("../renderer/Layer.js").default} A layer renderer. * @protected */ - createRenderer() { - // TODO: rework WebGL renderers to share context - return null; + createRenderer(mapRenderer) { + return new WebGLTileLayerRenderer(mapRenderer, this); } } diff --git a/src/ol/layer/WebGLVector.js b/src/ol/layer/WebGLVector.js index 03a2c6d109..3bf62b37f5 100644 --- a/src/ol/layer/WebGLVector.js +++ b/src/ol/layer/WebGLVector.js @@ -2,6 +2,7 @@ * @module ol/layer/WebGLVector */ import BaseVectorLayer from './BaseVector.js'; +import WebGLVectorLayerRenderer from '../renderer/webgl/VectorLayer.js'; /** @@ -28,12 +29,12 @@ class WebGLVectorLayer extends BaseVectorLayer { /** * Create a renderer for this layer. + * @param {import("../renderer/webgl/Map.js").default} mapRenderer The map renderer. * @return {import("../renderer/Layer.js").default} A layer renderer. * @protected */ - createRenderer() { - // TODO: rework WebGL renderers to share context - return null; + createRenderer(mapRenderer) { + return new WebGLVectorLayerRenderer(mapRenderer, this); } } diff --git a/src/ol/renderer/Map.js b/src/ol/renderer/Map.js index 157dce98d5..9e3679f7c6 100644 --- a/src/ol/renderer/Map.js +++ b/src/ol/renderer/Map.js @@ -40,12 +40,6 @@ class MapRenderer extends Disposable { */ this.layerRendererListeners_ = {}; - /** - * @private - * @type {Array} - */ - this.layerRendererConstructors_ = []; - } /** @@ -57,14 +51,6 @@ class MapRenderer extends Disposable { abstract(); } - /** - * Register layer renderer constructors. - * @param {Array} constructors Layer renderers. - */ - registerLayerRenderers(constructors) { - this.layerRendererConstructors_.push.apply(this.layerRendererConstructors_, constructors); - } - /** * @param {import("../PluggableMap.js").FrameState} frameState FrameState. * @protected @@ -151,10 +137,10 @@ class MapRenderer extends Disposable { let i; for (i = numLayers - 1; i >= 0; --i) { const layerState = layerStates[i]; - const layer = layerState.layer; + const layer = /** @type {import("../layer/Layer.js").default} */ (layerState.layer); if (visibleAtResolution(layerState, viewResolution) && layerFilter.call(thisArg2, layer)) { const layerRenderer = this.getLayerRenderer(layer); - const source = /** @type {import("../layer/Layer.js").default} */ (layer).getSource(); + const source = layer.getSource(); if (source) { result = layerRenderer.forEachFeatureAtCoordinate( source.getWrapX() ? translatedCoordinate : coordinate, @@ -208,7 +194,7 @@ class MapRenderer extends Disposable { } /** - * @param {import("../layer/Base.js").default} layer Layer. + * @param {import("../layer/Layer.js").default} layer Layer. * @protected * @return {import("./Layer.js").default} Layer renderer. */ @@ -216,26 +202,16 @@ class MapRenderer extends Disposable { const layerKey = getUid(layer); if (layerKey in this.layerRenderers_) { return this.layerRenderers_[layerKey]; - } else { - let renderer = layer.getRenderer(); - if (!renderer) { - for (let i = 0, ii = this.layerRendererConstructors_.length; i < ii; ++i) { - const candidate = this.layerRendererConstructors_[i]; - if (candidate['handles'](layer)) { - renderer = candidate['create'](this, layer); - break; - } - } - } - if (renderer) { - this.layerRenderers_[layerKey] = renderer; - this.layerRendererListeners_[layerKey] = listen(renderer, - EventType.CHANGE, this.handleLayerRendererChange_, this); - } else { - throw new Error('Unable to create renderer for layer: ' + layer.getType()); - } - return renderer; } + + const renderer = layer.getRenderer(this); + if (!renderer) { + throw new Error('Unable to create renderer for layer: ' + layer.getType()); + } + + this.layerRenderers_[layerKey] = renderer; + this.layerRendererListeners_[layerKey] = listen(renderer, EventType.CHANGE, this.handleLayerRendererChange_, this); + return renderer; } /** diff --git a/src/ol/renderer/webgl/ImageLayer.js b/src/ol/renderer/webgl/ImageLayer.js index cbd4378773..1ec1defa10 100644 --- a/src/ol/renderer/webgl/ImageLayer.js +++ b/src/ol/renderer/webgl/ImageLayer.js @@ -2,7 +2,6 @@ * @module ol/renderer/webgl/ImageLayer */ import {ENABLE_RASTER_REPROJECTION} from '../../reproj/common.js'; -import LayerType from '../../LayerType.js'; import ViewHint from '../../ViewHint.js'; import {createCanvasContext2D} from '../../dom.js'; import {getIntersection, isEmpty} from '../../extent.js'; @@ -269,28 +268,4 @@ class WebGLImageLayerRenderer extends WebGLLayerRenderer { } -/** - * Determine if this renderer handles the provided layer. - * @param {import("../../layer/Layer.js").default} layer The candidate layer. - * @return {boolean} The renderer can render the layer. - */ -WebGLImageLayerRenderer['handles'] = function(layer) { - return layer.getType() === LayerType.IMAGE; -}; - - -/** - * Create a layer renderer. - * @param {import("../Map.js").default} mapRenderer The map renderer. - * @param {import("../../layer/Layer.js").default} layer The layer to be rendererd. - * @return {WebGLImageLayerRenderer} The layer renderer. - */ -WebGLImageLayerRenderer['create'] = function(mapRenderer, layer) { - return new WebGLImageLayerRenderer( - /** @type {import("./Map.js").default} */ (mapRenderer), - /** @type {import("../../layer/Image.js").default} */ (layer) - ); -}; - - export default WebGLImageLayerRenderer; diff --git a/src/ol/renderer/webgl/TileLayer.js b/src/ol/renderer/webgl/TileLayer.js index d59e263a99..dc76346e3d 100644 --- a/src/ol/renderer/webgl/TileLayer.js +++ b/src/ol/renderer/webgl/TileLayer.js @@ -4,7 +4,6 @@ // FIXME large resolutions lead to too large framebuffers :-( // FIXME animated shaders! check in redraw -import LayerType from '../../LayerType.js'; import ImageTile from '../../ImageTile.js'; import TileRange from '../../TileRange.js'; import TileState from '../../TileState.js'; @@ -396,28 +395,4 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { } -/** - * Determine if this renderer handles the provided layer. - * @param {import("../../layer/Layer.js").default} layer The candidate layer. - * @return {boolean} The renderer can render the layer. - */ -WebGLTileLayerRenderer['handles'] = function(layer) { - return layer.getType() === LayerType.TILE; -}; - - -/** - * Create a layer renderer. - * @param {import("../Map.js").default} mapRenderer The map renderer. - * @param {import("../../layer/Layer.js").default} layer The layer to be rendererd. - * @return {WebGLTileLayerRenderer} The layer renderer. - */ -WebGLTileLayerRenderer['create'] = function(mapRenderer, layer) { - return new WebGLTileLayerRenderer( - /** @type {import("./Map.js").default} */ (mapRenderer), - /** @type {import("../../layer/Tile.js").default} */ (layer) - ); -}; - - export default WebGLTileLayerRenderer; diff --git a/src/ol/renderer/webgl/VectorLayer.js b/src/ol/renderer/webgl/VectorLayer.js index 1edba3559f..c609479e45 100644 --- a/src/ol/renderer/webgl/VectorLayer.js +++ b/src/ol/renderer/webgl/VectorLayer.js @@ -2,7 +2,6 @@ * @module ol/renderer/webgl/VectorLayer */ import {getUid} from '../../util.js'; -import LayerType from '../../LayerType.js'; import ViewHint from '../../ViewHint.js'; import {buffer, containsExtent, createEmpty} from '../../extent.js'; import WebGLReplayGroup from '../../render/webgl/ReplayGroup.js'; @@ -304,28 +303,4 @@ class WebGLVectorLayerRenderer extends WebGLLayerRenderer { } -/** - * Determine if this renderer handles the provided layer. - * @param {import("../../layer/Layer.js").default} layer The candidate layer. - * @return {boolean} The renderer can render the layer. - */ -WebGLVectorLayerRenderer['handles'] = function(layer) { - return layer.getType() === LayerType.VECTOR; -}; - - -/** - * Create a layer renderer. - * @param {import("../Map.js").default} mapRenderer The map renderer. - * @param {import("../../layer/Layer.js").default} layer The layer to be rendererd. - * @return {WebGLVectorLayerRenderer} The layer renderer. - */ -WebGLVectorLayerRenderer['create'] = function(mapRenderer, layer) { - return new WebGLVectorLayerRenderer( - /** @type {import("./Map.js").default} */ (mapRenderer), - /** @type {import("../../layer/Vector.js").default} */ (layer) - ); -}; - - export default WebGLVectorLayerRenderer; From c50b9b2c25e7f323f6c16788ad35b3cf249251dc Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 4 Nov 2018 19:49:34 -0700 Subject: [PATCH 09/15] Fix examples that use WebGLMap --- examples/icon-sprite-webgl.js | 2 +- examples/layer-clipping-webgl.js | 2 +- examples/side-by-side.js | 7 ++++++- examples/symbol-atlas-webgl.js | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/examples/icon-sprite-webgl.js b/examples/icon-sprite-webgl.js index 0ea50ca555..6119d6ac7d 100644 --- a/examples/icon-sprite-webgl.js +++ b/examples/icon-sprite-webgl.js @@ -2,7 +2,7 @@ import Feature from '../src/ol/Feature.js'; import Map from '../src/ol/WebGLMap.js'; import View from '../src/ol/View.js'; import Point from '../src/ol/geom/Point.js'; -import VectorLayer from '../src/ol/layer/Vector.js'; +import VectorLayer from '../src/ol/layer/WebGLVector.js'; import VectorSource from '../src/ol/source/Vector.js'; import {Icon, Style} from '../src/ol/style.js'; diff --git a/examples/layer-clipping-webgl.js b/examples/layer-clipping-webgl.js index 8f4f2caeec..adb352fb57 100644 --- a/examples/layer-clipping-webgl.js +++ b/examples/layer-clipping-webgl.js @@ -1,7 +1,7 @@ import Map from '../src/ol/WebGLMap.js'; import View from '../src/ol/View.js'; import {WEBGL} from '../src/ol/has.js'; -import TileLayer from '../src/ol/layer/Tile.js'; +import TileLayer from '../src/ol/layer/WebGLTile.js'; import OSM from '../src/ol/source/OSM.js'; if (!WEBGL) { diff --git a/examples/side-by-side.js b/examples/side-by-side.js index 121ba51a0e..dd261df886 100644 --- a/examples/side-by-side.js +++ b/examples/side-by-side.js @@ -2,12 +2,17 @@ import Map from '../src/ol/Map.js'; import WebGLMap from '../src/ol/WebGLMap.js'; import View from '../src/ol/View.js'; import TileLayer from '../src/ol/layer/Tile.js'; +import WebGLTileLayer from '../src/ol/layer/WebGLTile.js'; import OSM from '../src/ol/source/OSM.js'; const layer = new TileLayer({ source: new OSM() }); +const webGLLayer = new WebGLTileLayer({ + source: new OSM() +}); + const view = new View({ center: [0, 0], zoom: 1 @@ -21,6 +26,6 @@ const map1 = new Map({ const map2 = new WebGLMap({ target: 'webglMap', - layers: [layer], + layers: [webGLLayer], view: view }); diff --git a/examples/symbol-atlas-webgl.js b/examples/symbol-atlas-webgl.js index a86e79164c..322e71974b 100644 --- a/examples/symbol-atlas-webgl.js +++ b/examples/symbol-atlas-webgl.js @@ -2,7 +2,7 @@ import Feature from '../src/ol/Feature.js'; import Map from '../src/ol/WebGLMap.js'; import View from '../src/ol/View.js'; import Point from '../src/ol/geom/Point.js'; -import VectorLayer from '../src/ol/layer/Vector.js'; +import VectorLayer from '../src/ol/layer/WebGLVector.js'; import VectorSource from '../src/ol/source/Vector.js'; import {AtlasManager, Circle as CircleStyle, Fill, RegularShape, Stroke, Style} from '../src/ol/style.js'; From ca5b0c63a582687cd847658c7d503b73b1fd4eb4 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 4 Nov 2018 19:54:55 -0700 Subject: [PATCH 10/15] Remove LayerType --- src/ol/LayerType.js | 14 -------------- src/ol/layer/Base.js | 15 --------------- src/ol/layer/BaseImage.js | 9 --------- src/ol/layer/BaseTile.js | 8 -------- src/ol/layer/BaseVector.js | 7 ------- src/ol/layer/VectorTile.js | 7 ------- src/ol/renderer/Map.js | 2 +- 7 files changed, 1 insertion(+), 61 deletions(-) delete mode 100644 src/ol/LayerType.js diff --git a/src/ol/LayerType.js b/src/ol/LayerType.js deleted file mode 100644 index a51a68121c..0000000000 --- a/src/ol/LayerType.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @module ol/LayerType - */ - -/** - * A layer type used when creating layer renderers. - * @enum {string} - */ -export default { - IMAGE: 'IMAGE', - TILE: 'TILE', - VECTOR_TILE: 'VECTOR_TILE', - VECTOR: 'VECTOR' -}; diff --git a/src/ol/layer/Base.js b/src/ol/layer/Base.js index f733eb2421..124841f1e9 100644 --- a/src/ol/layer/Base.js +++ b/src/ol/layer/Base.js @@ -65,21 +65,6 @@ class BaseLayer extends BaseObject { */ this.state_ = null; - /** - * The layer type. - * @type {import("../LayerType.js").default} - * @protected; - */ - this.type; - - } - - /** - * Get the layer type (used when creating a layer renderer). - * @return {import("../LayerType.js").default} The layer type. - */ - getType() { - return this.type; } /** diff --git a/src/ol/layer/BaseImage.js b/src/ol/layer/BaseImage.js index 1d02eeadc5..d11edc7efe 100644 --- a/src/ol/layer/BaseImage.js +++ b/src/ol/layer/BaseImage.js @@ -1,7 +1,6 @@ /** * @module ol/layer/BaseImage */ -import LayerType from '../LayerType.js'; import Layer from './Layer.js'; @@ -46,14 +45,6 @@ class BaseImageLayer extends Layer { constructor(opt_options) { const options = opt_options ? opt_options : {}; super(options); - - /** - * The layer type. - * @protected - * @type {import("../LayerType.js").default} - */ - this.type = LayerType.IMAGE; - } } diff --git a/src/ol/layer/BaseTile.js b/src/ol/layer/BaseTile.js index cc9f876093..efeea306a6 100644 --- a/src/ol/layer/BaseTile.js +++ b/src/ol/layer/BaseTile.js @@ -1,7 +1,6 @@ /** * @module ol/layer/BaseTile */ -import LayerType from '../LayerType.js'; import Layer from './Layer.js'; import TileProperty from './TileProperty.js'; import {assign} from '../obj.js'; @@ -59,13 +58,6 @@ class BaseTileLayer extends Layer { this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? options.useInterimTilesOnError : true); - /** - * The layer type. - * @protected - * @type {import("../LayerType.js").default} - */ - this.type = LayerType.TILE; - } /** diff --git a/src/ol/layer/BaseVector.js b/src/ol/layer/BaseVector.js index 57dc79eb90..6498e00e92 100644 --- a/src/ol/layer/BaseVector.js +++ b/src/ol/layer/BaseVector.js @@ -1,7 +1,6 @@ /** * @module ol/layer/BaseVector */ -import LayerType from '../LayerType.js'; import Layer from './Layer.js'; import {assign} from '../obj.js'; import {createDefaultStyle, toFunction as toStyleFunction} from '../style/Style.js'; @@ -123,12 +122,6 @@ class BaseVectorLayer extends Layer { this.updateWhileInteracting_ = options.updateWhileInteracting !== undefined ? options.updateWhileInteracting : false; - /** - * The layer type. - * @protected - * @type {import("../LayerType.js").default} - */ - this.type = LayerType.VECTOR; } /** diff --git a/src/ol/layer/VectorTile.js b/src/ol/layer/VectorTile.js index 5938b2098e..58d65e5495 100644 --- a/src/ol/layer/VectorTile.js +++ b/src/ol/layer/VectorTile.js @@ -1,7 +1,6 @@ /** * @module ol/layer/VectorTile */ -import LayerType from '../LayerType.js'; import {assert} from '../asserts.js'; import TileProperty from './TileProperty.js'; import BaseVectorLayer from './BaseVector.js'; @@ -113,12 +112,6 @@ class VectorTileLayer extends BaseVectorLayer { this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? options.useInterimTilesOnError : true); - /** - * The layer type. - * @protected - * @type {import("../LayerType.js").default} - */ - this.type = LayerType.VECTOR_TILE; } /** diff --git a/src/ol/renderer/Map.js b/src/ol/renderer/Map.js index 9e3679f7c6..dd8bff7c49 100644 --- a/src/ol/renderer/Map.js +++ b/src/ol/renderer/Map.js @@ -206,7 +206,7 @@ class MapRenderer extends Disposable { const renderer = layer.getRenderer(this); if (!renderer) { - throw new Error('Unable to create renderer for layer: ' + layer.getType()); + throw new Error('Unable to create renderer for layer'); } this.layerRenderers_[layerKey] = renderer; From a69eeceebafa3164b537ee683124affba87dddd5 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 4 Nov 2018 20:29:54 -0700 Subject: [PATCH 11/15] Switch map and layer constructor in tests The webgl tests do not run currently (which is why these have not been failing). --- test/rendering/ol/layer/image.test.js | 17 ++++---- test/rendering/ol/layer/tile.test.js | 41 +++++++++++--------- test/rendering/ol/layer/vector.test.js | 31 +++++++-------- test/rendering/ol/layer/vectorimage.test.js | 17 ++++---- test/rendering/ol/layer/vectortile.test.js | 15 ++++--- test/rendering/ol/map.test.js | 10 +++-- test/rendering/ol/source/raster.test.js | 5 +-- test/rendering/ol/source/tilewms.test.js | 15 ++++--- test/rendering/ol/style/circle.test.js | 10 +++-- test/rendering/ol/style/icon.test.js | 10 +++-- test/rendering/ol/style/linestring.test.js | 10 +++-- test/rendering/ol/style/polygon.test.js | 10 +++-- test/rendering/ol/style/regularshape.test.js | 10 +++-- test/rendering/ol/style/text.test.js | 10 +++-- 14 files changed, 122 insertions(+), 89 deletions(-) diff --git a/test/rendering/ol/layer/image.test.js b/test/rendering/ol/layer/image.test.js index 136b577c6c..17ef62cf35 100644 --- a/test/rendering/ol/layer/image.test.js +++ b/test/rendering/ol/layer/image.test.js @@ -1,6 +1,8 @@ import Map from '../../../../src/ol/Map.js'; +import WebGLMap from '../../../../src/ol/WebGLMap.js'; import View from '../../../../src/ol/View.js'; import ImageLayer from '../../../../src/ol/layer/Image.js'; +import WebGLImageLayer from '../../../../src/ol/layer/Image.js'; import {assign} from '../../../../src/ol/obj.js'; import {get as getProjection, transform, transformExtent} from '../../../../src/ol/proj.js'; import Static from '../../../../src/ol/source/ImageStatic.js'; @@ -12,10 +14,10 @@ describe('ol.rendering.layer.Image', function() { let map; function createMap(renderer) { - map = new Map({ + const MapConstructor = renderer === 'webgl' ? WebGLMap : Map; + map = new MapConstructor({ pixelRatio: 1, target: createMapDiv(50, 50), - renderer: renderer, view: new View({ center: transform( [-122.416667, 37.783333], 'EPSG:4326', 'EPSG:3857'), @@ -31,7 +33,8 @@ describe('ol.rendering.layer.Image', function() { map = null; }); - function waitForImages(sources, layerOptions, onImagesLoaded) { + function waitForImages(renderer, sources, layerOptions, onImagesLoaded) { + const LayerConstructor = renderer === 'webgl' ? WebGLImageLayer : ImageLayer; let imagesLoading = 0; let imagesLoaded = 0; @@ -57,7 +60,7 @@ describe('ol.rendering.layer.Image', function() { source: source }; assign(options, layerOptions); - map.addLayer(new ImageLayer(options)); + map.addLayer(new LayerConstructor(options)); }); } @@ -75,7 +78,7 @@ describe('ol.rendering.layer.Image', function() { it('tests the canvas renderer', function(done) { createMap('canvas'); - waitForImages([source], {}, function() { + waitForImages('canvas', [source], {}, function() { expectResemble(map, 'rendering/ol/layer/expected/image-canvas.png', IMAGE_TOLERANCE, done); }); @@ -84,7 +87,7 @@ describe('ol.rendering.layer.Image', function() { where('WebGL').it('tests the WebGL renderer', function(done) { assertWebGL(); createMap('webgl'); - waitForImages([source], {}, function() { + waitForImages('webgl', [source], {}, function() { expectResemble(map, 'rendering/ol/layer/expected/image-webgl.png', IMAGE_TOLERANCE, done); }); @@ -104,7 +107,7 @@ describe('ol.rendering.layer.Image', function() { it('renders correctly', function(done) { createMap('canvas'); - waitForImages([source], {}, function() { + waitForImages('canvas', [source], {}, function() { expectResemble(map, 'rendering/ol/layer/expected/image-scaled.png', IMAGE_TOLERANCE, done); }); diff --git a/test/rendering/ol/layer/tile.test.js b/test/rendering/ol/layer/tile.test.js index d5a800434c..54705c3f08 100644 --- a/test/rendering/ol/layer/tile.test.js +++ b/test/rendering/ol/layer/tile.test.js @@ -1,8 +1,10 @@ import Map from '../../../../src/ol/Map.js'; +import WebGLMap from '../../../../src/ol/WebGLMap.js'; import View from '../../../../src/ol/View.js'; import {getSize} from '../../../../src/ol/extent.js'; import Point from '../../../../src/ol/geom/Point.js'; import TileLayer from '../../../../src/ol/layer/Tile.js'; +import WebGLTileLayer from '../../../../src/ol/layer/WebGLTile.js'; import {assign} from '../../../../src/ol/obj.js'; import {transform} from '../../../../src/ol/proj.js'; import TileImage from '../../../../src/ol/source/TileImage.js'; @@ -18,12 +20,12 @@ describe('ol.rendering.layer.Tile', function() { let map; function createMap(renderer, opt_center, opt_size, opt_pixelRatio, opt_resolutions) { + const MapConstructor = renderer === 'webgl' ? WebGLMap : Map; const size = opt_size !== undefined ? opt_size : [50, 50]; - map = new Map({ + map = new MapConstructor({ pixelRatio: opt_pixelRatio || 1, target: createMapDiv(size[0], size[1]), - renderer: renderer, view: new View({ center: opt_center !== undefined ? opt_center : transform( [-122.416667, 37.783333], 'EPSG:4326', 'EPSG:3857'), @@ -40,7 +42,8 @@ describe('ol.rendering.layer.Tile', function() { map = null; }); - function waitForTiles(sources, layerOptions, onTileLoaded) { + function waitForTiles(renderer, sources, layerOptions, onTileLoaded) { + const LayerConstructor = renderer === 'webgl' ? WebGLTileLayer : TileLayer; let tilesLoading = 0; let tileLoaded = 0; @@ -66,7 +69,7 @@ describe('ol.rendering.layer.Tile', function() { source: source }; assign(options, layerOptions[i] || layerOptions); - map.addLayer(new TileLayer(options)); + map.addLayer(new LayerConstructor(options)); }); } @@ -76,7 +79,7 @@ describe('ol.rendering.layer.Tile', function() { const source = new XYZ({ url: 'rendering/ol/data/tiles/osm/{z}/{x}/{y}.png' }); - waitForTiles([source], {}, function() { + waitForTiles('canvas', [source], {}, function() { setTimeout(function() { expectResemble(map, 'rendering/ol/layer/expected/osm-canvas.png', IMAGE_TOLERANCE, done); @@ -97,7 +100,7 @@ describe('ol.rendering.layer.Tile', function() { it('tests the canvas renderer', function(done) { createMap('canvas'); - waitForTiles([source], {}, function() { + waitForTiles('canvas', [source], {}, function() { expectResemble(map, 'rendering/ol/layer/expected/osm-canvas.png', IMAGE_TOLERANCE, done); }); @@ -106,7 +109,7 @@ describe('ol.rendering.layer.Tile', function() { where('WebGL').it('tests the WebGL renderer', function(done) { assertWebGL(); createMap('webgl'); - waitForTiles([source], {}, function() { + waitForTiles('webgl', [source], {}, function() { expectResemble(map, 'rendering/ol/layer/expected/osm-webgl.png', IMAGE_TOLERANCE, done); }); @@ -130,7 +133,7 @@ describe('ol.rendering.layer.Tile', function() { where('WebGL').it('tests the WebGL renderer', function(done) { assertWebGL(); createMap('webgl'); - waitForTiles([source1, source2], {}, function() { + waitForTiles('webgl', [source1, source2], {}, function() { expectResemble(map, 'rendering/ol/layer/expected/2-layers-webgl.png', IMAGE_TOLERANCE, done); }); @@ -145,7 +148,7 @@ describe('ol.rendering.layer.Tile', function() { it('tests canvas layer extent clipping', function(done) { createMap('canvas'); - waitForTiles([source1, source2], [{}, {extent: centerExtent(map)}], function() { + waitForTiles('canvas', [source1, source2], [{}, {extent: centerExtent(map)}], function() { expectResemble(map, 'rendering/ol/layer/expected/2-layers-canvas-extent.png', IMAGE_TOLERANCE, done); }); @@ -154,7 +157,7 @@ describe('ol.rendering.layer.Tile', function() { it('tests canvas layer extent clipping with rotation', function(done) { createMap('canvas'); map.getView().setRotation(Math.PI / 2); - waitForTiles([source1, source2], [{}, {extent: centerExtent(map)}], function() { + waitForTiles('canvas', [source1, source2], [{}, {extent: centerExtent(map)}], function() { expectResemble(map, 'rendering/ol/layer/expected/2-layers-canvas-extent-rotate.png', IMAGE_TOLERANCE, done); }); @@ -162,7 +165,7 @@ describe('ol.rendering.layer.Tile', function() { it('tests canvas layer extent clipping (HiDPI)', function(done) { createMap('canvas', undefined, undefined, 2); - waitForTiles([source1, source2], [{}, {extent: centerExtent(map)}], function() { + waitForTiles('canvas', [source1, source2], [{}, {extent: centerExtent(map)}], function() { expectResemble(map, 'rendering/ol/layer/expected/2-layers-canvas-extent-hidpi.png', IMAGE_TOLERANCE, done); }); @@ -171,7 +174,7 @@ describe('ol.rendering.layer.Tile', function() { it('tests canvas layer extent clipping with rotation (HiDPI)', function(done) { createMap('canvas', undefined, undefined, 2); map.getView().setRotation(Math.PI / 2); - waitForTiles([source1, source2], [{}, {extent: centerExtent(map)}], function() { + waitForTiles('canvas', [source1, source2], [{}, {extent: centerExtent(map)}], function() { expectResemble(map, 'rendering/ol/layer/expected/2-layers-canvas-extent-rotate-hidpi.png', IMAGE_TOLERANCE, done); }); @@ -191,7 +194,7 @@ describe('ol.rendering.layer.Tile', function() { it('tests the canvas renderer', function(done) { createMap('canvas'); - waitForTiles([source], {opacity: 0.2}, function() { + waitForTiles('canvas', [source], {opacity: 0.2}, function() { expectResemble(map, 'rendering/ol/layer/expected/opacity-canvas.png', IMAGE_TOLERANCE, done); }); @@ -200,7 +203,7 @@ describe('ol.rendering.layer.Tile', function() { where('WebGL').it('tests the WebGL renderer', function(done) { assertWebGL(); createMap('webgl'); - waitForTiles([source], {opacity: 0.2}, function() { + waitForTiles('webgl', [source], {opacity: 0.2}, function() { expectResemble(map, 'rendering/ol/layer/expected/opacity-webgl.png', IMAGE_TOLERANCE, done); }); @@ -222,7 +225,7 @@ describe('ol.rendering.layer.Tile', function() { it('512x256 renders correcly using the canvas renderer', function(done) { const source = createSource('512x256'); createMap('canvas', [-10997148, 4569099]); - waitForTiles([source], {}, function() { + waitForTiles('canvas', [source], {}, function() { expectResemble(map, 'rendering/ol/layer/expected/512x256-canvas.png', IMAGE_TOLERANCE, done); }); @@ -232,7 +235,7 @@ describe('ol.rendering.layer.Tile', function() { assertWebGL(); const source = createSource('512x256'); createMap('webgl', [-10997148, 4569099]); - waitForTiles([source], {}, function() { + waitForTiles('webgl', [source], {}, function() { expectResemble(map, 'rendering/ol/layer/expected/512x256-webgl.png', IMAGE_TOLERANCE, done); }); @@ -242,7 +245,7 @@ describe('ol.rendering.layer.Tile', function() { const source = createSource('192x256'); createMap('canvas', [-11271098, 3747248], [100, 100], undefined, source.getTileGrid().getResolutions()); - waitForTiles([source], {}, function() { + waitForTiles('canvas', [source], {}, function() { expectResemble(map, 'rendering/ol/layer/expected/192x256-canvas.png', IMAGE_TOLERANCE, done); }); @@ -253,7 +256,7 @@ describe('ol.rendering.layer.Tile', function() { const source = createSource('192x256'); createMap('webgl', [-11271098, 3747248], [100, 100], undefined, source.getTileGrid().getResolutions()); - waitForTiles([source], {}, function() { + waitForTiles('webgl', [source], {}, function() { expectResemble(map, 'rendering/ol/layer/expected/192x256-webgl.png', IMAGE_TOLERANCE, done); }); @@ -284,7 +287,7 @@ describe('ol.rendering.layer.Tile', function() { it('works with the canvas renderer', function(done) { createMap('canvas', undefined, [100, 100]); map.getLayers().on('add', onAddLayer); - waitForTiles([source], {}, function() { + waitForTiles('canvas', [source], {}, function() { expectResemble(map, 'rendering/ol/layer/expected/render-canvas.png', IMAGE_TOLERANCE, done); }); diff --git a/test/rendering/ol/layer/vector.test.js b/test/rendering/ol/layer/vector.test.js index 963e97b09f..545d30a9a2 100644 --- a/test/rendering/ol/layer/vector.test.js +++ b/test/rendering/ol/layer/vector.test.js @@ -20,11 +20,10 @@ describe('ol.rendering.layer.Vector', function() { const center = [1825927.7316762917, 6143091.089223046]; let map; - function createMap(renderer) { + function createMap() { map = new Map({ pixelRatio: 1, target: createMapDiv(80, 80), - renderer: renderer, view: new View({ center: center, zoom: 13 @@ -74,7 +73,7 @@ describe('ol.rendering.layer.Vector', function() { }); it('renders opacity correctly with the canvas renderer', function(done) { - createMap('canvas'); + createMap(); const smallLine = new Feature(new LineString([ [center[0], center[1] - 1], [center[0], center[1] + 1] @@ -100,7 +99,7 @@ describe('ol.rendering.layer.Vector', function() { }); it('renders transparent layers correctly with the canvas renderer', function(done) { - createMap('canvas'); + createMap(); const smallLine = new Feature(new LineString([ [center[0], center[1] - 1], [center[0], center[1] + 1] @@ -139,7 +138,7 @@ describe('ol.rendering.layer.Vector', function() { }); it('renders rotation correctly with the canvas renderer', function(done) { - createMap('canvas'); + createMap(); map.getView().setRotation(Math.PI + Math.PI / 4); addPolygon(300); addCircle(500); @@ -159,7 +158,7 @@ describe('ol.rendering.layer.Vector', function() { }); it('renders fill/stroke batches correctly with the canvas renderer', function(done) { - createMap('canvas'); + createMap(); source = new VectorSource({ overlaps: false }); @@ -185,7 +184,7 @@ describe('ol.rendering.layer.Vector', function() { }); it('renders stroke batches correctly with the canvas renderer', function(done) { - createMap('canvas'); + createMap(); source = new VectorSource({ overlaps: false }); @@ -209,7 +208,7 @@ describe('ol.rendering.layer.Vector', function() { }); it('interrupts fill/stroke batches correctly with the canvas renderer', function(done) { - createMap('canvas'); + createMap(); let color; function createSource(overlaps) { color = '#3399CC'; @@ -262,7 +261,7 @@ describe('ol.rendering.layer.Vector', function() { }); it('interrupts stroke batches correctly with the canvas renderer', function(done) { - createMap('canvas'); + createMap(); let color; function createSource(overlaps) { color = '#3399CC'; @@ -469,7 +468,7 @@ describe('ol.rendering.layer.Vector', function() { }); it('declutters text', function(done) { - createMap('canvas'); + createMap(); const layer = new VectorLayer({ source: source }); @@ -509,7 +508,7 @@ describe('ol.rendering.layer.Vector', function() { }); it('declutters text and respects z-index', function(done) { - createMap('canvas'); + createMap(); const layer = new VectorLayer({ source: source }); @@ -549,7 +548,7 @@ describe('ol.rendering.layer.Vector', function() { }); it('declutters images', function(done) { - createMap('canvas'); + createMap(); const layer = new VectorLayer({ source: source }); @@ -588,7 +587,7 @@ describe('ol.rendering.layer.Vector', function() { }); it('declutters images and respects z-index', function(done) { - createMap('canvas'); + createMap(); const layer = new VectorLayer({ source: source }); @@ -627,7 +626,7 @@ describe('ol.rendering.layer.Vector', function() { }); it('declutters image & text groups', function(done) { - createMap('canvas'); + createMap(); const layer = new VectorLayer({ source: source }); @@ -671,7 +670,7 @@ describe('ol.rendering.layer.Vector', function() { }); it('declutters text along lines and images', function(done) { - createMap('canvas'); + createMap(); const layer = new VectorLayer({ source: source }); @@ -714,7 +713,7 @@ describe('ol.rendering.layer.Vector', function() { }); it('declutters text along lines and images with z-index', function(done) { - createMap('canvas'); + createMap(); const layer = new VectorLayer({ source: source }); diff --git a/test/rendering/ol/layer/vectorimage.test.js b/test/rendering/ol/layer/vectorimage.test.js index 036f0f0654..c2b2a0a320 100644 --- a/test/rendering/ol/layer/vectorimage.test.js +++ b/test/rendering/ol/layer/vectorimage.test.js @@ -18,12 +18,11 @@ describe('ol.rendering.layer.VectorImage', function() { const center = [1825927.7316762917, 6143091.089223046]; let map, source; - function createMap(renderer) { + function createMap() { source = new VectorSource(); map = new Map({ pixelRatio: 1, target: createMapDiv(80, 80), - renderer: renderer, view: new View({ center: center, zoom: 13 @@ -55,7 +54,7 @@ describe('ol.rendering.layer.VectorImage', function() { } it('renders opacity correctly', function(done) { - createMap('canvas'); + createMap(); const smallLine = new Feature(new LineString([ [center[0], center[1] - 1], [center[0], center[1] + 1] @@ -81,7 +80,7 @@ describe('ol.rendering.layer.VectorImage', function() { }); it('renders transparent layers correctly', function(done) { - createMap('canvas'); + createMap(); const smallLine = new Feature(new LineString([ [center[0], center[1] - 1], [center[0], center[1] + 1] @@ -120,7 +119,7 @@ describe('ol.rendering.layer.VectorImage', function() { }); it('renders rotation correctly', function(done) { - createMap('canvas'); + createMap(); map.getView().setRotation(Math.PI + Math.PI / 4); addPolygon(300); addCircle(500); @@ -140,7 +139,7 @@ describe('ol.rendering.layer.VectorImage', function() { }); it('unskips features correctly', function(done) { - createMap('canvas'); + createMap(); addCircle(500); addPolygon(300); map.skipFeature(source.getFeatures()[1]); @@ -165,7 +164,7 @@ describe('ol.rendering.layer.VectorImage', function() { }); it('declutters text', function(done) { - createMap('canvas'); + createMap(); const layer = new VectorImageLayer({ source: source }); @@ -205,7 +204,7 @@ describe('ol.rendering.layer.VectorImage', function() { }); it('declutters images', function(done) { - createMap('canvas'); + createMap(); const layer = new VectorImageLayer({ source: source }); @@ -244,7 +243,7 @@ describe('ol.rendering.layer.VectorImage', function() { }); it('declutters text along lines and images', function(done) { - createMap('canvas'); + createMap(); const layer = new VectorImageLayer({ source: source }); diff --git a/test/rendering/ol/layer/vectortile.test.js b/test/rendering/ol/layer/vectortile.test.js index 36e107898e..d7ff102877 100644 --- a/test/rendering/ol/layer/vectortile.test.js +++ b/test/rendering/ol/layer/vectortile.test.js @@ -19,12 +19,11 @@ describe('ol.rendering.layer.VectorTile', function() { let map; - function createMap(renderer, opt_pixelRatio, opt_size) { + function createMap(opt_pixelRatio, opt_size) { const size = opt_size || 50; map = new Map({ pixelRatio: opt_pixelRatio || 1, target: createMapDiv(size, size), - renderer: renderer, view: new View({ center: [1825927.7316762917, 6143091.089223046], zoom: 14 @@ -78,7 +77,7 @@ describe('ol.rendering.layer.VectorTile', function() { }); it('renders correctly with the canvas renderer', function(done) { - createMap('canvas'); + createMap(); waitForTiles(source, {}, function() { expectResemble(map, 'rendering/ol/layer/expected/vectortile-canvas.png', 22, done); @@ -86,7 +85,7 @@ describe('ol.rendering.layer.VectorTile', function() { }); it('renders rotated view correctly with the canvas renderer', function(done) { - createMap('canvas'); + createMap(); map.getView().setRotation(Math.PI / 4); waitForTiles(source, {}, function() { expectResemble(map, 'rendering/ol/layer/expected/vectortile-canvas-rotated.png', @@ -95,7 +94,7 @@ describe('ol.rendering.layer.VectorTile', function() { }); it('renders rotated view correctly with vector layer on top', function(done) { - createMap('canvas'); + createMap(); const vectorSource = new VectorSource({ features: [ new Feature(new Point([1825727.7316762917, 6143091.089223046])) @@ -121,7 +120,7 @@ describe('ol.rendering.layer.VectorTile', function() { }); it('renders correctly with the canvas renderer (HiDPI)', function(done) { - createMap('canvas', 2); + createMap(2); waitForTiles(source, {}, function() { expectResemble(map, 'rendering/ol/layer/expected/vectortile-canvas-hidpi.png', 11.3, done); @@ -129,7 +128,7 @@ describe('ol.rendering.layer.VectorTile', function() { }); it('renders rotated view correctly with the canvas renderer (HiDPI)', function(done) { - createMap('canvas', 2); + createMap(2); map.getView().setRotation(Math.PI / 4); waitForTiles(source, {}, function() { expectResemble(map, 'rendering/ol/layer/expected/vectortile-canvas-rotated-hidpi.png', @@ -138,7 +137,7 @@ describe('ol.rendering.layer.VectorTile', function() { }); it('declutters text and images', function(done) { - createMap('canvas', 1, 100); + createMap(1, 100); map.getView().setZoom(13.8); const style = function(feature, resolution) { const geom = feature.getGeometry(); diff --git a/test/rendering/ol/map.test.js b/test/rendering/ol/map.test.js index fed97af903..4a3a7cda9b 100644 --- a/test/rendering/ol/map.test.js +++ b/test/rendering/ol/map.test.js @@ -1,8 +1,10 @@ import Feature from '../../../src/ol/Feature.js'; import Point from '../../../src/ol/geom/Point.js'; import Map from '../../../src/ol/Map.js'; +import WebGLMap from '../../../src/ol/WebGLMap.js'; import View from '../../../src/ol/View.js'; import VectorLayer from '../../../src/ol/layer/Vector.js'; +import WebGLVectorLayer from '../../../src/ol/layer/WebGLVector.js'; import VectorSource from '../../../src/ol/source/Vector.js'; @@ -10,7 +12,10 @@ describe('ol.rendering.Map', function() { let map; function createMap(renderer) { - const vectorLayer = new VectorLayer({ + const MapConstructor = renderer === 'webgl' ? WebGLMap : Map; + const LayerConstructor = renderer === 'webgl' ? WebGLVectorLayer : VectorLayer; + + const vectorLayer = new LayerConstructor({ source: new VectorSource({ features: [new Feature({ geometry: new Point([0, 0]) @@ -18,10 +23,9 @@ describe('ol.rendering.Map', function() { }) }); - map = new Map({ + map = new MapConstructor({ pixelRatio: 1, target: createMapDiv(50, 50), - renderer: renderer, layers: [vectorLayer], view: new View({ projection: 'EPSG:4326', diff --git a/test/rendering/ol/source/raster.test.js b/test/rendering/ol/source/raster.test.js index d102917f87..4a156341f7 100644 --- a/test/rendering/ol/source/raster.test.js +++ b/test/rendering/ol/source/raster.test.js @@ -27,11 +27,10 @@ where('Uint8ClampedArray').describe('ol.rendering.source.Raster', function() { } let map; - function createMap(renderer, pixelRatio) { + function createMap(pixelRatio) { map = new Map({ target: createMapDiv(200, 200), pixelRatio: pixelRatio, - renderer: renderer, view: new View({ center: [0, 0], zoom: 0 @@ -48,7 +47,7 @@ where('Uint8ClampedArray').describe('ol.rendering.source.Raster', function() { describe('raster source rendering', function() { it('renders the result of an operation', function(done) { - createMap('canvas', 1); + createMap(1); const source = new XYZ({ url: 'rendering/ol/data/tiles/osm/{z}/{x}/{y}.png', diff --git a/test/rendering/ol/source/tilewms.test.js b/test/rendering/ol/source/tilewms.test.js index c01f753790..d8802d98c7 100644 --- a/test/rendering/ol/source/tilewms.test.js +++ b/test/rendering/ol/source/tilewms.test.js @@ -1,6 +1,8 @@ +import WebGLMap from '../../../../src/ol/WebGLMap.js'; import Map from '../../../../src/ol/Map.js'; import View from '../../../../src/ol/View.js'; import TileLayer from '../../../../src/ol/layer/Tile.js'; +import WebGLTileLayer from '../../../../src/ol/layer/WebGLTile.js'; import TileWMS from '../../../../src/ol/source/TileWMS.js'; describe('ol.rendering.source.TileWMS', function() { @@ -25,10 +27,11 @@ describe('ol.rendering.source.TileWMS', function() { let map; function createMap(renderer, pixelRatio) { - map = new Map({ + const MapConstructor = renderer === 'webgl' ? WebGLMap : Map; + + map = new MapConstructor({ target: createMapDiv(200, 200), pixelRatio: pixelRatio, - renderer: renderer, view: new View({ center: [0, 0], zoom: 5 @@ -74,7 +77,7 @@ describe('ol.rendering.source.TileWMS', function() { tilesLoaded(source, function() { expectResemble(map, 'rendering/ol/source/expected/0_1.webgl.png', IMAGE_TOLERANCE, done); }); - map.addLayer(new TileLayer({ + map.addLayer(new WebGLTileLayer({ source: source })); }); @@ -99,7 +102,7 @@ describe('ol.rendering.source.TileWMS', function() { tilesLoaded(source, function() { expectResemble(map, 'rendering/ol/source/expected/0_2.webgl.png', IMAGE_TOLERANCE, done); }); - map.addLayer(new TileLayer({ + map.addLayer(new WebGLTileLayer({ source: source })); }); @@ -125,7 +128,7 @@ describe('ol.rendering.source.TileWMS', function() { tilesLoaded(source, function() { expectResemble(map, 'rendering/ol/source/expected/20_1.webgl.png', IMAGE_TOLERANCE, done); }); - map.addLayer(new TileLayer({ + map.addLayer(new WebGLTileLayer({ source: source })); }); @@ -150,7 +153,7 @@ describe('ol.rendering.source.TileWMS', function() { tilesLoaded(source, function() { expectResemble(map, 'rendering/ol/source/expected/20_2.webgl.png', IMAGE_TOLERANCE, done); }); - map.addLayer(new TileLayer({ + map.addLayer(new WebGLTileLayer({ source: source })); }); diff --git a/test/rendering/ol/style/circle.test.js b/test/rendering/ol/style/circle.test.js index 394be3e15d..c078a8f74d 100644 --- a/test/rendering/ol/style/circle.test.js +++ b/test/rendering/ol/style/circle.test.js @@ -2,8 +2,10 @@ import Feature from '../../../../src/ol/Feature.js'; import Point from '../../../../src/ol/geom/Point.js'; import MultiPoint from '../../../../src/ol/geom/MultiPoint.js'; import Map from '../../../../src/ol/Map.js'; +import WebGLMap from '../../../../src/ol/WebGLMap.js'; import View from '../../../../src/ol/View.js'; import VectorLayer from '../../../../src/ol/layer/Vector.js'; +import WebGLVectorLayer from '../../../../src/ol/layer/Vector.js'; import VectorSource from '../../../../src/ol/source/Vector.js'; import CircleStyle from '../../../../src/ol/style/Circle.js'; import Fill from '../../../../src/ol/style/Fill.js'; @@ -16,15 +18,17 @@ describe('ol.rendering.style.Circle', function() { let map, vectorSource; function createMap(renderer) { + const MapConstructor = renderer === 'webgl' ? WebGLMap : Map; + const LayerConstructor = renderer === 'webgl' ? WebGLVectorLayer : VectorLayer; + vectorSource = new VectorSource(); - const vectorLayer = new VectorLayer({ + const vectorLayer = new LayerConstructor({ source: vectorSource }); - map = new Map({ + map = new MapConstructor({ pixelRatio: 1, target: createMapDiv(50, 50), - renderer: renderer, layers: [vectorLayer], view: new View({ projection: 'EPSG:4326', diff --git a/test/rendering/ol/style/icon.test.js b/test/rendering/ol/style/icon.test.js index a901d436b9..aa9da7875e 100644 --- a/test/rendering/ol/style/icon.test.js +++ b/test/rendering/ol/style/icon.test.js @@ -1,8 +1,10 @@ import Feature from '../../../../src/ol/Feature.js'; import Point from '../../../../src/ol/geom/Point.js'; import Map from '../../../../src/ol/Map.js'; +import WebGLMap from '../../../../src/ol/WebGLMap.js'; import View from '../../../../src/ol/View.js'; import VectorLayer from '../../../../src/ol/layer/Vector.js'; +import WebGLVectorLayer from '../../../../src/ol/layer/Vector.js'; import VectorSource from '../../../../src/ol/source/Vector.js'; import Icon from '../../../../src/ol/style/Icon.js'; import Style from '../../../../src/ol/style/Style.js'; @@ -22,15 +24,17 @@ describe('ol.rendering.style.Icon', function() { }; function createMap(renderer, width, height) { + const MapConstructor = renderer === 'webgl' ? WebGLMap : Map; + const LayerConstructor = renderer === 'webgl' ? WebGLVectorLayer : VectorLayer; + vectorSource = new VectorSource(); - const vectorLayer = new VectorLayer({ + const vectorLayer = new LayerConstructor({ source: vectorSource }); - map = new Map({ + map = new MapConstructor({ pixelRatio: 1, target: createMapDiv(width ? width : 50, height ? height : 50), - renderer: renderer, layers: [vectorLayer], view: new View({ projection: 'EPSG:4326', diff --git a/test/rendering/ol/style/linestring.test.js b/test/rendering/ol/style/linestring.test.js index 19e46ba959..9853dee081 100644 --- a/test/rendering/ol/style/linestring.test.js +++ b/test/rendering/ol/style/linestring.test.js @@ -1,8 +1,10 @@ import Feature from '../../../../src/ol/Feature.js'; import LineString from '../../../../src/ol/geom/LineString.js'; import Map from '../../../../src/ol/Map.js'; +import WebGLMap from '../../../../src/ol/WebGLMap.js'; import View from '../../../../src/ol/View.js'; import VectorLayer from '../../../../src/ol/layer/Vector.js'; +import WebGLVectorLayer from '../../../../src/ol/layer/Vector.js'; import VectorSource from '../../../../src/ol/source/Vector.js'; import Style from '../../../../src/ol/style/Style.js'; import Stroke from '../../../../src/ol/style/Stroke.js'; @@ -13,15 +15,17 @@ describe('ol.rendering.style.LineString', function() { let map, vectorSource; function createMap(renderer, opt_pixelRatio) { + const MapConstructor = renderer === 'webgl' ? WebGLMap : Map; + const LayerConstructor = renderer === 'webgl' ? WebGLVectorLayer : VectorLayer; + vectorSource = new VectorSource(); - const vectorLayer = new VectorLayer({ + const vectorLayer = new LayerConstructor({ source: vectorSource }); - map = new Map({ + map = new MapConstructor({ pixelRatio: opt_pixelRatio || 1, target: createMapDiv(50, 50), - renderer: renderer, layers: [vectorLayer], view: new View({ projection: 'EPSG:4326', diff --git a/test/rendering/ol/style/polygon.test.js b/test/rendering/ol/style/polygon.test.js index fadfd3e039..50c8606c7c 100644 --- a/test/rendering/ol/style/polygon.test.js +++ b/test/rendering/ol/style/polygon.test.js @@ -1,8 +1,10 @@ import Feature from '../../../../src/ol/Feature.js'; import Polygon from '../../../../src/ol/geom/Polygon.js'; import Map from '../../../../src/ol/Map.js'; +import WebGLMap from '../../../../src/ol/WebGLMap.js'; import View from '../../../../src/ol/View.js'; import VectorLayer from '../../../../src/ol/layer/Vector.js'; +import WebGLVectorLayer from '../../../../src/ol/layer/Vector.js'; import VectorSource from '../../../../src/ol/source/Vector.js'; import Fill from '../../../../src/ol/style/Fill.js'; import Style from '../../../../src/ol/style/Style.js'; @@ -14,17 +16,19 @@ describe('ol.rendering.style.Polygon', function() { let map, vectorSource; function createMap(renderer, opt_size) { + const MapConstructor = renderer === 'webgl' ? WebGLMap : Map; + const LayerConstructor = renderer === 'webgl' ? WebGLVectorLayer : VectorLayer; + const size = opt_size || 50; vectorSource = new VectorSource(); - const vectorLayer = new VectorLayer({ + const vectorLayer = new LayerConstructor({ source: vectorSource }); - map = new Map({ + map = new MapConstructor({ pixelRatio: 1, target: createMapDiv(size, size), - renderer: renderer, layers: [vectorLayer], view: new View({ projection: 'EPSG:4326', diff --git a/test/rendering/ol/style/regularshape.test.js b/test/rendering/ol/style/regularshape.test.js index 5ee362fb33..d91f89513f 100644 --- a/test/rendering/ol/style/regularshape.test.js +++ b/test/rendering/ol/style/regularshape.test.js @@ -1,8 +1,10 @@ import Feature from '../../../../src/ol/Feature.js'; import Point from '../../../../src/ol/geom/Point.js'; import Map from '../../../../src/ol/Map.js'; +import WebGLMap from '../../../../src/ol/WebGLMap.js'; import View from '../../../../src/ol/View.js'; import VectorLayer from '../../../../src/ol/layer/Vector.js'; +import WebGLVectorLayer from '../../../../src/ol/layer/Vector.js'; import VectorSource from '../../../../src/ol/source/Vector.js'; import Fill from '../../../../src/ol/style/Fill.js'; import RegularShape from '../../../../src/ol/style/RegularShape.js'; @@ -15,15 +17,17 @@ describe('ol.rendering.style.RegularShape', function() { let map, vectorSource; function createMap(renderer) { + const MapConstructor = renderer === 'webgl' ? WebGLMap : Map; + const LayerConstructor = renderer === 'webgl' ? WebGLVectorLayer : VectorLayer; + vectorSource = new VectorSource(); - const vectorLayer = new VectorLayer({ + const vectorLayer = new LayerConstructor({ source: vectorSource }); - map = new Map({ + map = new MapConstructor({ pixelRatio: 1, target: createMapDiv(50, 50), - renderer: renderer, layers: [vectorLayer], view: new View({ projection: 'EPSG:4326', diff --git a/test/rendering/ol/style/text.test.js b/test/rendering/ol/style/text.test.js index bc5b95c70b..f22816968d 100644 --- a/test/rendering/ol/style/text.test.js +++ b/test/rendering/ol/style/text.test.js @@ -5,8 +5,10 @@ import MultiPolygon from '../../../../src/ol/geom/MultiPolygon.js'; import Point from '../../../../src/ol/geom/Point.js'; import Polygon from '../../../../src/ol/geom/Polygon.js'; import Map from '../../../../src/ol/Map.js'; +import WebGLMap from '../../../../src/ol/WebGLMap.js'; import View from '../../../../src/ol/View.js'; import VectorLayer from '../../../../src/ol/layer/Vector.js'; +import WebGLVectorLayer from '../../../../src/ol/layer/Vector.js'; import VectorSource from '../../../../src/ol/source/Vector.js'; import Text from '../../../../src/ol/style/Text.js'; import Fill from '../../../../src/ol/style/Fill.js'; @@ -18,16 +20,18 @@ describe('ol.rendering.style.Text', function() { let map, vectorSource; function createMap(renderer, opt_pixelRatio) { + const MapConstructor = renderer === 'webgl' ? WebGLMap : Map; + const LayerConstructor = renderer === 'webgl' ? WebGLVectorLayer : VectorLayer; + const pixelRatio = opt_pixelRatio || 1; vectorSource = new VectorSource(); - const vectorLayer = new VectorLayer({ + const vectorLayer = new LayerConstructor({ source: vectorSource }); - map = new Map({ + map = new MapConstructor({ pixelRatio: pixelRatio, target: createMapDiv(200 / pixelRatio, 200 / pixelRatio), - renderer: renderer, layers: [vectorLayer], view: new View({ projection: 'EPSG:4326', From fc6882f146e0eea4e612e89cf7cd1d384bc233a7 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sat, 10 Nov 2018 08:03:56 -0700 Subject: [PATCH 12/15] Utility method for efficiently managing child nodes --- src/ol/dom.js | 43 ++++++++++ test/spec/ol/dom/dom.test.js | 162 ++++++++++++++++++++++++++++++++++- 2 files changed, 204 insertions(+), 1 deletion(-) diff --git a/src/ol/dom.js b/src/ol/dom.js index f5bcfa0ba1..abaadfd186 100644 --- a/src/ol/dom.js +++ b/src/ol/dom.js @@ -79,3 +79,46 @@ export function removeChildren(node) { node.removeChild(node.lastChild); } } + +/** + * Transform the children of a parent node so they match the + * provided list of children. This function aims to efficiently + * remove, add, and reorder child nodes while maintaining a simple + * implementation (it is not guaranteed to minimize DOM operations). + * @param {Node} node The parent node whose children need reworking. + * @param {Array} children The desired children. + */ +export function replaceChildren(node, children) { + const oldChildren = node.childNodes; + + for (let i = 0; true; ++i) { + const oldChild = oldChildren[i]; + const newChild = children[i]; + + // check if our work is done + if (!oldChild && !newChild) { + break; + } + + // check if children match + if (oldChild === newChild) { + continue; + } + + // check if a new child needs to be added + if (!oldChild) { + node.appendChild(newChild); + continue; + } + + // check if an old child needs to be removed + if (!newChild) { + node.removeChild(oldChild); + --i; + continue; + } + + // reorder + node.insertBefore(newChild, oldChild); + } +} diff --git a/test/spec/ol/dom/dom.test.js b/test/spec/ol/dom/dom.test.js index ce164d6540..445af35c1b 100644 --- a/test/spec/ol/dom/dom.test.js +++ b/test/spec/ol/dom/dom.test.js @@ -1,4 +1,4 @@ -import {createCanvasContext2D, outerWidth, outerHeight} from '../../../../src/ol/dom.js'; +import {createCanvasContext2D, outerWidth, outerHeight, replaceChildren} from '../../../../src/ol/dom.js'; describe('ol.dom', function() { @@ -344,4 +344,164 @@ describe('ol.dom', function() { }); + describe('replaceChildren()', function() { + + function assertChildrenMatch(parent, children) { + const actual = parent.childNodes; + expect(actual).to.have.length(children.length); + for (let i = 0; i < children.length; i++) { + expect(actual[i]).to.be(children[i]); + } + } + + it('adds new children to an empty parent', function() { + const parent = document.createElement('div'); + const children = [ + document.createElement('a'), + document.createElement('b'), + document.createElement('c') + ]; + + replaceChildren(parent, children); + assertChildrenMatch(parent, children); + }); + + it('removes children', function() { + const parent = document.createElement('div'); + const existingChildren = [ + document.createElement('a'), + document.createElement('b'), + document.createElement('c') + ]; + existingChildren.forEach(function(child) { + parent.appendChild(child); + }); + + replaceChildren(parent, []); + expect(parent.childNodes).to.have.length(0); + }); + + it('swaps children', function() { + const parent = document.createElement('div'); + const existingChildren = [ + document.createElement('a'), + document.createElement('b'), + document.createElement('c') + ]; + existingChildren.forEach(function(child) { + parent.appendChild(child); + }); + + const newChildren = [ + document.createElement('d'), + document.createElement('e'), + document.createElement('f') + ]; + + replaceChildren(parent, newChildren); + assertChildrenMatch(parent, newChildren); + }); + + it('appends children', function() { + const parent = document.createElement('div'); + const existingChildren = [ + document.createElement('a'), + document.createElement('b'), + document.createElement('c') + ]; + existingChildren.forEach(function(child) { + parent.appendChild(child); + }); + + const newChildren = [ + document.createElement('d'), + document.createElement('e'), + document.createElement('f') + ]; + + const allChildren = existingChildren.concat(newChildren); + + replaceChildren(parent, allChildren); + assertChildrenMatch(parent, allChildren); + }); + + it('prunes children', function() { + const parent = document.createElement('div'); + const existingChildren = [ + document.createElement('a'), + document.createElement('b'), + document.createElement('c'), + document.createElement('d'), + document.createElement('e') + ]; + existingChildren.forEach(function(child) { + parent.appendChild(child); + }); + + const desiredChildren = [ + existingChildren[1], + existingChildren[3] + ]; + + replaceChildren(parent, desiredChildren); + assertChildrenMatch(parent, desiredChildren); + }); + + it('reorders children', function() { + const parent = document.createElement('div'); + const existingChildren = [ + document.createElement('a'), + document.createElement('b'), + document.createElement('c'), + document.createElement('d'), + document.createElement('e') + ]; + existingChildren.forEach(function(child) { + parent.appendChild(child); + }); + + const desiredChildren = [ + existingChildren[1], + existingChildren[3], + existingChildren[0], + existingChildren[4], + existingChildren[2] + ]; + + replaceChildren(parent, desiredChildren); + assertChildrenMatch(parent, desiredChildren); + }); + + it('reorders, prunes, and appends children', function() { + const parent = document.createElement('div'); + const existingChildren = [ + document.createElement('a'), + document.createElement('b'), + document.createElement('c'), + document.createElement('d'), + document.createElement('e') + ]; + existingChildren.forEach(function(child) { + parent.appendChild(child); + }); + + const desiredChildren = [ + document.createElement('f'), + existingChildren[3], + document.createElement('g'), + existingChildren[0], + existingChildren[2] + ]; + + const clone = desiredChildren.slice(); + + replaceChildren(parent, desiredChildren); + assertChildrenMatch(parent, desiredChildren); + + // confirm we haven't modified the input + expect(desiredChildren).to.eql(clone); + }); + + }); + }); From 43ed2c1764891c7a56d2b947976541594d52078a Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sat, 10 Nov 2018 17:39:00 -0600 Subject: [PATCH 13/15] Composite renderer --- src/ol/CompositeMap.js | 84 ++++++++++++++ src/ol/renderer/Composite.js | 151 ++++++++++++++++++++++++++ src/ol/renderer/Layer.js | 39 ++++++- src/ol/renderer/canvas/Layer.js | 28 +++-- src/ol/renderer/canvas/VectorLayer.js | 109 ++++++++++++++++++- 5 files changed, 400 insertions(+), 11 deletions(-) create mode 100644 src/ol/CompositeMap.js create mode 100644 src/ol/renderer/Composite.js diff --git a/src/ol/CompositeMap.js b/src/ol/CompositeMap.js new file mode 100644 index 0000000000..7050fd96f6 --- /dev/null +++ b/src/ol/CompositeMap.js @@ -0,0 +1,84 @@ +/** + * @module ol/CompositeMap + */ +import PluggableMap from './PluggableMap.js'; +import {defaults as defaultControls} from './control/util.js'; +import {defaults as defaultInteractions} from './interaction.js'; +import {assign} from './obj.js'; +import CompositeMapRenderer from './renderer/Composite.js'; + +/** + * @classdesc + * The map is the core component of OpenLayers. For a map to render, a view, + * one or more layers, and a target container are needed: + * + * import Map from 'ol/Map'; + * import View from 'ol/View'; + * import TileLayer from 'ol/layer/Tile'; + * import OSM from 'ol/source/OSM'; + * + * var map = new Map({ + * view: new View({ + * center: [0, 0], + * zoom: 1 + * }), + * layers: [ + * new TileLayer({ + * source: new OSM() + * }) + * ], + * target: 'map' + * }); + * + * The above snippet creates a map using a {@link module:ol/layer/Tile} to + * display {@link module:ol/source/OSM~OSM} OSM data and render it to a DOM + * element with the id `map`. + * + * The constructor places a viewport container (with CSS class name + * `ol-viewport`) in the target element (see `getViewport()`), and then two + * further elements within the viewport: one with CSS class name + * `ol-overlaycontainer-stopevent` for controls and some overlays, and one with + * CSS class name `ol-overlaycontainer` for other overlays (see the `stopEvent` + * option of {@link module:ol/Overlay~Overlay} for the difference). The map + * itself is placed in a further element within the viewport. + * + * Layers are stored as a {@link module:ol/Collection~Collection} in + * layerGroups. A top-level group is provided by the library. This is what is + * accessed by `getLayerGroup` and `setLayerGroup`. Layers entered in the + * options are added to this group, and `addLayer` and `removeLayer` change the + * layer collection in the group. `getLayers` is a convenience function for + * `getLayerGroup().getLayers()`. Note that {@link module:ol/layer/Group~Group} + * is a subclass of {@link module:ol/layer/Base}, so layers entered in the + * options or added with `addLayer` can be groups, which can contain further + * groups, and so on. + * + * @fires import("./MapBrowserEvent.js").MapBrowserEvent + * @fires import("./MapEvent.js").MapEvent + * @fires module:ol/render/Event~RenderEvent#postcompose + * @fires module:ol/render/Event~RenderEvent#precompose + * @api + */ +class CompositeMap extends PluggableMap { + + /** + * @param {import("./PluggableMap.js").MapOptions} options Map options. + */ + constructor(options) { + options = assign({}, options); + if (!options.controls) { + options.controls = defaultControls(); + } + if (!options.interactions) { + options.interactions = defaultInteractions(); + } + + super(options); + } + + createRenderer() { + return new CompositeMapRenderer(this); + } +} + + +export default CompositeMap; diff --git a/src/ol/renderer/Composite.js b/src/ol/renderer/Composite.js new file mode 100644 index 0000000000..6cbe9c586a --- /dev/null +++ b/src/ol/renderer/Composite.js @@ -0,0 +1,151 @@ +/** + * @module ol/renderer/canvas/Map + */ +import {apply as applyTransform} from '../transform.js'; +import {stableSort} from '../array.js'; +import {CLASS_UNSELECTABLE} from '../css.js'; +import {visibleAtResolution} from '../layer/Layer.js'; +import RenderEvent from '../render/Event.js'; +import RenderEventType from '../render/EventType.js'; +import MapRenderer, {sortByZIndex} from './Map.js'; +import SourceState from '../source/State.js'; +import {replaceChildren} from '../dom.js'; + + +/** + * @classdesc + * Canvas map renderer. + * @api + */ +class CompositeMapRenderer extends MapRenderer { + + /** + * @param {import("../PluggableMap.js").default} map Map. + */ + constructor(map) { + super(map); + + /** + * @private + * @type {HTMLDivElement} + */ + this.element_ = document.createElement('div'); + + this.element_.style.width = '100%'; + this.element_.style.height = '100%'; + this.element_.className = CLASS_UNSELECTABLE; + + const container = map.getViewport(); + container.insertBefore(this.element_, container.firstChild || null); + + /** + * @private + * @type {Array} + */ + this.children_ = []; + + /** + * @private + * @type {boolean} + */ + this.renderedVisible_ = true; + } + + /** + * @param {import("../render/EventType.js").default} type Event type. + * @param {import("../PluggableMap.js").FrameState} frameState Frame state. + */ + dispatchRenderEvent(type, frameState) { + const map = this.getMap(); + if (map.hasListener(type)) { + const event = new RenderEvent(type, undefined, frameState); + map.dispatchEvent(event); + } + } + + /** + * @inheritDoc + */ + renderFrame(frameState) { + if (!frameState) { + if (this.renderedVisible_) { + this.element_.style.display = 'none'; + this.renderedVisible_ = false; + } + return; + } + + this.calculateMatrices2D(frameState); + this.dispatchRenderEvent(RenderEventType.PRECOMPOSE, frameState); + + const layerStatesArray = frameState.layerStatesArray; + stableSort(layerStatesArray, sortByZIndex); + + const rotation = frameState.viewState.rotation; + if (rotation) { + // TODO: apply rotation + } + + const viewResolution = frameState.viewState.resolution; + + this.children_.length = 0; + for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) { + const layerState = layerStatesArray[i]; + if (!visibleAtResolution(layerState, viewResolution) || layerState.sourceState != SourceState.READY) { + continue; + } + + const layer = layerState.layer; + const layerRenderer = this.getLayerRenderer(layer); + if (layerRenderer.prepareFrame(frameState, layerState)) { + const element = layerRenderer.renderFrame(frameState, layerState); + // TODO: deal with opacity + this.children_.push(element); + } + } + + replaceChildren(this.element_, this.children_); + + this.dispatchRenderEvent(RenderEventType.POSTCOMPOSE, frameState); + + if (!this.renderedVisible_) { + this.element_.style.display = ''; + this.renderedVisible_ = true; + } + + this.scheduleRemoveUnusedLayerRenderers(frameState); + this.scheduleExpireIconCache(frameState); + } + + /** + * @inheritDoc + */ + forEachLayerAtPixel(pixel, frameState, hitTolerance, callback, thisArg, layerFilter, thisArg2) { + let result; + const viewState = frameState.viewState; + const viewResolution = viewState.resolution; + + const layerStates = frameState.layerStatesArray; + const numLayers = layerStates.length; + + const coordinate = applyTransform( + frameState.pixelToCoordinateTransform, pixel.slice()); + + for (let i = numLayers - 1; i >= 0; --i) { + const layerState = layerStates[i]; + const layer = layerState.layer; + if (visibleAtResolution(layerState, viewResolution) && layerFilter.call(thisArg2, layer)) { + const layerRenderer = this.getLayerRenderer(layer); + result = layerRenderer.forEachLayerAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg); + if (result) { + return result; + } + } + } + return undefined; + } + +} + + +export default CompositeMapRenderer; diff --git a/src/ol/renderer/Layer.js b/src/ol/renderer/Layer.js index c1294826db..06de5216a5 100644 --- a/src/ol/renderer/Layer.js +++ b/src/ol/renderer/Layer.js @@ -1,7 +1,7 @@ /** * @module ol/renderer/Layer */ -import {getUid} from '../util.js'; +import {getUid, abstract} from '../util.js'; import ImageState from '../ImageState.js'; import Observable from '../Observable.js'; import TileState from '../TileState.js'; @@ -26,6 +26,28 @@ class LayerRenderer extends Observable { } + /** + * Determine whether render should be called. + * @abstract + * @param {import("../PluggableMap.js").FrameState} frameState Frame state. + * @param {import("../layer/Layer.js").State} layerState Layer state. + * @return {boolean} Layer is ready to be rendered. + */ + prepareFrame(frameState, layerState) { + return abstract(); + } + + /** + * Render the layer. + * @abstract + * @param {import("../PluggableMap.js").FrameState} frameState Frame state. + * @param {import("../layer/Layer.js").State} layerState Layer state. + * @return {HTMLElement} The rendered element. + */ + renderFrame(frameState, layerState) { + return abstract(); + } + /** * Create a function that adds loaded tiles to the tile lookup. * @param {import("../source/Tile.js").default} source Tile source. @@ -68,6 +90,21 @@ class LayerRenderer extends Observable { */ forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback) {} + /** + * @abstract + * @param {import("../coordinate.js").Coordinate} coordinate Coordinate. + * @param {import("../PluggableMap.js").FrameState} frameState FrameState. + * @param {number} hitTolerance Hit tolerance in pixels. + * @param {function(this: S, import("../layer/Layer.js").default, (Uint8ClampedArray|Uint8Array)): T} callback Layer + * callback. + * @param {S} thisArg Value to use as `this` when executing `callback`. + * @return {T|undefined} Callback result. + * @template S,T + */ + forEachLayerAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg) { + return abstract(); + } + /** * @return {import("../layer/Layer.js").default} Layer. */ diff --git a/src/ol/renderer/canvas/Layer.js b/src/ol/renderer/canvas/Layer.js index 4136428322..47b0a582ae 100644 --- a/src/ol/renderer/canvas/Layer.js +++ b/src/ol/renderer/canvas/Layer.js @@ -136,6 +136,25 @@ class CanvasLayerRenderer extends LayerRenderer { this.dispatchComposeEvent_(RenderEventType.PRECOMPOSE, context, frameState, opt_transform); } + /** + * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. + * @param {import("../../transform.js").Transform=} opt_transform Transform. + * @protected + */ + preRender(frameState, opt_transform) { + // TODO: pre-render event + } + + /** + * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. + * @param {import("../../layer/Layer.js").State} layerState Layer state. + * @param {import("../../transform.js").Transform=} opt_transform Transform. + * @protected + */ + postRender(frameState, layerState, opt_transform) { + // TODO: pre-render event + } + /** * @param {CanvasRenderingContext2D} context Context. * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. @@ -175,15 +194,6 @@ class CanvasLayerRenderer extends LayerRenderer { abstract(); } - /** - * @abstract - * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. - * @param {import("../../layer/Layer.js").State} layerState Layer state. - * @return {boolean} whether composeFrame should be called. - */ - prepareFrame(frameState, layerState) { - return abstract(); - } } export default CanvasLayerRenderer; diff --git a/src/ol/renderer/canvas/VectorLayer.js b/src/ol/renderer/canvas/VectorLayer.js index ea30d981e2..dcabe4fcbc 100644 --- a/src/ol/renderer/canvas/VectorLayer.js +++ b/src/ol/renderer/canvas/VectorLayer.js @@ -82,7 +82,6 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { this.context = createCanvasContext2D(); listen(labelCache, EventType.CLEAR, this.handleFontsChanged_, this); - } /** @@ -225,6 +224,114 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { this.postCompose(context, frameState, layerState, transform); } + /** + * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. + * @param {import("../../layer/Layer.js").State} layerState Layer state. + */ + render(frameState, layerState) { + const replayGroup = this.replayGroup_; + if (!replayGroup || replayGroup.isEmpty()) { + return; + } + + const context = this.context; + const canvas = context.canvas; + + const extent = frameState.extent; + const pixelRatio = frameState.pixelRatio; + const viewState = frameState.viewState; + const projection = viewState.projection; + const rotation = viewState.rotation; + const projectionExtent = projection.getExtent(); + const vectorSource = /** @type {import("../../source/Vector.js").default} */ (this.getLayer().getSource()); + + // clipped rendering if layer extent is set + const clipExtent = layerState.extent; + const clipped = clipExtent !== undefined; + if (clipped) { + this.clip(context, frameState, /** @type {import("../../extent.js").Extent} */ (clipExtent)); + } + + if (this.declutterTree_) { + this.declutterTree_.clear(); + } + + // resize and clear + let width = Math.round(frameState.size[0] * pixelRatio); + let height = Math.round(frameState.size[1] * pixelRatio); + if (rotation) { + const size = Math.round(Math.sqrt(width * width + height * height)); + width = height = size; + } + if (canvas.width != width || canvas.height != height) { + canvas.width = width; + canvas.height = height; + canvas.style.width = (width / pixelRatio) + 'px'; + canvas.style.height = (height / pixelRatio) + 'px'; + } else { + context.clearRect(0, 0, width, height); + } + + const viewHints = frameState.viewHints; + const snapToPixel = !(viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]); + + // TODO: deal with rotation (this should not be necessary) + if (rotation) { + rotateAtOffset(context, -rotation, width / 2, height / 2); + } + + let transform = this.getTransform(frameState, 0); + const skippedFeatureUids = layerState.managed ? frameState.skippedFeatureUids : {}; + replayGroup.replay(context, transform, rotation, skippedFeatureUids, snapToPixel); + + if (vectorSource.getWrapX() && projection.canWrapX() && !containsExtent(projectionExtent, extent)) { + let startX = extent[0]; + const worldWidth = getWidth(projectionExtent); + let world = 0; + let offsetX; + while (startX < projectionExtent[0]) { + --world; + offsetX = worldWidth * world; + transform = this.getTransform(frameState, offsetX); + replayGroup.replay(context, transform, rotation, skippedFeatureUids, snapToPixel); + startX += worldWidth; + } + world = 0; + startX = extent[2]; + while (startX > projectionExtent[2]) { + ++world; + offsetX = worldWidth * world; + transform = this.getTransform(frameState, offsetX); + replayGroup.replay(context, transform, rotation, skippedFeatureUids, snapToPixel); + startX -= worldWidth; + } + } + + // TODO: deal with rotation (this should not be necessary) + if (rotation) { + rotateAtOffset(context, rotation, width / 2, height / 2); + } + + if (this.getLayer().hasListener(RenderEventType.RENDER)) { + this.dispatchRenderEvent(context, frameState, transform); + } + + if (clipped) { + context.restore(); + } + } + + /** + * @inheritDoc + */ + renderFrame(frameState, layerState) { + const transform = this.getTransform(frameState, 0); + this.preRender(frameState, transform); + this.render(frameState, layerState); + this.postRender(frameState, layerState, transform); + return this.context.canvas; + } + /** * @inheritDoc */ From dc05f48294d7b801de749b029e8b0e7d2ec5d651 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 11 Nov 2018 14:17:12 -0600 Subject: [PATCH 14/15] Use composite renderer in linestring test --- rendering/cases/linestring-style/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rendering/cases/linestring-style/main.js b/rendering/cases/linestring-style/main.js index 800659c76f..d5cd8e4236 100644 --- a/rendering/cases/linestring-style/main.js +++ b/rendering/cases/linestring-style/main.js @@ -1,4 +1,4 @@ -import Map from '../../../src/ol/Map.js'; +import Map from '../../../src/ol/CompositeMap.js'; import View from '../../../src/ol/View.js'; import Feature from '../../../src/ol/Feature.js'; import LineString from '../../../src/ol/geom/LineString.js'; From a9b2952be4ef204db9e7c52cea9cdc92b7489c6e Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 11 Nov 2018 14:18:20 -0600 Subject: [PATCH 15/15] Add support for running a subset of rendering tests --- rendering/test.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/rendering/test.js b/rendering/test.js index 616699b61c..13ca07b736 100755 --- a/rendering/test.js +++ b/rendering/test.js @@ -275,9 +275,13 @@ async function getOutdated(entries, options) { } async function main(entries, options) { - if (!options.force) { + if (!options.force && !options.match) { entries = await getOutdated(entries, options); } + if (options.match) { + const exp = new RegExp(options.match); + entries = entries.filter(entry => exp.test(entry)); + } if (!options.interactive && entries.length === 0) { return; } @@ -308,10 +312,9 @@ if (require.main === module) { type: 'number', default: 3000 }). - option('timeout', { - describe: 'The timeout for loading pages (in milliseconds)', - type: 'number', - default: 60000 + option('match', { + describe: 'Only run tests matching the provided string RegExp pattern', + type: 'string' }). option('force', { describe: 'Run all tests (instead of just outdated tests)', @@ -328,6 +331,11 @@ if (require.main === module) { choices: ['trace', 'debug', 'info', 'warn', 'error', 'silent'], default: 'error' }). + option('timeout', { + describe: 'The timeout for loading pages (in milliseconds)', + type: 'number', + default: 60000 + }). option('headless', { describe: 'Launch Puppeteer in headless mode', type: 'boolean',