From d6e28e5ab94fb44a51438ae325813ee89f87fbc3 Mon Sep 17 00:00:00 2001 From: Arjen Kopinga Date: Wed, 22 Feb 2012 15:26:21 +0100 Subject: [PATCH 1/5] Added support for named graphics to Canvas renderer --- lib/OpenLayers/Renderer/Canvas.js | 153 ++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/lib/OpenLayers/Renderer/Canvas.js b/lib/OpenLayers/Renderer/Canvas.js index daf9e19e35..da82e1d7f9 100644 --- a/lib/OpenLayers/Renderer/Canvas.js +++ b/lib/OpenLayers/Renderer/Canvas.js @@ -51,6 +51,12 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, { */ pendingRedraw: false, + /** + * Property: cachedSymbolBounds + * {Object} Internal cache of calculated symbol extents. + */ + cachedSymbolBounds: {}, + /** * Constructor: OpenLayers.Renderer.Canvas * @@ -283,6 +289,151 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, { img.src = style.externalGraphic; }, + /** + * Method: drawNamedSymbol + * Called to draw Well Known Graphic Symbol Name. + * This method is only called by the renderer itself. + * + * Parameters: + * geometry - {} + * style - {Object} + * featureId - {String} + */ + drawNamedSymbol: function(geometry, style, featureId) { + var x, y, cx, cy, i, symbolBounds, scaling, angle; + var unscaledStrokeWidth; + var deg2rad = Math.PI / 180.0; + + var symbol = OpenLayers.Renderer.symbol[style.graphicName]; + + if (!symbol) { + throw new Error(style.graphicName + ' is not a valid symbol name'); + } + + if (!symbol.length || symbol.length < 2) return; + + var pt = this.getLocalXY(geometry); + var p0 = pt[0]; + var p1 = pt[1]; + + if (isNaN(p0) || isNaN(p1)) return; + + // Use rounded line caps + this.canvas.lineCap = "round"; + this.canvas.lineJoin = "round"; + + // Scale and rotate symbols, using precalculated bounds whenever possible. + if (style.graphicName in this.cachedSymbolBounds) { + symbolBounds = this.cachedSymbolBounds[style.graphicName]; + } else { + symbolBounds = new OpenLayers.Bounds(); + for(i = 0; i < symbol.length; i+=2) { + symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i+1])); + } + this.cachedSymbolBounds[style.graphicName] = symbolBounds; + } + + // Push symbol scaling, translation and rotation onto the transformation stack in reverse order. + // Don't forget to apply all canvas transformations to the hitContext canvas as well(!) + this.canvas.save(); + if (this.hitDetection) this.hitContext.save(); + + // Step 3: place symbol at the desired location + this.canvas.translate(p0,p1); + if (this.hitDetection) this.hitContext.translate(p0,p1); + + // Step 2a. rotate the symbol if necessary + angle = deg2rad * style.rotation; // will be NaN when style.rotation is undefined. + if (!isNaN(angle)) { + this.canvas.rotate(angle); + if (this.hitDetection) this.hitContext.rotate(angle); + } + + // // Step 2: scale symbol such that pointRadius equals half the maximum symbol dimension. + scaling = 2.0 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight()); + this.canvas.scale(scaling,scaling); + if (this.hitDetection) this.hitContext.scale(scaling,scaling); + + // Step 1: center the symbol at the origin + cx = symbolBounds.getCenterLonLat().lon; + cy = symbolBounds.getCenterLonLat().lat; + this.canvas.translate(-cx,-cy); + if (this.hitDetection) this.hitContext.translate(-cx,-cy); + + // Don't forget to scale stroke widths, because they are affected by canvas scale transformations as well(!) + // Alternative: scale symbol coordinates manually, so stroke width scaling is not needed anymore. + unscaledStrokeWidth = style.strokeWidth; + style.strokeWidth = unscaledStrokeWidth / scaling; + + if (style.fill !== false) { + this.setCanvasStyle("fill", style); + this.canvas.beginPath(); + for (i=0; i Date: Wed, 22 Feb 2012 15:39:31 +0100 Subject: [PATCH 2/5] Small fix for hit detection on a canvas layer as an illegal feature id might result at a location where the edge of one feature intersects the interior of another. This is due to antialiasing on the hit context canvas which can't be turned off, unfortunately. --- .gitignore | 2 ++ examples/graphic-name.js | 4 ++-- lib/OpenLayers/Renderer/Canvas.js | 11 +++++++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 42b60260f3..0f81cf6d15 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ /doc/apidocs/ /examples/example-list.js /examples/example-list.xml +/examples/graphic-name.js +/examples/graphic-name.html diff --git a/examples/graphic-name.js b/examples/graphic-name.js index 654a4c997d..267ae071dc 100644 --- a/examples/graphic-name.js +++ b/examples/graphic-name.js @@ -34,14 +34,14 @@ function init(){ var styles = new OpenLayers.StyleMap({ "default": { graphicName: "${type}", - pointRadius: 10, + pointRadius: 40, strokeColor: "fuchsia", strokeWidth: 2, fillColor: "lime", fillOpacity: 0.6 }, "select": { - pointRadius: 20, + pointRadius: 50, fillOpacity: 1, rotation: 45 } diff --git a/lib/OpenLayers/Renderer/Canvas.js b/lib/OpenLayers/Renderer/Canvas.js index da82e1d7f9..e180ef7288 100644 --- a/lib/OpenLayers/Renderer/Canvas.js +++ b/lib/OpenLayers/Renderer/Canvas.js @@ -786,7 +786,7 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, { * layer. */ getFeatureIdFromEvent: function(evt) { - var feature; + var featureId, feature; if (this.hitDetection) { // this dragging check should go in the feature handler if (!this.map.dragging) { @@ -797,7 +797,14 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, { if (data[3] === 255) { // antialiased var id = data[2] + (256 * (data[1] + (256 * data[0]))); if (id) { - feature = this.features["OpenLayers.Feature.Vector_" + (id - 1 + this.hitOverflow)][0]; + featureId = "OpenLayers.Feature.Vector_" + (id - 1 + this.hitOverflow); + try { + feature = this.features["OpenLayers.Feature.Vector_" + (id - 1 + this.hitOverflow)][0]; + } catch(err) { + // Because of antialiasing on the canvas, when the hit location is at a point where the edge of + // one symbol intersects the interior of another symbol, a wrong hit color (and therefore id) results. + // todo: set Antialiasing = 'off' on the hitContext as soon as browsers allow it. + } } } } From 65aeaec284f66b76073672f066b6932d666eb709 Mon Sep 17 00:00:00 2001 From: Arjen Kopinga Date: Wed, 22 Feb 2012 15:52:01 +0100 Subject: [PATCH 3/5] returned graphic-name.js to its original state --- .gitignore | 2 -- examples/graphic-name.js | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 0f81cf6d15..42b60260f3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,3 @@ /doc/apidocs/ /examples/example-list.js /examples/example-list.xml -/examples/graphic-name.js -/examples/graphic-name.html diff --git a/examples/graphic-name.js b/examples/graphic-name.js index 267ae071dc..654a4c997d 100644 --- a/examples/graphic-name.js +++ b/examples/graphic-name.js @@ -34,14 +34,14 @@ function init(){ var styles = new OpenLayers.StyleMap({ "default": { graphicName: "${type}", - pointRadius: 40, + pointRadius: 10, strokeColor: "fuchsia", strokeWidth: 2, fillColor: "lime", fillOpacity: 0.6 }, "select": { - pointRadius: 50, + pointRadius: 20, fillOpacity: 1, rotation: 45 } From bb3ae997135d3c3f16c85bd12a5cf1390d4d7995 Mon Sep 17 00:00:00 2001 From: Arjen Kopinga Date: Wed, 22 Feb 2012 15:57:54 +0100 Subject: [PATCH 4/5] forgot to actually used featureId in getFeatureIdFromEvent --- lib/OpenLayers/Renderer/Canvas.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenLayers/Renderer/Canvas.js b/lib/OpenLayers/Renderer/Canvas.js index e180ef7288..eb2b7fcc54 100644 --- a/lib/OpenLayers/Renderer/Canvas.js +++ b/lib/OpenLayers/Renderer/Canvas.js @@ -799,7 +799,7 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, { if (id) { featureId = "OpenLayers.Feature.Vector_" + (id - 1 + this.hitOverflow); try { - feature = this.features["OpenLayers.Feature.Vector_" + (id - 1 + this.hitOverflow)][0]; + feature = this.features[featureId][0]; } catch(err) { // Because of antialiasing on the canvas, when the hit location is at a point where the edge of // one symbol intersects the interior of another symbol, a wrong hit color (and therefore id) results. From 69660e3530b9c04b1b96aa7b86d250ff1f06dc72 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 22 Feb 2012 17:22:30 +0100 Subject: [PATCH 5/5] Minor coding style improvements. --- lib/OpenLayers/Renderer/Canvas.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/OpenLayers/Renderer/Canvas.js b/lib/OpenLayers/Renderer/Canvas.js index eb2b7fcc54..35939afdd2 100644 --- a/lib/OpenLayers/Renderer/Canvas.js +++ b/lib/OpenLayers/Renderer/Canvas.js @@ -336,29 +336,29 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, { // Push symbol scaling, translation and rotation onto the transformation stack in reverse order. // Don't forget to apply all canvas transformations to the hitContext canvas as well(!) this.canvas.save(); - if (this.hitDetection) this.hitContext.save(); + if (this.hitDetection) { this.hitContext.save(); } // Step 3: place symbol at the desired location this.canvas.translate(p0,p1); - if (this.hitDetection) this.hitContext.translate(p0,p1); + if (this.hitDetection) { this.hitContext.translate(p0,p1); } // Step 2a. rotate the symbol if necessary angle = deg2rad * style.rotation; // will be NaN when style.rotation is undefined. if (!isNaN(angle)) { this.canvas.rotate(angle); - if (this.hitDetection) this.hitContext.rotate(angle); + if (this.hitDetection) { this.hitContext.rotate(angle); } } // // Step 2: scale symbol such that pointRadius equals half the maximum symbol dimension. scaling = 2.0 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight()); this.canvas.scale(scaling,scaling); - if (this.hitDetection) this.hitContext.scale(scaling,scaling); + if (this.hitDetection) { this.hitContext.scale(scaling,scaling); } // Step 1: center the symbol at the origin cx = symbolBounds.getCenterLonLat().lon; cy = symbolBounds.getCenterLonLat().lat; this.canvas.translate(-cx,-cy); - if (this.hitDetection) this.hitContext.translate(-cx,-cy); + if (this.hitDetection) { this.hitContext.translate(-cx,-cy); } // Don't forget to scale stroke widths, because they are affected by canvas scale transformations as well(!) // Alternative: scale symbol coordinates manually, so stroke width scaling is not needed anymore. @@ -408,7 +408,7 @@ OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, { style.strokeWidth = unscaledStrokeWidth; this.canvas.restore(); - if (this.hitDetection) this.hitContext.restore(); + if (this.hitDetection) { this.hitContext.restore(); } this.setCanvasStyle("reset"); },