diff --git a/externs/olx.js b/externs/olx.js index b009fdaaee..190ba3fb45 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -6840,6 +6840,14 @@ olx.style.RegularShapeOptions.prototype.snapToPixel; olx.style.RegularShapeOptions.prototype.stroke; +/** + * Whether to rotate the text with the view. Default is `false`. + * @type {boolean|undefined} + * @api + */ +olx.style.TextOptions.prototype.rotateWithView; + + /** * Rotation in radians (positive rotation clockwise). Default is `0`. * @type {number|undefined} @@ -6937,6 +6945,7 @@ olx.style.StrokeOptions.prototype.width; * offsetX: (number|undefined), * offsetY: (number|undefined), * scale: (number|undefined), + * rotateWithView: (boolean|undefined), * rotation: (number|undefined), * text: (string|undefined), * textAlign: (string|undefined), diff --git a/src/ol/render/canvas/immediate.js b/src/ol/render/canvas/immediate.js index bd254e271c..ddcc17418a 100644 --- a/src/ol/render/canvas/immediate.js +++ b/src/ol/render/canvas/immediate.js @@ -189,6 +189,12 @@ ol.render.canvas.Immediate = function(context, pixelRatio, extent, transform, vi */ this.textOffsetY_ = 0; + /** + * @private + * @type {boolean} + */ + this.textRotateWithView_ = false; + /** * @private * @type {number} @@ -318,14 +324,18 @@ ol.render.canvas.Immediate.prototype.drawText_ = function(flatCoordinates, offse flatCoordinates, offset, end, stride, this.transform_, this.pixelCoordinates_); var context = this.context_; + var rotation = this.textRotation_; + if (this.textRotateWithView_) { + rotation += this.viewRotation_; + } for (; offset < end; offset += stride) { var x = pixelCoordinates[offset] + this.textOffsetX_; var y = pixelCoordinates[offset + 1] + this.textOffsetY_; - if (this.textRotation_ !== 0 || this.textScale_ != 1) { + if (rotation !== 0 || this.textScale_ != 1) { var localTransform = ol.transform.compose(this.tmpLocalTransform_, x, y, this.textScale_, this.textScale_, - this.textRotation_, + rotation, -x, -y); context.setTransform.apply(context, localTransform); } @@ -336,7 +346,7 @@ ol.render.canvas.Immediate.prototype.drawText_ = function(flatCoordinates, offse context.fillText(this.text_, x, y); } } - if (this.textRotation_ !== 0 || this.textScale_ != 1) { + if (rotation !== 0 || this.textScale_ != 1) { context.setTransform(1, 0, 0, 1, 0, 0); } }; @@ -918,6 +928,7 @@ ol.render.canvas.Immediate.prototype.setTextStyle = function(textStyle) { var textFont = textStyle.getFont(); var textOffsetX = textStyle.getOffsetX(); var textOffsetY = textStyle.getOffsetY(); + var textRotateWithView = textStyle.getRotateWithView(); var textRotation = textStyle.getRotation(); var textScale = textStyle.getScale(); var textText = textStyle.getText(); @@ -936,6 +947,7 @@ ol.render.canvas.Immediate.prototype.setTextStyle = function(textStyle) { textOffsetX !== undefined ? (this.pixelRatio_ * textOffsetX) : 0; this.textOffsetY_ = textOffsetY !== undefined ? (this.pixelRatio_ * textOffsetY) : 0; + this.textRotateWithView_ = textRotateWithView !== undefined ? textRotateWithView : false; this.textRotation_ = textRotation !== undefined ? textRotation : 0; this.textScale_ = this.pixelRatio_ * (textScale !== undefined ? textScale : 1); diff --git a/src/ol/render/canvas/replay.js b/src/ol/render/canvas/replay.js index 1de3481ca2..9b3e14440a 100644 --- a/src/ol/render/canvas/replay.js +++ b/src/ol/render/canvas/replay.js @@ -384,6 +384,10 @@ ol.render.canvas.Replay.prototype.replay_ = function( goog.DEBUG && console.assert(typeof instruction[9] === 'boolean', '10th instruction should be a boolean'); stroke = /** @type {boolean} */ (instruction[9]); + rotateWithView = /** @type {boolean} */ (instruction[10]); + if (rotateWithView) { + rotation += viewRotation; + } for (; d < dd; d += 2) { x = pixelCoordinates[d] + offsetX; y = pixelCoordinates[d + 1] + offsetY; @@ -1545,6 +1549,12 @@ ol.render.canvas.TextReplay = function(tolerance, maxExtent, resolution) { */ this.textOffsetY_ = 0; + /** + * @private + * @type {boolean|undefined} + */ + this.textRotateWithView_ = undefined; + /** * @private * @type {number} @@ -1603,7 +1613,7 @@ ol.render.canvas.TextReplay.prototype.drawText = function(flatCoordinates, offse var drawTextInstruction = [ ol.render.canvas.Instruction.DRAW_TEXT, myBegin, myEnd, this.text_, this.textOffsetX_, this.textOffsetY_, this.textRotation_, this.textScale_, - fill, stroke]; + fill, stroke, this.textRotateWithView_]; this.instructions.push(drawTextInstruction); this.hitDetectionInstructions.push(drawTextInstruction); this.endGeometry(geometry, feature); @@ -1773,6 +1783,7 @@ ol.render.canvas.TextReplay.prototype.setTextStyle = function(textStyle) { var textFont = textStyle.getFont(); var textOffsetX = textStyle.getOffsetX(); var textOffsetY = textStyle.getOffsetY(); + var textRotateWithView = textStyle.getRotateWithView(); var textRotation = textStyle.getRotation(); var textScale = textStyle.getScale(); var textText = textStyle.getText(); @@ -1799,6 +1810,7 @@ ol.render.canvas.TextReplay.prototype.setTextStyle = function(textStyle) { this.text_ = textText !== undefined ? textText : ''; this.textOffsetX_ = textOffsetX !== undefined ? textOffsetX : 0; this.textOffsetY_ = textOffsetY !== undefined ? textOffsetY : 0; + this.textRotateWithView_ = textRotateWithView !== undefined ? textRotateWithView : false; this.textRotation_ = textRotation !== undefined ? textRotation : 0; this.textScale_ = textScale !== undefined ? textScale : 1; } diff --git a/src/ol/style/text.js b/src/ol/style/text.js index 4f62e83b69..8a8e31106c 100644 --- a/src/ol/style/text.js +++ b/src/ol/style/text.js @@ -28,6 +28,12 @@ ol.style.Text = function(opt_options) { */ this.rotation_ = options.rotation; + /** + * @private + * @type {boolean|undefined} + */ + this.rotateWithView_ = options.rotateWithView; + /** * @private * @type {number|undefined} @@ -129,6 +135,16 @@ ol.style.Text.prototype.getFill = function() { }; +/** + * Determine whether the text rotates with the map. + * @return {boolean|undefined} Rotate with map. + * @api + */ +ol.style.Text.prototype.getRotateWithView = function() { + return this.rotateWithView_; +}; + + /** * Get the text rotation. * @return {number|undefined} Rotation. diff --git a/test_rendering/spec/ol/style/expected/text-canvas.png b/test_rendering/spec/ol/style/expected/text-canvas.png new file mode 100644 index 0000000000..b2e9b26d45 Binary files /dev/null and b/test_rendering/spec/ol/style/expected/text-canvas.png differ diff --git a/test_rendering/spec/ol/style/expected/text-rotated-canvas.png b/test_rendering/spec/ol/style/expected/text-rotated-canvas.png new file mode 100644 index 0000000000..d84071c208 Binary files /dev/null and b/test_rendering/spec/ol/style/expected/text-rotated-canvas.png differ diff --git a/test_rendering/spec/ol/style/text.test.js b/test_rendering/spec/ol/style/text.test.js new file mode 100644 index 0000000000..ec162b955c --- /dev/null +++ b/test_rendering/spec/ol/style/text.test.js @@ -0,0 +1,106 @@ +goog.provide('ol.test.rendering.style.Text'); + +goog.require('ol.Feature'); +goog.require('ol.geom.Point'); +goog.require('ol.Map'); +goog.require('ol.View'); +goog.require('ol.layer.Vector'); +goog.require('ol.source.Vector'); +goog.require('ol.style.Text'); +goog.require('ol.style.Fill'); +goog.require('ol.style.Style'); +goog.require('ol.style.Stroke'); + +describe('ol.rendering.style.Text', function() { + + var target, map, vectorSource; + + function createMap(renderer) { + target = createMapDiv(200, 200); + + vectorSource = new ol.source.Vector(); + var vectorLayer = new ol.layer.Vector({ + source: vectorSource + }); + + map = new ol.Map({ + target: target, + renderer: renderer, + layers: [vectorLayer], + view: new ol.View({ + projection: 'EPSG:4326', + center: [0, 0], + resolution: 1 + }) + }); + return map; + } + + describe('#render', function() { + afterEach(function() { + disposeMap(map); + }); + + function createFeatures() { + var feature; + feature = new ol.Feature({ + geometry: new ol.geom.Point([-20, 18]) + }); + feature.setStyle(new ol.style.Style({ + text: new ol.style.Text({ + text: 'hello', + font: '10px' + }) + })); + vectorSource.addFeature(feature); + + feature = new ol.Feature({ + geometry: new ol.geom.Point([-10, 0]) + }); + feature.setStyle(new ol.style.Style({ + text: new ol.style.Text({ + text: 'hello', + fill: new ol.style.Fill({ + color: 'red', + font: '12px' + }), + stroke: new ol.style.Stroke({ + color: '#000', + width: 3 + }) + }) + })); + vectorSource.addFeature(feature); + + feature = new ol.Feature({ + geometry: new ol.geom.Point([20, 10]) + }); + feature.setStyle(new ol.style.Style({ + text: new ol.style.Text({ + rotateWithView: true, + text: 'hello', + font: '10px', + stroke: new ol.style.Stroke({ + color: [10, 10, 10, 0.5] + }) + }) + })); + vectorSource.addFeature(feature); + + } + + it('tests the canvas renderer without rotation', function(done) { + map = createMap('canvas'); + createFeatures(); + expectResemble(map, 'spec/ol/style/expected/text-canvas.png', IMAGE_TOLERANCE, done); + }); + + it('tests the canvas renderer with rotation', function(done) { + map = createMap('canvas'); + createFeatures(); + map.getView().setRotation(Math.PI / 7); + expectResemble(map, 'spec/ol/style/expected/text-rotated-canvas.png', IMAGE_TOLERANCE, done); + }); + + }); +});