From 2940eb10fa81ad97ae19238f58ca417cc4cd68a2 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sat, 18 Jan 2014 20:19:55 +0100 Subject: [PATCH] Remove old ol.renderer.canvas.VectorLayer --- .../canvas/canvasvectorlayerrenderer.js | 581 ---------------- .../renderer/canvas/canvasvectorrenderer.js | 646 ------------------ 2 files changed, 1227 deletions(-) delete mode 100644 old/src/ol/renderer/canvas/canvasvectorlayerrenderer.js delete mode 100644 old/src/ol/renderer/canvas/canvasvectorrenderer.js diff --git a/old/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/old/src/ol/renderer/canvas/canvasvectorlayerrenderer.js deleted file mode 100644 index 5203dd6025..0000000000 --- a/old/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ /dev/null @@ -1,581 +0,0 @@ -goog.provide('ol.renderer.canvas.VectorLayer'); - -goog.require('goog.asserts'); -goog.require('goog.dom'); -goog.require('goog.dom.TagName'); -goog.require('goog.events'); -goog.require('goog.object'); -goog.require('goog.vec.Mat4'); -goog.require('ol.Pixel'); -goog.require('ol.TileCache'); -goog.require('ol.TileCoord'); -goog.require('ol.TileRange'); -goog.require('ol.ViewHint'); -goog.require('ol.extent'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.layer.Vector'); -goog.require('ol.layer.VectorEventType'); -goog.require('ol.layer.VectorLayerRenderIntent'); -goog.require('ol.renderer.canvas.Layer'); -goog.require('ol.renderer.canvas.Vector'); -goog.require('ol.tilegrid.TileGrid'); - - -/** - * Resolution at zoom level 21 in a web mercator default tiling scheme. This - * is a workaround for browser bugs that cause line segments to disappear when - * they get too long. TODO: Use line clipping as a better work around. See - * https://github.com/openlayers/ol3/issues/404. - * - * @define {number} The lowest supported resolution value. - */ -ol.renderer.canvas.MIN_RESOLUTION = 0.14929107086948487; - - - -/** - * @constructor - * @extends {ol.renderer.canvas.Layer} - * @param {ol.renderer.Map} mapRenderer Map renderer. - * @param {ol.layer.Vector} layer Vector layer. - */ -ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { - - goog.base(this, mapRenderer, layer); - - /** - * Final canvas made available to the map renderer. - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = /** @type {HTMLCanvasElement} */ - (goog.dom.createElement(goog.dom.TagName.CANVAS)); - - /** - * @private - * @type {CanvasRenderingContext2D} - */ - this.context_ = /** @type {CanvasRenderingContext2D} */ - (this.canvas_.getContext('2d')); - - /** - * @private - * @type {!goog.vec.Mat4.Number} - */ - this.transform_ = goog.vec.Mat4.createNumber(); - - /** - * Interim canvas for drawing newly visible features. - * @private - * @type {HTMLCanvasElement} - */ - this.sketchCanvas_ = /** @type {HTMLCanvasElement} */ - (goog.dom.createElement(goog.dom.TagName.CANVAS)); - - /** - * @private - * @type {!goog.vec.Mat4.Number} - */ - this.sketchTransform_ = goog.vec.Mat4.createNumber(); - - /** - * Tile cache entries are arrays. The first item in each array is the tile - * itself, the second are the symbol sizes, and the third is the maximum - * symbol size. - * - * @private - * @type {ol.TileCache} - */ - this.tileCache_ = new ol.TileCache( - ol.renderer.canvas.VectorLayer.TILECACHE_SIZE); - goog.events.listen(layer, [ - ol.layer.VectorEventType.ADD, - ol.layer.VectorEventType.CHANGE, - ol.layer.VectorEventType.REMOVE, - ol.layer.VectorEventType.INTENTCHANGE - ], - this.handleLayerChange_, false, this); - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.tileArchetype_ = null; - - /** - * The maximum symbol size ever rendered for this layer (in pixels). - * @private - * @type {number} - */ - this.maxSymbolPixelDim_ = 0; - - /** - * @private - * @type {number} - */ - this.renderedResolution_; - - /** - * @private - * @type {ol.Extent} - */ - this.renderedExtent_ = null; - - /** - * Flag to be set internally when we know something has changed that suggests - * we need to re-render. - * TODO: discuss setting this for all layers when something changes before - * calling map.render(). - * @private - * @type {boolean} - */ - this.dirty_ = false; - - /** - * @private - * @type {boolean} - */ - this.pendingCachePrune_ = false; - - /** - * Grid used for internal generation of canvas tiles. This is created - * lazily so we have access to the view projection. - * - * @private - * @type {ol.tilegrid.TileGrid} - */ - this.tileGrid_ = null; - - /** - * Tile range before the current animation or interaction. This is updated - * whenever the view is idle. - * - * @private - * @type {ol.TileRange} - */ - this.tileRange_ = new ol.TileRange(NaN, NaN, NaN, NaN); - - /** - * @private - * @type {function()} - */ - this.requestMapRenderFrame_ = goog.bind(function() { - this.dirty_ = true; - mapRenderer.getMap().requestRenderFrame(); - }, this); - -}; -goog.inherits(ol.renderer.canvas.VectorLayer, ol.renderer.canvas.Layer); - - -/** - * Get rid cached tiles. If the optional extent is provided, only tiles that - * intersect that extent will be removed. - * @param {Array.} extents extent Expire tiles within the provided - * extents. If the array is empty, all tiles will be expired. - * @private - */ -ol.renderer.canvas.VectorLayer.prototype.expireTiles_ = function(extents) { - // buffer by max symbolizer size at rendered resolution - var resolution = this.renderedResolution_; - goog.asserts.assertNumber(resolution); - var tileCache = this.tileCache_; - var length = extents.length; - var extent; - if (length > 0) { - for (var i = 0; i < length; ++i) { - extent = ol.extent.clone(extents[i]); - ol.extent.buffer(extent, this.maxSymbolPixelDim_ * resolution / 2); - tileCache.pruneTileRange( - this.tileGrid_.getTileRangeForExtentAndZ(extent, 0)); - } - } else { - tileCache.clear(); - } -}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.VectorLayer.prototype.getImage = function() { - return this.canvas_; -}; - - -/** - * @return {ol.layer.Vector} Vector layer. - */ -ol.renderer.canvas.VectorLayer.prototype.getVectorLayer = function() { - return /** @type {ol.layer.Vector} */ (this.getLayer()); -}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.VectorLayer.prototype.getTransform = function() { - return this.transform_; -}; - - -/** - * @param {ol.Pixel} pixel Pixel coordinate relative to the map viewport. - * @param {function(string, ol.layer.Layer)} success Callback for - * successful queries. The passed arguments are the resulting feature - * information and the layer. - */ -ol.renderer.canvas.VectorLayer.prototype.getFeatureInfoForPixel = - function(pixel, success) { - var callback = function(features, layer) { - success(layer.getTransformFeatureInfo()(features), layer); - }; - this.getFeaturesForPixel(pixel, callback); -}; - - -/** - * @param {ol.Pixel} pixel Pixel coordinate relative to the map viewport. - * @param {function(Array., ol.layer.Layer)} success Callback for - * successful queries. The passed arguments are the resulting features - * and the layer. - * @param {function()=} opt_error Callback for unsuccessful queries. - */ -ol.renderer.canvas.VectorLayer.prototype.getFeaturesForPixel = - function(pixel, success, opt_error) { - // TODO What do we want to pass to the error callback? - var map = this.getMap(); - var result = []; - - var layer = this.getLayer(); - var location = map.getCoordinateFromPixel(pixel); - var tileCoord = this.tileGrid_.getTileCoordForCoordAndZ(location, 0); - var key = tileCoord.toString(); - if (this.tileCache_.containsKey(key)) { - var cachedTile = this.tileCache_.get(key); - var symbolSizes = cachedTile[1]; - var maxSymbolSize = cachedTile[2]; - var symbolOffsets = cachedTile[3]; - var halfMaxWidth = maxSymbolSize[0] / 2; - var halfMaxHeight = maxSymbolSize[1] / 2; - var locationMin = [location[0] - halfMaxWidth, location[1] - halfMaxHeight]; - var locationMax = [location[0] + halfMaxWidth, location[1] + halfMaxHeight]; - var locationBbox = ol.extent.boundingExtent([locationMin, locationMax]); - var candidates = layer.getFeaturesObjectForExtent(locationBbox, - map.getView().getView2D().getProjection()); - if (goog.isNull(candidates)) { - // data is not loaded - if (goog.isDef(opt_error)) { - goog.global.setTimeout(function() { opt_error(); }, 0); - } - return; - } - - var candidate, geom, type, symbolBounds, symbolSize, symbolOffset, - halfWidth, halfHeight, uid, coordinates, j; - for (var id in candidates) { - candidate = candidates[id]; - if (candidate.getRenderIntent() == - ol.layer.VectorLayerRenderIntent.HIDDEN) { - continue; - } - geom = candidate.getGeometry(); - type = geom.getType(); - if (type === ol.geom.GeometryType.POINT || - type === ol.geom.GeometryType.MULTIPOINT) { - // For points, check if the pixel coordinate is inside the candidate's - // symbol - uid = goog.getUid(candidate); - symbolSize = symbolSizes[uid]; - symbolOffset = symbolOffsets[uid]; - halfWidth = symbolSize[0] / 2; - halfHeight = symbolSize[1] / 2; - symbolBounds = ol.extent.boundingExtent([ - [location[0] - halfWidth - symbolOffset[0], - location[1] - halfHeight + symbolOffset[1]], - [location[0] + halfWidth - symbolOffset[0], - location[1] + halfHeight + symbolOffset[1]] - ]); - coordinates = geom.getCoordinates(); - if (!goog.isArray(coordinates[0])) { - coordinates = [coordinates]; - } - for (j = coordinates.length - 1; j >= 0; --j) { - if (ol.extent.containsCoordinate(symbolBounds, coordinates[j])) { - result.push(candidate); - break; - } - } - } else if (goog.isFunction(geom.containsCoordinate)) { - // For polygons, check if the pixel location is inside the polygon - if (geom.containsCoordinate(location)) { - result.push(candidate); - } - } else if (goog.isFunction(geom.distanceFromCoordinate)) { - // For lines, check if the distance to the pixel location is - // within the rendered line width - if (2 * geom.distanceFromCoordinate(location) <= - symbolSizes[goog.getUid(candidate)][0]) { - result.push(candidate); - } - } - } - } - goog.global.setTimeout(function() { success(result, layer); }, 0); -}; - - -/** - * @param {ol.layer.VectorEvent} event Vector layer event. - * @private - */ -ol.renderer.canvas.VectorLayer.prototype.handleLayerChange_ = function(event) { - if (goog.isDef(this.renderedResolution_)) { - this.expireTiles_(event.extents); - } - this.requestMapRenderFrame_(); -}; - - -/** - * @inheritDoc - */ -ol.renderer.canvas.VectorLayer.prototype.prepareFrame = - function(frameState, layerState) { - - // TODO: consider bailing out here if rendered center and resolution - // have not changed. Requires that other change listeners set a dirty flag. - - var view2DState = frameState.view2DState, - resolution = view2DState.resolution, - projection = view2DState.projection, - extent = frameState.extent, - layer = this.getVectorLayer(), - tileGrid = this.tileGrid_, - tileSize = [512, 512], - idle = !frameState.viewHints[ol.ViewHint.ANIMATING] && - !frameState.viewHints[ol.ViewHint.INTERACTING]; - - // lazy tile grid creation - if (idle) { - // avoid rendering issues for very high zoom levels - var minResolution = ol.renderer.canvas.MIN_RESOLUTION; - var metersPerUnit = projection.getMetersPerUnit(); - if (metersPerUnit) { - minResolution = minResolution / metersPerUnit; - } - var gridResolution = Math.max(resolution, minResolution); - if (gridResolution !== this.renderedResolution_) { - tileGrid = new ol.tilegrid.TileGrid({ - origin: [0, 0], - projection: projection, - resolutions: [gridResolution], - tileSize: tileSize - }); - this.tileCache_.clear(); - this.tileGrid_ = tileGrid; - } - } - - if (goog.isNull(tileGrid)) { - // We should only get here when the first call to prepareFrame happens - // during an animation. Try again in the next prepareFrame call. - return; - } - - - // set up transform for the layer canvas to be drawn to the map canvas - var tileResolution = tileGrid.getResolution(0); - if (idle) { - tileGrid.getTileRangeForExtentAndResolution( - extent, tileResolution, this.tileRange_); - } - var transform = this.transform_, - tileRange = this.tileRange_, - tileRangeExtent = tileGrid.getTileRangeExtent(0, tileRange), - sketchOrigin = ol.extent.getTopLeft(tileRangeExtent); - - goog.vec.Mat4.makeIdentity(transform); - goog.vec.Mat4.translate(transform, - frameState.size[0] / 2, - frameState.size[1] / 2, - 0); - goog.vec.Mat4.scale(transform, - tileResolution / resolution, tileResolution / resolution, 1); - goog.vec.Mat4.rotateZ(transform, view2DState.rotation); - goog.vec.Mat4.translate(transform, - (sketchOrigin[0] - view2DState.center[0]) / tileResolution, - (view2DState.center[1] - sketchOrigin[1]) / tileResolution, - 0); - - /** - * Fastest path out of here. This method is called many many times while - * there is nothing to do (e.g. while waiting for tiles from every other - * layer to load.) Do not put anything above here that is more expensive than - * necessary. And look for ways to get here faster. - */ - if (!this.dirty_ && this.renderedResolution_ === tileResolution && - ol.extent.equals(this.renderedExtent_, tileRangeExtent)) { - return; - } - - if (goog.isNull(this.tileArchetype_)) { - this.tileArchetype_ = /** @type {HTMLCanvasElement} */ - (goog.dom.createElement(goog.dom.TagName.CANVAS)); - this.tileArchetype_.width = tileSize[0]; - this.tileArchetype_.height = tileSize[1]; - } - - /** - * Prepare the sketch canvas. This covers the currently visible tile range - * and will have rendered all newly visible features. - */ - var sketchCanvas = this.sketchCanvas_; - var sketchWidth = tileSize[0] * tileRange.getWidth(); - var sketchHeight = tileSize[1] * tileRange.getHeight(); - - // transform for map coords to sketch canvas pixel coords - var sketchTransform = this.sketchTransform_; - var halfWidth = sketchWidth / 2; - var halfHeight = sketchHeight / 2; - goog.vec.Mat4.makeIdentity(sketchTransform); - goog.vec.Mat4.translate(sketchTransform, - halfWidth, - halfHeight, - 0); - goog.vec.Mat4.scale(sketchTransform, - 1 / tileResolution, - -1 / tileResolution, - 1); - goog.vec.Mat4.translate(sketchTransform, - -(sketchOrigin[0] + halfWidth * tileResolution), - -(sketchOrigin[1] - halfHeight * tileResolution), - 0); - - // clear/resize sketch canvas - sketchCanvas.width = sketchWidth; - sketchCanvas.height = sketchHeight; - - var sketchContext = /** @type {CanvasRenderingContext2D} */ - (sketchCanvas.getContext('2d')); - var sketchCanvasRenderer = new ol.renderer.canvas.Vector( - sketchContext, sketchTransform, this.requestMapRenderFrame_); - - // clear/resize final canvas - var finalCanvas = this.canvas_; - finalCanvas.width = sketchWidth; - finalCanvas.height = sketchHeight; - var finalContext = this.context_; - - var featuresToRender = {}; - var tilesToRender = {}; - var tilesOnSketchCanvas = {}; - // TODO make gutter configurable? - var tileGutter = 15 * tileResolution; - var tile, tileCoord, key, x, y, i, type; - var deferred = false; - var dirty = false; - var tileExtent, groups, group, j, numGroups, featuresObject, tileHasFeatures; - fetchTileData: - for (x = tileRange.minX; x <= tileRange.maxX; ++x) { - for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - tileCoord = new ol.TileCoord(0, x, y); - key = tileCoord.toString(); - if (this.tileCache_.containsKey(key)) { - tilesToRender[key] = tileCoord; - } else if (idle) { - tileExtent = tileGrid.getTileCoordExtent(tileCoord); - tileExtent[0] -= tileGutter; - tileExtent[2] += tileGutter; - tileExtent[1] -= tileGutter; - tileExtent[3] += tileGutter; - tileHasFeatures = false; - featuresObject = layer.getFeaturesObjectForExtent(tileExtent, - projection, this.requestMapRenderFrame_); - if (goog.isNull(featuresObject)) { - deferred = true; - break fetchTileData; - } - tileHasFeatures = tileHasFeatures || - !goog.object.isEmpty(featuresObject); - goog.object.extend(featuresToRender, featuresObject); - if (tileHasFeatures) { - tilesOnSketchCanvas[key] = tileCoord; - } - } else { - dirty = true; - } - } - } - this.dirty_ = dirty; - - groups = layer.groupFeaturesBySymbolizerLiteral(featuresToRender, - tileResolution); - numGroups = groups.length; - for (j = 0; j < numGroups; ++j) { - group = groups[j]; - deferred = sketchCanvasRenderer.renderFeatures(group[0], group[1], - group[2]); - if (deferred) { - break; - } - } - - if (!deferred) { - goog.object.extend(tilesToRender, tilesOnSketchCanvas); - } - - var symbolSizes = sketchCanvasRenderer.getSymbolSizes(), - maxSymbolSize = sketchCanvasRenderer.getMaxSymbolSize(), - symbolOffsets = sketchCanvasRenderer.getSymbolOffsets(); - - // keep track of maximum pixel size for symbols rendered on this layer - this.maxSymbolPixelDim_ = Math.max(maxSymbolSize[0] / tileResolution, - maxSymbolSize[1] / tileResolution); - - for (key in tilesToRender) { - tileCoord = tilesToRender[key]; - if (this.tileCache_.containsKey(key)) { - tile = /** @type {HTMLCanvasElement} */ (this.tileCache_.get(key)[0]); - } else { - tile = /** @type {HTMLCanvasElement} */ - (this.tileArchetype_.cloneNode(false)); - tile.getContext('2d').drawImage(sketchCanvas, - (tileRange.minX - tileCoord.x) * tileSize[0], - (tileCoord.y - tileRange.maxY) * tileSize[1]); - // TODO: Create an ol.VectorTile subclass of ol.Tile - this.tileCache_.set(key, - [tile, symbolSizes, maxSymbolSize, symbolOffsets]); - } - finalContext.drawImage(tile, - tileSize[0] * (tileCoord.x - tileRange.minX), - tileSize[1] * (tileRange.maxY - tileCoord.y)); - } - - this.renderedResolution_ = tileResolution; - this.renderedExtent_ = tileRangeExtent; - if (!this.pendingCachePrune_) { - this.pendingCachePrune_ = true; - frameState.postRenderFunctions.push(goog.bind(this.pruneTileCache_, this)); - } - -}; - - -/** - * Get rid of tiles that exceed the cache capacity. - * TODO: add a method to the cache to handle this - * @private - */ -ol.renderer.canvas.VectorLayer.prototype.pruneTileCache_ = function() { - while (this.tileCache_.canExpireCache()) { - this.tileCache_.pop(); - } - this.pendingCachePrune_ = false; -}; - - -/** - * @type {number} - */ -ol.renderer.canvas.VectorLayer.TILECACHE_SIZE = 128; diff --git a/old/src/ol/renderer/canvas/canvasvectorrenderer.js b/old/src/ol/renderer/canvas/canvasvectorrenderer.js deleted file mode 100644 index b3573766a6..0000000000 --- a/old/src/ol/renderer/canvas/canvasvectorrenderer.js +++ /dev/null @@ -1,646 +0,0 @@ -goog.provide('ol.renderer.canvas.Vector'); - - -goog.require('goog.asserts'); -goog.require('goog.dom'); -goog.require('goog.dom.TagName'); -goog.require('goog.events'); -goog.require('goog.events.EventType'); -goog.require('goog.vec.Mat4'); -goog.require('ol.Feature'); -goog.require('ol.FeatureRenderIntent'); -goog.require('ol.geom.AbstractCollection'); -goog.require('ol.geom.Geometry'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.LineString'); -goog.require('ol.geom.MultiLineString'); -goog.require('ol.geom.MultiPoint'); -goog.require('ol.geom.MultiPolygon'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.Polygon'); -goog.require('ol.style.IconLiteral'); -goog.require('ol.style.LineLiteral'); -goog.require('ol.style.Literal'); -goog.require('ol.style.PointLiteral'); -goog.require('ol.style.PolygonLiteral'); -goog.require('ol.style.ShapeLiteral'); -goog.require('ol.style.ShapeType'); -goog.require('ol.style.TextLiteral'); - - - -/** - * @constructor - * @param {CanvasRenderingContext2D} context Context. - * @param {goog.vec.Mat4.Number} transform Transform. - * @param {function()=} opt_iconLoadedCallback Callback for deferred rendering - * when images need to be loaded before rendering. - */ -ol.renderer.canvas.Vector = - function(context, transform, opt_iconLoadedCallback) { - - /** - * @type {goog.vec.Mat4.Number} - * @private - */ - this.transform_ = transform; - - var vec = [1, 0, 0]; - goog.vec.Mat4.multVec3NoTranslate(transform, vec, vec); - - /** - * @type {number} - * @private - */ - this.inverseScale_ = 1 / Math.sqrt(vec[0] * vec[0] + vec[1] * vec[1]); - - /** - * @type {CanvasRenderingContext2D} - * @private - */ - this.context_ = context; - - /** - * @type {function()|undefined} - * @private - */ - this.iconLoadedCallback_ = opt_iconLoadedCallback; - - /** - * @type {Object.>} - * @private - */ - this.symbolSizes_ = {}; - - /** - * @type {Object.>} - * @private - */ - this.symbolOffsets_ = {}; - - /** - * @type {Array.} - * @private - */ - this.maxSymbolSize_ = [0, 0]; - -}; - - -/** - * @return {Object.>} Symbolizer sizes. - */ -ol.renderer.canvas.Vector.prototype.getSymbolSizes = function() { - return this.symbolSizes_; -}; - - -/** - * @return {Object.>} Symbolizer offsets. - */ -ol.renderer.canvas.Vector.prototype.getSymbolOffsets = function() { - return this.symbolOffsets_; -}; - - -/** - * @return {Array.} Maximum symbolizer size. - */ -ol.renderer.canvas.Vector.prototype.getMaxSymbolSize = function() { - return this.maxSymbolSize_; -}; - - -/** - * @param {Array.} features Array of features. - * @param {ol.style.Literal} symbolizer Symbolizer. - * @param {Array} data Additional data. - * @return {boolean} true if deferred, false if rendered. - */ -ol.renderer.canvas.Vector.prototype.renderFeatures = - function(features, symbolizer, data) { - var deferred = false; - if (symbolizer instanceof ol.style.PointLiteral) { - deferred = this.renderPointFeatures_(features, symbolizer); - } else if (symbolizer instanceof ol.style.LineLiteral) { - this.renderLineStringFeatures_(features, symbolizer); - } else if (symbolizer instanceof ol.style.PolygonLiteral) { - this.renderPolygonFeatures_(features, symbolizer); - } else if (symbolizer instanceof ol.style.TextLiteral) { - this.renderText_(features, symbolizer, data); - } - return deferred; -}; - - -/** - * @param {Array.} features Array of line features. - * @param {ol.style.LineLiteral} symbolizer Line symbolizer. - * @private - */ -ol.renderer.canvas.Vector.prototype.renderLineStringFeatures_ = - function(features, symbolizer) { - - var context = this.context_, - i, ii, feature, id, currentSize, geometry, components, j, jj, - coordinates, coordinate, k, kk, strokeSize; - - var vec = [NaN, NaN, 0]; - var pixel = [NaN, NaN]; - var lastPixel = [NaN, NaN]; - - context.globalAlpha = symbolizer.opacity; - context.strokeStyle = symbolizer.color; - context.lineWidth = symbolizer.width; - context.lineCap = 'round'; // TODO: accept this as a symbolizer property - context.lineJoin = 'round'; // TODO: accept this as a symbolizer property - strokeSize = context.lineWidth * this.inverseScale_; - context.beginPath(); - for (i = 0, ii = features.length; i < ii; ++i) { - feature = features[i]; - if (feature.getRenderIntent() == ol.FeatureRenderIntent.HIDDEN) { - continue; - } - id = goog.getUid(feature); - currentSize = goog.isDef(this.symbolSizes_[id]) ? - this.symbolSizes_[id] : [0]; - currentSize[0] = Math.max(currentSize[0], strokeSize); - this.symbolSizes_[id] = currentSize; - this.maxSymbolSize_ = [Math.max(currentSize[0], this.maxSymbolSize_[0]), - Math.max(currentSize[0], this.maxSymbolSize_[1])]; - geometry = feature.getGeometry(); - if (geometry instanceof ol.geom.LineString) { - components = [geometry]; - } else { - goog.asserts.assert(geometry instanceof ol.geom.MultiLineString, - 'Expected MultiLineString'); - components = geometry.getComponents(); - } - for (j = 0, jj = components.length; j < jj; ++j) { - coordinates = components[j].getCoordinates(); - for (k = 0, kk = coordinates.length; k < kk; ++k) { - coordinate = coordinates[k]; - vec[0] = coordinate[0]; - vec[1] = coordinate[1]; - goog.vec.Mat4.multVec3(this.transform_, vec, vec); - if (k === 0) { - lastPixel[0] = NaN; - lastPixel[1] = NaN; - context.moveTo(vec[0], vec[1]); - } else { - pixel[0] = Math.round(vec[0]); - pixel[1] = Math.round(vec[1]); - if (pixel[0] !== lastPixel[0] || pixel[1] !== lastPixel[1]) { - context.lineTo(vec[0], vec[1]); - lastPixel[0] = pixel[0]; - lastPixel[1] = pixel[1]; - } - } - } - } - } - - context.stroke(); -}; - - -/** - * @param {Array.} features Array of point features. - * @param {ol.style.PointLiteral} symbolizer Point symbolizer. - * @return {boolean} true if deferred, false if rendered. - * @private - */ -ol.renderer.canvas.Vector.prototype.renderPointFeatures_ = - function(features, symbolizer) { - - var context = this.context_, - content, alpha, i, ii, feature, id, size, geometry, components, j, jj, - point, vec; - - var xOffset = 0; - var yOffset = 0; - if (symbolizer instanceof ol.style.ShapeLiteral) { - content = ol.renderer.canvas.Vector.renderShape(symbolizer); - alpha = 1; - } else if (symbolizer instanceof ol.style.IconLiteral) { - content = ol.renderer.canvas.Vector.renderIcon( - symbolizer, this.iconLoadedCallback_); - alpha = symbolizer.opacity; - xOffset = symbolizer.xOffset; - yOffset = symbolizer.yOffset; - } else { - throw new Error('Unsupported symbolizer: ' + symbolizer); - } - - if (goog.isNull(content)) { - return true; - } - - var midWidth = Math.floor(content.width / 2); - var midHeight = Math.floor(content.height / 2); - var contentWidth = content.width * this.inverseScale_; - var contentHeight = content.height * this.inverseScale_; - var contentXOffset = xOffset * this.inverseScale_; - var contentYOffset = yOffset * this.inverseScale_; - context.save(); - context.setTransform(1, 0, 0, 1, -midWidth, -midHeight); - context.globalAlpha = alpha; - for (i = 0, ii = features.length; i < ii; ++i) { - feature = features[i]; - if (feature.getRenderIntent() == ol.FeatureRenderIntent.HIDDEN) { - continue; - } - id = goog.getUid(feature); - size = this.symbolSizes_[id]; - this.symbolSizes_[id] = goog.isDef(size) ? - [Math.max(size[0], contentWidth), Math.max(size[1], contentHeight)] : - [contentWidth, contentHeight]; - this.symbolOffsets_[id] = - [xOffset * this.inverseScale_, yOffset * this.inverseScale_]; - this.maxSymbolSize_ = - [Math.max(this.maxSymbolSize_[0], - this.symbolSizes_[id][0] + 2 * Math.abs(contentXOffset)), - Math.max(this.maxSymbolSize_[1], - this.symbolSizes_[id][1] + 2 * Math.abs(contentYOffset))]; - geometry = feature.getGeometry(); - if (geometry instanceof ol.geom.Point) { - components = [geometry]; - } else { - goog.asserts.assert(geometry instanceof ol.geom.MultiPoint, - 'Expected MultiPoint'); - components = geometry.getComponents(); - } - for (j = 0, jj = components.length; j < jj; ++j) { - point = components[j]; - goog.asserts.assertInstanceof(point, ol.geom.Point); - vec = [point.get(0), point.get(1), 0]; - goog.vec.Mat4.multVec3(this.transform_, vec, vec); - context.drawImage(content, Math.round(vec[0] + xOffset), - Math.round(vec[1] + yOffset), - content.width, content.height); - } - } - context.restore(); - - return false; -}; - - -/** - * @param {Array.} features Array of features. - * @param {ol.style.TextLiteral} text Text symbolizer. - * @param {Array} texts Label text for each feature. - * @private - */ -ol.renderer.canvas.Vector.prototype.renderText_ = - function(features, text, texts) { - var context = this.context_, - feature, vecs, vec; - - if (context.fillStyle !== text.color) { - context.fillStyle = text.color; - } - - // font shorthand values must be given in the correct order - // see http://www.w3.org/TR/CSS21/fonts.html#font-shorthand - context.font = text.fontWeight + ' ' + - text.fontSize + 'px ' + - text.fontFamily; - - context.globalAlpha = text.opacity; - - // TODO: make alignments configurable - context.textAlign = 'center'; - context.textBaseline = 'middle'; - - var stroke = false; - if (goog.isDef(text.strokeColor)) { - stroke = true; - goog.asserts.assertString(text.strokeColor); - context.strokeStyle = text.strokeColor; - goog.asserts.assertNumber(text.strokeWidth); - context.lineWidth = text.strokeWidth; - } - - for (var i = 0, ii = features.length; i < ii; ++i) { - feature = features[i]; - if (feature.getRenderIntent() == ol.FeatureRenderIntent.HIDDEN) { - continue; - } - vecs = ol.renderer.canvas.Vector.getLabelVectors( - feature.getGeometry()); - for (var j = 0, jj = vecs.length; j < jj; ++j) { - vec = vecs[j]; - goog.vec.Mat4.multVec3(this.transform_, vec, vec); - if (stroke) { - if (text.strokeOpacity !== text.opacity) { - goog.asserts.assertNumber(text.strokeOpacity); - context.globalAlpha = text.strokeOpacity; - } - context.strokeText(texts[i], vec[0], vec[1]); - if (text.strokeOpacity !== text.opacity) { - context.globalAlpha = text.opacity; - } - } - context.fillText(texts[i], vec[0], vec[1]); - } - } - -}; - - -/** - * @param {Array.} features Array of polygon features. - * @param {ol.style.PolygonLiteral} symbolizer Polygon symbolizer. - * @private - */ -ol.renderer.canvas.Vector.prototype.renderPolygonFeatures_ = - function(features, symbolizer) { - var context = this.context_, - strokeColor = symbolizer.strokeColor, - strokeWidth = symbolizer.strokeWidth, - strokeOpacity = symbolizer.strokeOpacity, - fillColor = symbolizer.fillColor, - fillOpacity = symbolizer.fillOpacity, - globalAlpha, - i, ii, geometry, components, j, jj, poly, - rings, numRings, coordinates, coordinate, k, kk, feature; - - var vec = [NaN, NaN, 0]; - var pixel = [NaN, NaN]; - var lastPixel = [NaN, NaN]; - - if (strokeColor) { - context.strokeStyle = strokeColor; - if (strokeWidth) { - context.lineWidth = strokeWidth; - } - context.lineCap = 'round'; // TODO: accept this as a symbolizer property - context.lineJoin = 'round'; // TODO: accept this as a symbolizer property - } - if (fillColor) { - context.fillStyle = fillColor; - } - - /** - * Four scenarios covered here: - * 1) stroke only, no holes - only need to have a single path - * 2) fill only, no holes - only need to have a single path - * 3) fill and stroke, no holes - * 4) holes - render polygon to sketch canvas first - */ - context.beginPath(); - for (i = 0, ii = features.length; i < ii; ++i) { - feature = features[i]; - if (feature.getRenderIntent() == ol.FeatureRenderIntent.HIDDEN) { - continue; - } - geometry = feature.getGeometry(); - if (geometry instanceof ol.geom.Polygon) { - components = [geometry]; - } else { - goog.asserts.assert(geometry instanceof ol.geom.MultiPolygon, - 'Expected MultiPolygon'); - components = geometry.getComponents(); - } - for (j = 0, jj = components.length; j < jj; ++j) { - poly = components[j]; - goog.asserts.assertInstanceof(poly, ol.geom.Polygon); - rings = poly.getRings(); - numRings = rings.length; - if (numRings > 0) { - // TODO: scenario 4 - coordinates = rings[0].getCoordinates(); - for (k = 0, kk = coordinates.length; k < kk; ++k) { - coordinate = coordinates[k]; - vec[0] = coordinate[0]; - vec[1] = coordinate[1]; - goog.vec.Mat4.multVec3(this.transform_, vec, vec); - if (k === 0) { - lastPixel[0] = NaN; - lastPixel[1] = NaN; - context.moveTo(vec[0], vec[1]); - } else { - pixel[0] = Math.round(vec[0]); - pixel[1] = Math.round(vec[1]); - if (pixel[0] !== lastPixel[0] || pixel[1] !== lastPixel[1]) { - context.lineTo(vec[0], vec[1]); - lastPixel[0] = pixel[0]; - lastPixel[1] = pixel[1]; - } - } - } - if (fillColor && strokeColor) { - // scenario 3 - fill and stroke each time - if (fillOpacity !== globalAlpha) { - goog.asserts.assertNumber(fillOpacity); - context.globalAlpha = fillOpacity; - globalAlpha = fillOpacity; - } - context.fill(); - if (strokeOpacity !== globalAlpha) { - goog.asserts.assertNumber(strokeOpacity); - context.globalAlpha = strokeOpacity; - globalAlpha = strokeOpacity; - } - context.stroke(); - if (i < ii - 1 || j < jj - 1) { - context.beginPath(); - } - } - } - } - } - if (!(fillColor && strokeColor)) { - if (fillColor) { - // scenario 2 - fill all at once - if (fillOpacity !== globalAlpha) { - goog.asserts.assertNumber(fillOpacity); - context.globalAlpha = fillOpacity; - globalAlpha = fillOpacity; - } - context.fill(); - } else { - // scenario 1 - stroke all at once - if (strokeOpacity !== globalAlpha) { - goog.asserts.assertNumber(strokeOpacity); - context.globalAlpha = strokeOpacity; - globalAlpha = strokeOpacity; - } - context.stroke(); - } - } -}; - - -/** - * @param {ol.style.ShapeLiteral} circle Shape symbolizer. - * @return {!HTMLCanvasElement} Canvas element. - * @private - */ -ol.renderer.canvas.Vector.renderCircle_ = function(circle) { - var strokeWidth = circle.strokeWidth || 0, - size = circle.size + (2 * strokeWidth) + 1, - mid = size / 2, - canvas = /** @type {HTMLCanvasElement} */ - (goog.dom.createElement(goog.dom.TagName.CANVAS)), - context = /** @type {CanvasRenderingContext2D} */ - (canvas.getContext('2d')), - fillColor = circle.fillColor, - strokeColor = circle.strokeColor, - twoPi = Math.PI * 2; - - canvas.height = size; - canvas.width = size; - - if (fillColor) { - context.fillStyle = fillColor; - } - if (strokeColor) { - context.lineWidth = strokeWidth; - context.strokeStyle = strokeColor; - context.lineCap = 'round'; // TODO: accept this as a symbolizer property - context.lineJoin = 'round'; // TODO: accept this as a symbolizer property - } - - context.beginPath(); - context.arc(mid, mid, circle.size / 2, 0, twoPi, true); - - if (fillColor) { - goog.asserts.assertNumber(circle.fillOpacity); - context.globalAlpha = circle.fillOpacity; - context.fill(); - } - if (strokeColor) { - goog.asserts.assertNumber(circle.strokeOpacity); - context.globalAlpha = circle.strokeOpacity; - context.stroke(); - } - return canvas; -}; - - -/** - * @param {ol.geom.Geometry} geometry Geometry. - * @return {Array.} Renderable geometry vectors. - */ -ol.renderer.canvas.Vector.getLabelVectors = function(geometry) { - if (geometry instanceof ol.geom.AbstractCollection) { - var components = geometry.getComponents(); - var numComponents = components.length; - var result = []; - for (var i = 0; i < numComponents; ++i) { - result.push.apply(result, - ol.renderer.canvas.Vector.getLabelVectors(components[i])); - } - return result; - } - var type = geometry.getType(); - if (type == ol.geom.GeometryType.POINT) { - goog.asserts.assertInstanceof(geometry, ol.geom.Point); - return [[geometry.get(0), geometry.get(1), 0]]; - } - if (type == ol.geom.GeometryType.POLYGON) { - goog.asserts.assertInstanceof(geometry, ol.geom.Polygon); - var coordinates = geometry.getInteriorPoint(); - return [[coordinates[0], coordinates[1], 0]]; - } - throw new Error('Label rendering not implemented for geometry type: ' + - type); -}; - - -/** - * @param {ol.style.ShapeLiteral} shape Shape symbolizer. - * @return {!HTMLCanvasElement} Canvas element. - */ -ol.renderer.canvas.Vector.renderShape = function(shape) { - var canvas; - if (shape.type === ol.style.ShapeType.CIRCLE) { - canvas = ol.renderer.canvas.Vector.renderCircle_(shape); - } else { - throw new Error('Unsupported shape type: ' + shape); - } - return canvas; -}; - - -/** - * @param {ol.style.IconLiteral} icon Icon literal. - * @param {function()=} opt_callback Callback which will be called when - * the icon is loaded and rendering will work without deferring. - * @return {HTMLImageElement} image element of null if deferred. - */ -ol.renderer.canvas.Vector.renderIcon = function(icon, opt_callback) { - var url = icon.url; - var image = ol.renderer.canvas.Vector.icons_[url]; - var deferred = false; - if (!goog.isDef(image)) { - deferred = true; - image = /** @type {HTMLImageElement} */ - (goog.dom.createElement(goog.dom.TagName.IMG)); - goog.events.listenOnce(image, goog.events.EventType.ERROR, - goog.bind(ol.renderer.canvas.Vector.handleIconError_, null, - opt_callback), - false, ol.renderer.canvas.Vector.renderIcon); - goog.events.listenOnce(image, goog.events.EventType.LOAD, - goog.bind(ol.renderer.canvas.Vector.handleIconLoad_, null, - opt_callback), - false, ol.renderer.canvas.Vector.renderIcon); - image.setAttribute('src', url); - } else if (!goog.isNull(image)) { - var width = icon.width, - height = icon.height; - if (goog.isDef(width) && goog.isDef(height)) { - image.width = width; - image.height = height; - } else if (goog.isDef(width)) { - image.height = width / image.width * image.height; - image.width = width; - } else if (goog.isDef(height)) { - image.width = height / image.height * image.width; - image.height = height; - } - } - return deferred ? null : image; -}; - - -/** - * @type {Object.} - * @private - */ -ol.renderer.canvas.Vector.icons_ = {}; - - -/** - * @param {function()=} opt_callback Callback. - * @param {Event=} opt_event Event. - * @private - */ -ol.renderer.canvas.Vector.handleIconError_ = - function(opt_callback, opt_event) { - if (goog.isDef(opt_event)) { - var url = opt_event.target.getAttribute('src'); - ol.renderer.canvas.Vector.icons_[url] = null; - ol.renderer.canvas.Vector.handleIconLoad_(opt_callback, opt_event); - } -}; - - -/** - * @param {function()=} opt_callback Callback. - * @param {Event=} opt_event Event. - * @private - */ -ol.renderer.canvas.Vector.handleIconLoad_ = - function(opt_callback, opt_event) { - if (goog.isDef(opt_event)) { - var url = opt_event.target.getAttribute('src'); - ol.renderer.canvas.Vector.icons_[url] = - /** @type {HTMLImageElement} */ (opt_event.target); - } - if (goog.isDef(opt_callback)) { - opt_callback(); - } -};