diff --git a/examples/vector-layer.js b/examples/vector-layer.js
index 35b95c69b2..adda211076 100644
--- a/examples/vector-layer.js
+++ b/examples/vector-layer.js
@@ -40,7 +40,7 @@ var map = new ol.Map({
target: 'map',
view: new ol.View2D({
center: [0, 0],
- zoom: 2
+ zoom: 1
})
});
diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc
index 622a98a221..71636f4f7c 100644
--- a/src/objectliterals.jsdoc
+++ b/src/objectliterals.jsdoc
@@ -25,6 +25,9 @@
* @typedef {Object} olx.MapOptions
* @property {ol.Collection|Array.
|undefined} controls
* Controls initially added to the map.
+ * @property {number|undefined} devicePixelRatio The ratio between physical
+ * pixels and device-independent pixels (dips) on the device. If `undefined`
+ * then it gets set by using `window.devicePixelRatio`.
* @property {ol.Collection|Array.|undefined} interactions
* Interactions that are initially added to the map.
* @property {Array.|ol.Collection|undefined} layers Layers.
diff --git a/src/ol/framestate.js b/src/ol/framestate.js
index b86c897ef9..84f3da2c46 100644
--- a/src/ol/framestate.js
+++ b/src/ol/framestate.js
@@ -20,6 +20,7 @@ goog.require('ol.layer.LayerState');
* @typedef {{animate: boolean,
* attributions: Object.,
* coordinateToPixelMatrix: goog.vec.Mat4.Number,
+ * devicePixelRatio: number,
* extent: (null|ol.Extent),
* focus: ol.Coordinate,
* index: number,
diff --git a/src/ol/map.js b/src/ol/map.js
index 1019341a02..98ea98d24e 100644
--- a/src/ol/map.js
+++ b/src/ol/map.js
@@ -157,6 +157,13 @@ ol.Map = function(options) {
var optionsInternal = ol.Map.createOptionsInternal(options);
+ /**
+ * @private
+ * @type {number}
+ */
+ this.devicePixelRatio_ = goog.isDef(options.devicePixelRatio) ?
+ options.devicePixelRatio : ol.BrowserFeature.DEVICE_PIXEL_RATIO;
+
/**
* @private
* @type {goog.async.AnimationDelay}
@@ -1069,6 +1076,7 @@ ol.Map.prototype.renderFrame_ = function(time) {
animate: false,
attributions: {},
coordinateToPixelMatrix: this.coordinateToPixelMatrix_,
+ devicePixelRatio: this.devicePixelRatio_,
extent: null,
focus: goog.isNull(this.focus_) ? view2DState.center : this.focus_,
index: this.frameIndex_++,
diff --git a/src/ol/render/canvas/canvasimmediate.js b/src/ol/render/canvas/canvasimmediate.js
index f3aa2228b5..2e1729bc29 100644
--- a/src/ol/render/canvas/canvasimmediate.js
+++ b/src/ol/render/canvas/canvasimmediate.js
@@ -17,11 +17,12 @@ goog.require('ol.style.Text');
* @constructor
* @implements {ol.render.IRender}
* @param {CanvasRenderingContext2D} context Context.
+ * @param {number} pixelRatio Pixel ratio.
* @param {ol.Extent} extent Extent.
* @param {goog.vec.Mat4.AnyType} transform Transform.
* @struct
*/
-ol.render.canvas.Immediate = function(context, extent, transform) {
+ol.render.canvas.Immediate = function(context, pixelRatio, extent, transform) {
/**
* @private
@@ -29,6 +30,12 @@ ol.render.canvas.Immediate = function(context, extent, transform) {
*/
this.context_ = context;
+ /**
+ * @private
+ * @type {number}
+ */
+ this.pixelRatio_ = pixelRatio;
+
/**
* @private
* @type {ol.Extent}
@@ -385,8 +392,8 @@ ol.render.canvas.Immediate.prototype.setFillStrokeStyle =
strokeStyle.lineDash : ol.render.canvas.defaultLineDash;
state.lineJoin = goog.isDef(strokeStyle.lineJoin) ?
strokeStyle.lineJoin : ol.render.canvas.defaultLineJoin;
- state.lineWidth = goog.isDef(strokeStyle.width) ?
- strokeStyle.width : ol.render.canvas.defaultLineWidth;
+ state.lineWidth = this.pixelRatio_ * (goog.isDef(strokeStyle.width) ?
+ strokeStyle.width : ol.render.canvas.defaultLineWidth);
state.miterLimit = goog.isDef(strokeStyle.miterLimit) ?
strokeStyle.miterLimit : ol.render.canvas.defaultMiterLimit;
} else {
diff --git a/src/ol/render/canvas/canvasreplay.js b/src/ol/render/canvas/canvasreplay.js
index 8a7f7a7a12..14763abc57 100644
--- a/src/ol/render/canvas/canvasreplay.js
+++ b/src/ol/render/canvas/canvasreplay.js
@@ -41,10 +41,17 @@ ol.render.canvas.Instruction = {
/**
* @constructor
* @implements {ol.render.IRender}
+ * @param {number} pixelRatio Pixel ratio.
* @protected
* @struct
*/
-ol.render.canvas.Replay = function() {
+ol.render.canvas.Replay = function(pixelRatio) {
+
+ /**
+ * @protected
+ * @type {number}
+ */
+ this.pixelRatio = pixelRatio;
/**
* @private
@@ -430,12 +437,13 @@ ol.render.canvas.Replay.prototype.setTextStyle = goog.abstractMethod;
/**
* @constructor
* @extends {ol.render.canvas.Replay}
+ * @param {number} pixelRatio Pixel ratio.
* @protected
* @struct
*/
-ol.render.canvas.ImageReplay = function() {
+ol.render.canvas.ImageReplay = function(pixelRatio) {
- goog.base(this);
+ goog.base(this, pixelRatio);
/**
* @private
@@ -588,12 +596,13 @@ ol.render.canvas.ImageReplay.prototype.setImageStyle = function(imageStyle) {
/**
* @constructor
* @extends {ol.render.canvas.Replay}
+ * @param {number} pixelRatio Pixel ratio.
* @protected
* @struct
*/
-ol.render.canvas.LineStringReplay = function() {
+ol.render.canvas.LineStringReplay = function(pixelRatio) {
- goog.base(this);
+ goog.base(this, pixelRatio);
/**
* @private
@@ -779,8 +788,8 @@ ol.render.canvas.LineStringReplay.prototype.setFillStrokeStyle =
strokeStyle.lineDash : ol.render.canvas.defaultLineDash;
this.state_.lineJoin = goog.isDef(strokeStyle.lineJoin) ?
strokeStyle.lineJoin : ol.render.canvas.defaultLineJoin;
- this.state_.lineWidth = goog.isDef(strokeStyle.width) ?
- strokeStyle.width : ol.render.canvas.defaultLineWidth;
+ this.state_.lineWidth = this.pixelRatio * (goog.isDef(strokeStyle.width) ?
+ strokeStyle.width : ol.render.canvas.defaultLineWidth);
this.state_.miterLimit = goog.isDef(strokeStyle.miterLimit) ?
strokeStyle.miterLimit : ol.render.canvas.defaultMiterLimit;
};
@@ -790,12 +799,13 @@ ol.render.canvas.LineStringReplay.prototype.setFillStrokeStyle =
/**
* @constructor
* @extends {ol.render.canvas.Replay}
+ * @param {number} pixelRatio Pixel ratio.
* @protected
* @struct
*/
-ol.render.canvas.PolygonReplay = function() {
+ol.render.canvas.PolygonReplay = function(pixelRatio) {
- goog.base(this);
+ goog.base(this, pixelRatio);
/**
* @private
@@ -990,8 +1000,8 @@ ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyle =
strokeStyle.lineDash : ol.render.canvas.defaultLineDash;
state.lineJoin = goog.isDef(strokeStyle.lineJoin) ?
strokeStyle.lineJoin : ol.render.canvas.defaultLineJoin;
- state.lineWidth = goog.isDef(strokeStyle.width) ?
- strokeStyle.width : ol.render.canvas.defaultLineWidth;
+ state.lineWidth = this.pixelRatio * (goog.isDef(strokeStyle.width) ?
+ strokeStyle.width : ol.render.canvas.defaultLineWidth);
state.miterLimit = goog.isDef(strokeStyle.miterLimit) ?
strokeStyle.miterLimit : ol.render.canvas.defaultMiterLimit;
} else {
@@ -1052,9 +1062,16 @@ ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyles_ = function() {
/**
* @constructor
* @implements {ol.render.IReplayGroup}
+ * @param {number} pixelRatio Pixel ratio.
* @struct
*/
-ol.render.canvas.ReplayGroup = function() {
+ol.render.canvas.ReplayGroup = function(pixelRatio) {
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.pixelRatio_ = pixelRatio;
/**
* @private
@@ -1248,7 +1265,7 @@ ol.render.canvas.ReplayGroup.prototype.getReplay =
if (!goog.isDef(replay)) {
var constructor = ol.render.canvas.BATCH_CONSTRUCTORS_[replayType];
goog.asserts.assert(goog.isDef(constructor));
- replay = new constructor();
+ replay = new constructor(this.pixelRatio_);
replayes[replayType] = replay;
}
return replay;
@@ -1266,7 +1283,8 @@ ol.render.canvas.ReplayGroup.prototype.isEmpty = function() {
/**
* @const
* @private
- * @type {Object.}
+ * @type {Object.}
*/
ol.render.canvas.BATCH_CONSTRUCTORS_ = {
'Image': ol.render.canvas.ImageReplay,
diff --git a/src/ol/renderer/canvas/canvasimagelayerrenderer.js b/src/ol/renderer/canvas/canvasimagelayerrenderer.js
index 29af98a994..dc6a94d298 100644
--- a/src/ol/renderer/canvas/canvasimagelayerrenderer.js
+++ b/src/ol/renderer/canvas/canvasimagelayerrenderer.js
@@ -96,9 +96,12 @@ ol.renderer.canvas.ImageLayer.prototype.prepareFrame =
image = this.image_;
var imageExtent = image.getExtent();
var imageResolution = image.getResolution();
+ var devicePixelRatio = frameState.devicePixelRatio;
ol.vec.Mat4.makeTransform2D(this.imageTransform_,
- frameState.size[0] / 2, frameState.size[1] / 2,
- imageResolution / viewResolution, imageResolution / viewResolution,
+ devicePixelRatio * frameState.size[0] / 2,
+ devicePixelRatio * frameState.size[1] / 2,
+ devicePixelRatio * imageResolution / viewResolution,
+ devicePixelRatio * imageResolution / viewResolution,
viewRotation,
(imageExtent[0] - viewCenter[0]) / imageResolution,
(viewCenter[1] - imageExtent[3]) / imageResolution);
diff --git a/src/ol/renderer/canvas/canvaslayerrenderer.js b/src/ol/renderer/canvas/canvaslayerrenderer.js
index 7485e3f05a..f3a0366338 100644
--- a/src/ol/renderer/canvas/canvaslayerrenderer.js
+++ b/src/ol/renderer/canvas/canvaslayerrenderer.js
@@ -85,8 +85,8 @@ ol.renderer.canvas.Layer.prototype.dispatchComposeEvent_ =
if (layer.hasListener(type)) {
var transform = goog.isDef(opt_transform) ?
opt_transform : this.getTransform(frameState);
- var render = new ol.render.canvas.Immediate(context, frameState.extent,
- transform);
+ var render = new ol.render.canvas.Immediate(
+ context, frameState.devicePixelRatio, frameState.extent, transform);
var composeEvent = new ol.render.Event(type, layer, render, frameState,
context, null);
layer.dispatchEvent(composeEvent);
@@ -139,9 +139,12 @@ ol.renderer.canvas.Layer.prototype.getImageTransform = goog.abstractMethod;
*/
ol.renderer.canvas.Layer.prototype.getTransform = function(frameState) {
var view2DState = frameState.view2DState;
+ var devicePixelRatio = frameState.devicePixelRatio;
return ol.vec.Mat4.makeTransform2D(this.transform_,
- frameState.size[0] / 2, frameState.size[1] / 2,
- 1 / view2DState.resolution, -1 / view2DState.resolution,
+ devicePixelRatio * frameState.size[0] / 2,
+ devicePixelRatio * frameState.size[1] / 2,
+ devicePixelRatio / view2DState.resolution,
+ -devicePixelRatio / view2DState.resolution,
-view2DState.rotation,
-view2DState.center[0], -view2DState.center[1]);
};
diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js
index a78e1f7735..a842aa6482 100644
--- a/src/ol/renderer/canvas/canvasmaprenderer.js
+++ b/src/ol/renderer/canvas/canvasmaprenderer.js
@@ -6,6 +6,7 @@ goog.require('goog.asserts');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.style');
+goog.require('goog.vec.Mat4');
goog.require('ol.css');
goog.require('ol.layer.Image');
goog.require('ol.layer.Tile');
@@ -19,6 +20,7 @@ goog.require('ol.renderer.canvas.Layer');
goog.require('ol.renderer.canvas.TileLayer');
goog.require('ol.renderer.canvas.VectorLayer');
goog.require('ol.source.State');
+goog.require('ol.vec.Mat4');
@@ -56,6 +58,12 @@ ol.renderer.canvas.Map = function(container, map) {
this.context_ = /** @type {CanvasRenderingContext2D} */
(this.canvas_.getContext('2d'));
+ /**
+ * @private
+ * @type {!goog.vec.Mat4.Number}
+ */
+ this.transform_ = goog.vec.Mat4.createNumber();
+
};
goog.inherits(ol.renderer.canvas.Map, ol.renderer.Map);
@@ -87,8 +95,17 @@ ol.renderer.canvas.Map.prototype.dispatchComposeEvent_ =
var map = this.getMap();
var context = this.context_;
if (map.hasListener(type)) {
+ var view2DState = frameState.view2DState;
+ var devicePixelRatio = frameState.devicePixelRatio;
+ ol.vec.Mat4.makeTransform2D(this.transform_,
+ this.canvas_.width / 2,
+ this.canvas_.height / 2,
+ devicePixelRatio / view2DState.resolution,
+ -devicePixelRatio / view2DState.resolution,
+ -view2DState.rotation,
+ -view2DState.center[0], -view2DState.center[1]);
var render = new ol.render.canvas.Immediate(
- context, frameState.extent, frameState.coordinateToPixelMatrix);
+ context, devicePixelRatio, frameState.extent, this.transform_);
var composeEvent = new ol.render.Event(type, map, render, frameState,
context, null);
map.dispatchEvent(composeEvent);
@@ -121,11 +138,12 @@ ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) {
}
var context = this.context_;
-
- var size = frameState.size;
- if (this.canvas_.width != size[0] || this.canvas_.height != size[1]) {
- this.canvas_.width = size[0];
- this.canvas_.height = size[1];
+ var ratio = frameState.devicePixelRatio;
+ var width = frameState.size[0] * ratio;
+ var height = frameState.size[1] * ratio;
+ if (this.canvas_.width != width || this.canvas_.height != height) {
+ this.canvas_.width = width;
+ this.canvas_.height = height;
} else {
context.clearRect(0, 0, this.canvas_.width, this.canvas_.height);
}
diff --git a/src/ol/renderer/canvas/canvastilelayerrenderer.js b/src/ol/renderer/canvas/canvastilelayerrenderer.js
index db5919be45..c24e597615 100644
--- a/src/ol/renderer/canvas/canvastilelayerrenderer.js
+++ b/src/ol/renderer/canvas/canvastilelayerrenderer.js
@@ -382,10 +382,12 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame =
this.scheduleExpireCache(frameState, tileSource);
this.updateLogos(frameState, tileSource);
+ var devicePixelRatio = frameState.devicePixelRatio;
ol.vec.Mat4.makeTransform2D(this.imageTransform_,
- frameState.size[0] / 2, frameState.size[1] / 2,
- tileResolution / view2DState.resolution,
- tileResolution / view2DState.resolution,
+ devicePixelRatio * frameState.size[0] / 2,
+ devicePixelRatio * frameState.size[1] / 2,
+ devicePixelRatio * tileResolution / view2DState.resolution,
+ devicePixelRatio * tileResolution / view2DState.resolution,
view2DState.rotation,
(origin[0] - center[0]) / tileResolution,
(center[1] - origin[1]) / tileResolution);
diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js
index 1061501efb..08b78a3a7f 100644
--- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js
+++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js
@@ -172,6 +172,7 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame =
var vectorSource = vectorLayer.getVectorSource();
var frameStateExtent = frameState.extent;
var frameStateResolution = frameState.view2DState.resolution;
+ var pixelRatio = frameState.devicePixelRatio;
if (!this.dirty_ &&
this.renderedResolution_ == frameStateResolution &&
@@ -198,15 +199,15 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame =
if (!goog.isDef(styleFunction)) {
styleFunction = ol.layer.Vector.defaultStyleFunction;
}
- var replayGroup = new ol.render.canvas.ReplayGroup();
+ var replayGroup = new ol.render.canvas.ReplayGroup(pixelRatio);
vectorSource.forEachFeatureInExtent(extent,
/**
* @param {ol.Feature} feature Feature.
*/
function(feature) {
this.dirty_ = this.dirty_ ||
- this.renderFeature(feature, frameStateResolution, styleFunction,
- replayGroup);
+ this.renderFeature(feature, frameStateResolution, pixelRatio,
+ styleFunction, replayGroup);
}, this);
replayGroup.finish();
@@ -223,20 +224,22 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame =
/**
* @param {ol.Feature} feature Feature.
* @param {number} resolution Resolution.
+ * @param {number} pixelRatio Pixel ratio.
* @param {ol.style.StyleFunction} styleFunction Style function.
* @param {ol.render.canvas.ReplayGroup} replayGroup Replay group.
* @return {boolean} `true` if an image is loading.
*/
ol.renderer.canvas.VectorLayer.prototype.renderFeature =
- function(feature, resolution, styleFunction, replayGroup) {
+ function(feature, resolution, pixelRatio, styleFunction, replayGroup) {
var loading = false;
var styles = styleFunction(feature, resolution);
// FIXME if styles is null, should we use the default style?
if (!goog.isDefAndNotNull(styles)) {
return false;
}
- // simplify to a tolerance of half a CSS pixel
- var squaredTolerance = resolution * resolution / 4;
+ // simplify to a tolerance of half a device pixel
+ var squaredTolerance =
+ resolution * resolution / (4 * pixelRatio * pixelRatio);
var i, ii, style, imageStyle, imageState;
for (i = 0, ii = styles.length; i < ii; ++i) {
style = styles[i];