diff --git a/examples/dynamic-data.js b/examples/dynamic-data.js index 00d87e840e..30ce4df391 100644 --- a/examples/dynamic-data.js +++ b/examples/dynamic-data.js @@ -1,11 +1,14 @@ +goog.require('ol.Feature'); goog.require('ol.Map'); goog.require('ol.View'); goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.Point'); goog.require('ol.layer.Tile'); goog.require('ol.source.MapQuest'); goog.require('ol.style.Circle'); goog.require('ol.style.Fill'); goog.require('ol.style.Stroke'); +goog.require('ol.style.Style'); var map = new ol.Map({ @@ -14,6 +17,7 @@ var map = new ol.Map({ source: new ol.source.MapQuest({layer: 'sat'}) }) ], + renderer: exampleNS.getRendererFromQueryString(), target: 'map', view: new ol.View({ center: [0, 0], @@ -28,6 +32,20 @@ var imageStyle = new ol.style.Circle({ stroke: new ol.style.Stroke({color: 'red', width: 1}) }); +var headInnerImageStyle = new ol.style.Style({ + image: new ol.style.Circle({ + radius: 2, + snapToPixel: false, + fill: new ol.style.Fill({color: 'blue'}) + }) +}); + +var headOuterImageStyle = new ol.style.Circle({ + radius: 5, + snapToPixel: false, + fill: new ol.style.Fill({color: 'black'}) +}); + var n = 200; var omegaTheta = 30000; // Rotation period in ms var R = 7e6; @@ -48,6 +66,16 @@ map.on('postcompose', function(event) { vectorContext.setImageStyle(imageStyle); vectorContext.drawMultiPointGeometry( new ol.geom.MultiPoint(coordinates), null); + + var headPoint = new ol.geom.Point(coordinates[coordinates.length - 1]); + var headFeature = new ol.Feature(headPoint); + vectorContext.drawFeature(headFeature, headInnerImageStyle); + + vectorContext.setImageStyle(headOuterImageStyle); + vectorContext.drawMultiPointGeometry(headPoint, null); + + vectorContext.flush(); + map.render(); }); map.render(); diff --git a/src/ol/render/ivectorcontext.js b/src/ol/render/ivectorcontext.js index 22ba124831..be1a3f9a5d 100644 --- a/src/ol/render/ivectorcontext.js +++ b/src/ol/render/ivectorcontext.js @@ -6,7 +6,7 @@ goog.provide('ol.render.IVectorContext'); /** * VectorContext interface. Currently implemented by - * {@link ol.render.canvas.Immediate} + * {@link ol.render.canvas.Immediate} and {@link ol.render.webgl.Immediate} * @interface */ ol.render.IVectorContext = function() { @@ -15,7 +15,7 @@ ol.render.IVectorContext = function() { /** * @param {number} zIndex Z index. - * @param {function(ol.render.canvas.Immediate)} callback Callback. + * @param {function(ol.render.IVectorContext)} callback Callback. */ ol.render.IVectorContext.prototype.drawAsync = function(zIndex, callback) { }; diff --git a/src/ol/render/webgl/webglimmediate.js b/src/ol/render/webgl/webglimmediate.js index 375b0c96cf..0a520dc0af 100644 --- a/src/ol/render/webgl/webglimmediate.js +++ b/src/ol/render/webgl/webglimmediate.js @@ -1,4 +1,9 @@ goog.provide('ol.render.webgl.Immediate'); +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.object'); +goog.require('ol.extent'); +goog.require('ol.render.webgl.ReplayGroup'); @@ -6,17 +11,102 @@ goog.provide('ol.render.webgl.Immediate'); * @constructor * @implements {ol.render.IVectorContext} * @param {ol.webgl.Context} context Context. + * @param {ol.Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {ol.Size} size Size. + * @param {ol.Extent} extent Extent. * @param {number} pixelRatio Pixel ratio. * @struct */ -ol.render.webgl.Immediate = function(context, pixelRatio) { +ol.render.webgl.Immediate = function(context, + center, resolution, rotation, size, extent, pixelRatio) { + + /** + * @private + */ + this.context_ = context; + + /** + * @private + */ + this.center_ = center; + + /** + * @private + */ + this.extent_ = extent; + + /** + * @private + */ + this.pixelRatio_ = pixelRatio; + + /** + * @private + */ + this.size_ = size; + + /** + * @private + */ + this.rotation_ = rotation; + + /** + * @private + */ + this.resolution_ = resolution; + + /** + * @private + * @type {ol.style.Image} + */ + this.imageStyle_ = null; + + /** + * @private + * @type {ol.style.Text} + */ + this.textStyle_ = null; + + /** + * @private + * @type {Object.>} + */ + this.callbacksByZIndex_ = {}; }; /** - * @inheritDoc + * FIXME: empty description for jsdoc + */ +ol.render.webgl.Immediate.prototype.flush = function() { + /** @type {Array.} */ + var zs = goog.array.map(goog.object.getKeys(this.callbacksByZIndex_), Number); + goog.array.sort(zs); + var i, ii, callbacks, j, jj; + for (i = 0, ii = zs.length; i < ii; ++i) { + callbacks = this.callbacksByZIndex_[zs[i].toString()]; + for (j = 0, jj = callbacks.length; j < jj; ++j) { + callbacks[j](this); + } + } +}; + + +/** + * @param {number} zIndex Z index. + * @param {function(ol.render.webgl.Immediate)} callback Callback. */ ol.render.webgl.Immediate.prototype.drawAsync = function(zIndex, callback) { + var zIndexKey = zIndex.toString(); + var callbacks = this.callbacksByZIndex_[zIndexKey]; + if (goog.isDef(callbacks)) { + callbacks.push(callback); + } else { + this.callbacksByZIndex_[zIndexKey] = [callback]; + } }; @@ -32,6 +122,24 @@ ol.render.webgl.Immediate.prototype.drawCircleGeometry = * @inheritDoc */ ol.render.webgl.Immediate.prototype.drawFeature = function(feature, style) { + var geometry = feature.getGeometry(); + if (!goog.isDefAndNotNull(geometry) || + !ol.extent.intersects(this.extent_, geometry.getExtent())) { + return; + } + var zIndex = style.getZIndex(); + if (!goog.isDef(zIndex)) { + zIndex = 0; + } + this.drawAsync(zIndex, function(render) { + render.setFillStrokeStyle(style.getFill(), style.getStroke()); + render.setImageStyle(style.getImage()); + render.setTextStyle(style.getText()); + var renderGeometry = + ol.render.webgl.Immediate.GEOMETRY_RENDERERS_[geometry.getType()]; + goog.asserts.assert(goog.isDef(renderGeometry)); + renderGeometry.call(render, geometry, null); + }); }; @@ -40,6 +148,15 @@ ol.render.webgl.Immediate.prototype.drawFeature = function(feature, style) { */ ol.render.webgl.Immediate.prototype.drawGeometryCollectionGeometry = function(geometryCollectionGeometry, data) { + var geometries = geometryCollectionGeometry.getGeometriesArray(); + var renderers = ol.render.webgl.Immediate.GEOMETRY_RENDERERS_; + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + var geometry = geometries[i]; + var geometryRenderer = renderers[geometry.getType()]; + goog.asserts.assert(goog.isDef(geometryRenderer)); + geometryRenderer.call(this, geometry, data); + } }; @@ -48,6 +165,20 @@ ol.render.webgl.Immediate.prototype.drawGeometryCollectionGeometry = */ ol.render.webgl.Immediate.prototype.drawPointGeometry = function(pointGeometry, data) { + var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_); + var replay = replayGroup.getReplay(0, ol.render.ReplayType.IMAGE); + replay.setImageStyle(this.imageStyle_); + replay.drawPointGeometry(pointGeometry, data); + replay.finish(this.context_); + // default colors + var opacity = 1; + var brightness = 0; + var contrast = 1; + var hue = 0; + var saturation = 1; + replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, + this.size_, this.extent_, this.pixelRatio_, opacity, brightness, + contrast, hue, saturation, {}); }; @@ -72,6 +203,20 @@ ol.render.webgl.Immediate.prototype.drawMultiLineStringGeometry = */ ol.render.webgl.Immediate.prototype.drawMultiPointGeometry = function(multiPointGeometry, data) { + var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_); + var replay = replayGroup.getReplay(0, ol.render.ReplayType.IMAGE); + replay.setImageStyle(this.imageStyle_); + replay.drawMultiPointGeometry(multiPointGeometry, data); + replay.finish(this.context_); + // default colors + var opacity = 1; + var brightness = 0; + var contrast = 1; + var hue = 0; + var saturation = 1; + replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, + this.size_, this.extent_, this.pixelRatio_, opacity, brightness, + contrast, hue, saturation, {}); }; @@ -96,6 +241,13 @@ ol.render.webgl.Immediate.prototype.drawPolygonGeometry = */ ol.render.webgl.Immediate.prototype.drawText = function(flatCoordinates, offset, end, stride, geometry, data) { + var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_); + var replay = replayGroup.getReplay(0, ol.render.ReplayType.TEXT); + replay.setTextStyle(this.textStyle_); + replay.drawText(flatCoordinates, offset, end, stride, geometry, data); + replay.finish(this.context_); + replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, + this.size_, this.extent_, this.pixelRatio_, {}); }; @@ -111,6 +263,7 @@ ol.render.webgl.Immediate.prototype.setFillStrokeStyle = * @inheritDoc */ ol.render.webgl.Immediate.prototype.setImageStyle = function(imageStyle) { + this.imageStyle_ = imageStyle; }; @@ -118,4 +271,20 @@ ol.render.webgl.Immediate.prototype.setImageStyle = function(imageStyle) { * @inheritDoc */ ol.render.webgl.Immediate.prototype.setTextStyle = function(textStyle) { + this.textStyle_ = textStyle; +}; + + +/** + * @const + * @private + * @type {Object.} + */ +ol.render.webgl.Immediate.GEOMETRY_RENDERERS_ = { + 'Point': ol.render.webgl.Immediate.prototype.drawPointGeometry, + 'MultiPoint': ol.render.webgl.Immediate.prototype.drawMultiPointGeometry, + 'GeometryCollection': + ol.render.webgl.Immediate.prototype.drawGeometryCollectionGeometry }; diff --git a/src/ol/renderer/webgl/webgllayerrenderer.js b/src/ol/renderer/webgl/webgllayerrenderer.js index 6c51fc0394..39d5248f10 100644 --- a/src/ol/renderer/webgl/webgllayerrenderer.js +++ b/src/ol/renderer/webgl/webgllayerrenderer.js @@ -237,7 +237,16 @@ ol.renderer.webgl.Layer.prototype.dispatchComposeEvent_ = function(type, context, frameState) { var layer = this.getLayer(); if (layer.hasListener(type)) { - var render = new ol.render.webgl.Immediate(context, frameState.pixelRatio); + var viewState = frameState.viewState; + var resolution = viewState.resolution; + var pixelRatio = frameState.pixelRatio; + var extent = frameState.extent; + var center = viewState.center; + var rotation = viewState.rotation; + var size = frameState.size; + + var render = new ol.render.webgl.Immediate( + context, center, resolution, rotation, size, extent, pixelRatio); var composeEvent = new ol.render.Event( type, layer, render, null, frameState, null, context); layer.dispatchEvent(composeEvent); diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index ab89377a02..7f7c29e115 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -273,14 +273,19 @@ ol.renderer.webgl.Map.prototype.dispatchComposeEvent_ = var map = this.getMap(); if (map.hasListener(type)) { var context = this.context_; - var extent = frameState.extent; + var extent = frameState.extent; + var size = frameState.size; var viewState = frameState.viewState; - var resolution = viewState.resolution; var pixelRatio = frameState.pixelRatio; + + var resolution = viewState.resolution; + var center = viewState.center; + var rotation = viewState.rotation; var tolerance = ol.renderer.vector.getTolerance(resolution, pixelRatio); - var vectorContext = new ol.render.webgl.Immediate(context, pixelRatio); + var vectorContext = new ol.render.webgl.Immediate(context, + center, resolution, rotation, size, extent, pixelRatio); var replayGroup = new ol.render.webgl.ReplayGroup(tolerance, extent); var composeEvent = new ol.render.Event(type, map, vectorContext, replayGroup, frameState, null, context); @@ -288,9 +293,6 @@ ol.renderer.webgl.Map.prototype.dispatchComposeEvent_ = replayGroup.finish(context); if (!replayGroup.isEmpty()) { - var center = viewState.center; - var rotation = viewState.rotation; - var size = frameState.size; // use default color values var opacity = 1; var brightness = 0;