diff --git a/examples/wms-custom-proj.js b/examples/wms-custom-proj.js index 87c7d6d7d1..5857831e85 100644 --- a/examples/wms-custom-proj.js +++ b/examples/wms-custom-proj.js @@ -18,7 +18,7 @@ var projection = ol.proj.configureProj4jsProjection({ var extent = [420000, 30000, 900000, 350000]; var layers = [ new ol.layer.Tile({ - source: new ol.source.TileWMS({ + source: new ol.source.TileWMS(/** @type {olx.source.TileWMSOptions} */ ({ url: 'http://wms.geo.admin.ch/', crossOrigin: 'anonymous', attributions: [new ol.Attribution({ @@ -31,11 +31,12 @@ var layers = [ 'LAYERS': 'ch.swisstopo.pixelkarte-farbe-pk1000.noscale', 'FORMAT': 'image/jpeg' }, - extent: extent - }) + extent: extent, + serverType: 'mapserver' + })) }), new ol.layer.Tile({ - source: new ol.source.TileWMS({ + source: new ol.source.TileWMS(/** @type {olx.source.TileWMSOptions} */ ({ url: 'http://wms.geo.admin.ch/', crossOrigin: 'anonymous', attributions: [new ol.Attribution({ @@ -45,8 +46,9 @@ var layers = [ 'National parks / geo.admin.ch' })], params: {'LAYERS': 'ch.bafu.schutzgebiete-paerke_nationaler_bedeutung'}, - extent: extent - }) + extent: extent, + serverType: 'mapserver' + })) }) ]; diff --git a/examples/wms-tiled.js b/examples/wms-tiled.js index b5315579b4..e6c0bc048c 100644 --- a/examples/wms-tiled.js +++ b/examples/wms-tiled.js @@ -11,11 +11,12 @@ var layers = [ source: new ol.source.MapQuest({layer: 'sat'}) }), new ol.layer.Tile({ - source: new ol.source.TileWMS({ + source: new ol.source.TileWMS(/** @type {olx.source.TileWMSOptions} */ ({ url: 'http://demo.opengeo.org/geoserver/wms', params: {'LAYERS': 'topp:states', 'TILED': true}, - extent: [-13884991, 2870341, -7455066, 6338219] - }) + extent: [-13884991, 2870341, -7455066, 6338219], + serverType: 'geoserver' + })) }) ]; var map = new ol.Map({ diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 55ad47473a..ff886b03c1 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -755,10 +755,15 @@ * edges" issues by properly configuring the WMS service. For example, MapServer * has a `tile_map_edge_buffer` configuration parameter for this. See * http://mapserver.org/output/tile_mode.html. + * @property {boolean|undefined} hidpi Use the `ol.Map#pixelRatio` value when + * requesting the image from the remote server. Default is `true`. * @property {string|undefined} logo Logo. * @property {ol.tilegrid.TileGrid|undefined} tileGrid Tile grid. * @property {number|undefined} maxZoom Maximum zoom. * @property {ol.proj.ProjectionLike} projection Projection. + * @property {ol.source.wms.ServerType|undefined} serverType The type of the remote WMS + * server: `mapserver`, `geoserver` or `qgis`. Only needed if `hidpi` is `true`. + * Default is `undefined`. * @property {ol.TileLoadFunctionType|undefined} tileLoadFunction Optional * function to load a tile given a URL. * @property {string|undefined} url WMS service URL. diff --git a/src/ol/renderer/canvas/canvastilelayerrenderer.js b/src/ol/renderer/canvas/canvastilelayerrenderer.js index 64aca68b02..a1bfffce45 100644 --- a/src/ol/renderer/canvas/canvastilelayerrenderer.js +++ b/src/ol/renderer/canvas/canvastilelayerrenderer.js @@ -164,6 +164,7 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = // composition and positioning. // + var pixelRatio = frameState.pixelRatio; var view2DState = frameState.view2DState; var projection = view2DState.projection; @@ -171,14 +172,14 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = goog.asserts.assertInstanceof(tileLayer, ol.layer.Tile); var tileSource = tileLayer.getSource(); goog.asserts.assertInstanceof(tileSource, ol.source.Tile); - var tileGrid = tileSource.getTileGrid(); - if (goog.isNull(tileGrid)) { - tileGrid = ol.tilegrid.getForProjection(projection); - } + var tileGrid = tileSource.getTileGridForProjection(projection); var tileGutter = tileSource.getGutter(); var z = tileGrid.getZForResolution(view2DState.resolution); - var tileSize = tileGrid.getTileSize(z); + var tilePixelSize = + tileSource.getTilePixelSize(z, frameState.pixelRatio, projection); + var tilePixelRatio = tilePixelSize / tileGrid.getTileSize(z); var tileResolution = tileGrid.getResolution(z); + var tilePixelResolution = tileResolution / tilePixelRatio; var center = view2DState.center; var extent; if (tileResolution == view2DState.resolution) { @@ -191,8 +192,8 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = var tileRange = tileGrid.getTileRangeForExtentAndResolution( extent, tileResolution); - var canvasWidth = tileSize * tileRange.getWidth(); - var canvasHeight = tileSize * tileRange.getHeight(); + var canvasWidth = tilePixelSize * tileRange.getWidth(); + var canvasHeight = tilePixelSize * tileRange.getHeight(); var canvas, context; if (goog.isNull(this.canvas_)) { @@ -231,8 +232,8 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = var canvasTileRange, canvasTileRangeWidth, minX, minY; if (goog.isNull(this.renderedCanvasTileRange_)) { - canvasTileRangeWidth = canvasWidth / tileSize; - var canvasTileRangeHeight = canvasHeight / tileSize; + canvasTileRangeWidth = canvasWidth / tilePixelSize; + var canvasTileRangeHeight = canvasHeight / tilePixelSize; minX = tileRange.minX - Math.floor((canvasTileRangeWidth - tileRange.getWidth()) / 2); minY = tileRange.minY - @@ -261,7 +262,7 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = var getTileIfLoaded = this.createGetTileIfLoadedFunction(function(tile) { return !goog.isNull(tile) && tile.getState() == ol.TileState.LOADED; - }, tileSource, projection); + }, tileSource, pixelRatio, projection); var findLoadedTiles = goog.bind(tileSource.findLoadedTiles, tileSource, tilesToDrawByZ, getTileIfLoaded); @@ -271,7 +272,7 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - tile = tileSource.getTile(z, x, y, projection); + tile = tileSource.getTile(z, x, y, pixelRatio, projection); tileState = tile.getState(); if (tileState == ol.TileState.LOADED || tileState == ol.TileState.EMPTY || @@ -299,9 +300,9 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = var i, ii; for (i = 0, ii = tilesToClear.length; i < ii; ++i) { tile = tilesToClear[i]; - x = tileSize * (tile.tileCoord.x - canvasTileRange.minX); - y = tileSize * (canvasTileRange.maxY - tile.tileCoord.y); - context.clearRect(x, y, tileSize, tileSize); + x = tilePixelSize * (tile.tileCoord.x - canvasTileRange.minX); + y = tilePixelSize * (canvasTileRange.maxY - tile.tileCoord.y); + context.clearRect(x, y, tilePixelSize, tilePixelSize); } /** @type {Array.} */ @@ -316,7 +317,8 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = var height, width; for (i = 0, ii = zs.length; i < ii; ++i) { currentZ = zs[i]; - tileSize = tileGrid.getTileSize(currentZ); + tilePixelSize = + tileSource.getTilePixelSize(currentZ, pixelRatio, projection); tilesToDraw = tilesToDrawByZ[currentZ]; if (currentZ == z) { for (tileCoordKey in tilesToDraw) { @@ -325,18 +327,18 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = (tile.tileCoord.y - canvasTileRange.minY) * canvasTileRangeWidth + (tile.tileCoord.x - canvasTileRange.minX); if (this.renderedTiles_[index] != tile) { - x = tileSize * (tile.tileCoord.x - canvasTileRange.minX); - y = tileSize * (canvasTileRange.maxY - tile.tileCoord.y); + x = tilePixelSize * (tile.tileCoord.x - canvasTileRange.minX); + y = tilePixelSize * (canvasTileRange.maxY - tile.tileCoord.y); tileState = tile.getState(); if (tileState == ol.TileState.EMPTY || tileState == ol.TileState.ERROR || !opaque) { - context.clearRect(x, y, tileSize, tileSize); + context.clearRect(x, y, tilePixelSize, tilePixelSize); } if (tileState == ol.TileState.LOADED) { context.drawImage(tile.getImage(), - tileGutter, tileGutter, tileSize, tileSize, - x, y, tileSize, tileSize); + tileGutter, tileGutter, tilePixelSize, tilePixelSize, + x, y, tilePixelSize, tilePixelSize); } this.renderedTiles_[index] = tile; } @@ -346,17 +348,17 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = for (tileCoordKey in tilesToDraw) { tile = tilesToDraw[tileCoordKey]; tileExtent = tileGrid.getTileCoordExtent(tile.tileCoord, tmpExtent); - x = (tileExtent[0] - origin[0]) / tileResolution; - y = (origin[1] - tileExtent[3]) / tileResolution; - width = scale * tileSize; - height = scale * tileSize; + x = (tileExtent[0] - origin[0]) / tilePixelResolution; + y = (origin[1] - tileExtent[3]) / tilePixelResolution; + width = scale * tilePixelSize; + height = scale * tilePixelSize; tileState = tile.getState(); if (tileState == ol.TileState.EMPTY || !opaque) { context.clearRect(x, y, width, height); } if (tileState == ol.TileState.LOADED) { context.drawImage(tile.getImage(), - tileGutter, tileGutter, tileSize, tileSize, + tileGutter, tileGutter, tilePixelSize, tilePixelSize, x, y, width, height); } interimTileRange = @@ -377,19 +379,18 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = } this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); - this.manageTilePyramid(frameState, tileSource, tileGrid, projection, extent, - z, tileLayer.getPreload()); + this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio, + projection, extent, z, tileLayer.getPreload()); this.scheduleExpireCache(frameState, tileSource); this.updateLogos(frameState, tileSource); - var pixelRatio = frameState.pixelRatio; ol.vec.Mat4.makeTransform2D(this.imageTransform_, pixelRatio * frameState.size[0] / 2, pixelRatio * frameState.size[1] / 2, - pixelRatio * tileResolution / view2DState.resolution, - pixelRatio * tileResolution / view2DState.resolution, + pixelRatio * tilePixelResolution / view2DState.resolution, + pixelRatio * tilePixelResolution / view2DState.resolution, view2DState.rotation, - (origin[0] - center[0]) / tileResolution, - (center[1] - origin[1]) / tileResolution); + (origin[0] - center[0]) / tilePixelResolution, + (center[1] - origin[1]) / tilePixelResolution); }; diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index a2c7c1ec13..a2b9974a16 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -81,6 +81,7 @@ ol.renderer.dom.TileLayer.prototype.prepareFrame = return; } + var pixelRatio = frameState.pixelRatio; var view2DState = frameState.view2DState; var projection = view2DState.projection; @@ -88,10 +89,7 @@ ol.renderer.dom.TileLayer.prototype.prepareFrame = goog.asserts.assertInstanceof(tileLayer, ol.layer.Tile); var tileSource = tileLayer.getSource(); goog.asserts.assertInstanceof(tileSource, ol.source.Tile); - var tileGrid = tileSource.getTileGrid(); - if (goog.isNull(tileGrid)) { - tileGrid = ol.tilegrid.getForProjection(projection); - } + var tileGrid = tileSource.getTileGridForProjection(projection); var tileGutter = tileSource.getGutter(); var z = tileGrid.getZForResolution(view2DState.resolution); var tileResolution = tileGrid.getResolution(z); @@ -113,7 +111,7 @@ ol.renderer.dom.TileLayer.prototype.prepareFrame = var getTileIfLoaded = this.createGetTileIfLoadedFunction(function(tile) { return !goog.isNull(tile) && tile.getState() == ol.TileState.LOADED; - }, tileSource, projection); + }, tileSource, pixelRatio, projection); var findLoadedTiles = goog.bind(tileSource.findLoadedTiles, tileSource, tilesToDrawByZ, getTileIfLoaded); @@ -123,7 +121,7 @@ ol.renderer.dom.TileLayer.prototype.prepareFrame = for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - tile = tileSource.getTile(z, x, y, projection); + tile = tileSource.getTile(z, x, y, pixelRatio, projection); tileState = tile.getState(); if (tileState == ol.TileState.LOADED) { tilesToDrawByZ[z][tile.tileCoord.toString()] = tile; @@ -241,8 +239,8 @@ ol.renderer.dom.TileLayer.prototype.prepareFrame = } this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); - this.manageTilePyramid(frameState, tileSource, tileGrid, projection, extent, - z, tileLayer.getPreload()); + this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio, + projection, extent, z, tileLayer.getPreload()); this.scheduleExpireCache(frameState, tileSource); this.updateLogos(frameState, tileSource); @@ -339,8 +337,12 @@ ol.renderer.dom.TileLayerZ_.prototype.addTile = function(tile, tileGutter) { imageStyle.position = 'absolute'; imageStyle.left = -tileGutter + 'px'; imageStyle.top = -tileGutter + 'px'; + imageStyle.width = (tileSize + 2 * tileGutter) + 'px'; + imageStyle.height = (tileSize + 2 * tileGutter) + 'px'; goog.dom.appendChild(tileElement, image); } else { + imageStyle.width = tileSize + 'px'; + imageStyle.height = tileSize + 'px'; tileElement = image; tileElementStyle = imageStyle; } diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index dfce3aae5e..4ef96fd124 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -197,13 +197,14 @@ ol.renderer.Layer.prototype.updateUsedTiles = * @param {function(ol.Tile): boolean} isLoadedFunction Function to * determine if the tile is loaded. * @param {ol.source.Tile} tileSource Tile source. + * @param {number} pixelRatio Pixel ratio. * @param {ol.proj.Projection} projection Projection. * @protected * @return {function(number, number, number): ol.Tile} Returns a tile if it is * loaded. */ ol.renderer.Layer.prototype.createGetTileIfLoadedFunction = - function(isLoadedFunction, tileSource, projection) { + function(isLoadedFunction, tileSource, pixelRatio, projection) { return ( /** * @param {number} z Z. @@ -212,7 +213,7 @@ ol.renderer.Layer.prototype.createGetTileIfLoadedFunction = * @return {ol.Tile} Tile. */ function(z, x, y) { - var tile = tileSource.getTile(z, x, y, projection); + var tile = tileSource.getTile(z, x, y, pixelRatio, projection); return isLoadedFunction(tile) ? tile : null; }); }; @@ -244,6 +245,7 @@ ol.renderer.Layer.prototype.snapCenterToPixel = * @param {ol.FrameState} frameState Frame state. * @param {ol.source.Tile} tileSource Tile source. * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. + * @param {number} pixelRatio Pixel ratio. * @param {ol.proj.Projection} projection Projection. * @param {ol.Extent} extent Extent. * @param {number} currentZ Current Z. @@ -254,8 +256,8 @@ ol.renderer.Layer.prototype.snapCenterToPixel = * @template T */ ol.renderer.Layer.prototype.manageTilePyramid = function( - frameState, tileSource, tileGrid, projection, extent, currentZ, preload, - opt_tileCallback, opt_this) { + frameState, tileSource, tileGrid, pixelRatio, projection, extent, + currentZ, preload, opt_tileCallback, opt_this) { var tileSourceKey = goog.getUid(tileSource).toString(); if (!(tileSourceKey in frameState.wantedTiles)) { frameState.wantedTiles[tileSourceKey] = {}; @@ -270,7 +272,7 @@ ol.renderer.Layer.prototype.manageTilePyramid = function( for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { if (currentZ - z <= preload) { - tile = tileSource.getTile(z, x, y, projection); + tile = tileSource.getTile(z, x, y, pixelRatio, projection); if (tile.getState() == ol.TileState.IDLE) { wantedTiles[tile.tileCoord.toString()] = true; if (!tileQueue.isKeyQueued(tile.getKey())) { diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index bacc5b9df3..d2bdb4add3 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -473,10 +473,11 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) { layerRenderer.prepareFrame(frameState, layerState); } - 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 width = frameState.size[0] * frameState.pixelRatio; + var height = frameState.size[1] * frameState.pixelRatio; + if (this.canvas_.width != width || this.canvas_.height != height) { + this.canvas_.width = width; + this.canvas_.height = height; } gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, null); diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index 36bf1fa7ac..5d4afc6ddc 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -121,14 +121,14 @@ ol.renderer.webgl.TileLayer.prototype.prepareFrame = goog.asserts.assertInstanceof(tileLayer, ol.layer.Tile); var tileSource = tileLayer.getSource(); goog.asserts.assertInstanceof(tileSource, ol.source.Tile); - var tileGrid = tileSource.getTileGrid(); - if (goog.isNull(tileGrid)) { - tileGrid = ol.tilegrid.getForProjection(projection); - } + var tileGrid = tileSource.getTileGridForProjection(projection); var z = tileGrid.getZForResolution(view2DState.resolution); var tileResolution = tileGrid.getResolution(z); - var tileSize = tileGrid.getTileSize(z); + var tilePixelSize = + tileSource.getTilePixelSize(z, frameState.pixelRatio, projection); + var pixelRatio = tilePixelSize / tileGrid.getTileSize(z); + var tilePixelResolution = tileResolution / pixelRatio; var tileGutter = tileSource.getGutter(); var center = view2DState.center; @@ -152,13 +152,13 @@ ol.renderer.webgl.TileLayer.prototype.prepareFrame = var tileRangeSize = tileRange.getSize(); - var maxDimension = - Math.max(tileRangeSize[0] * tileSize, tileRangeSize[1] * tileSize); + var maxDimension = Math.max( + tileRangeSize[0] * tilePixelSize, tileRangeSize[1] * tilePixelSize); var framebufferDimension = ol.math.roundUpToPowerOfTwo(maxDimension); - var framebufferExtentDimension = tileResolution * framebufferDimension; + var framebufferExtentDimension = tilePixelResolution * framebufferDimension; var origin = tileGrid.getOrigin(z); - var minX = origin[0] + tileRange.minX * tileSize * tileResolution; - var minY = origin[1] + tileRange.minY * tileSize * tileResolution; + var minX = origin[0] + tileRange.minX * tilePixelSize * tilePixelResolution; + var minY = origin[1] + tileRange.minY * tilePixelSize * tilePixelResolution; framebufferExtent = [ minX, minY, minX + framebufferExtentDimension, minY + framebufferExtentDimension @@ -196,7 +196,7 @@ ol.renderer.webgl.TileLayer.prototype.prepareFrame = var getTileIfLoaded = this.createGetTileIfLoadedFunction(function(tile) { return !goog.isNull(tile) && tile.getState() == ol.TileState.LOADED && mapRenderer.isTileTextureLoaded(tile); - }, tileSource, projection); + }, tileSource, pixelRatio, projection); var findLoadedTiles = goog.bind(tileSource.findLoadedTiles, tileSource, tilesToDrawByZ, getTileIfLoaded); @@ -207,7 +207,7 @@ ol.renderer.webgl.TileLayer.prototype.prepareFrame = for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - tile = tileSource.getTile(z, x, y, projection); + tile = tileSource.getTile(z, x, y, pixelRatio, projection); tileState = tile.getState(); if (tileState == ol.TileState.LOADED) { if (mapRenderer.isTileTextureLoaded(tile)) { @@ -254,8 +254,8 @@ ol.renderer.webgl.TileLayer.prototype.prepareFrame = framebufferExtentDimension - 1; goog.vec.Vec4.setFromValues(u_tileOffset, sx, sy, tx, ty); gl.uniform4fv(this.locations_.u_tileOffset, u_tileOffset); - mapRenderer.bindTileTexture(tile, tileSize, tileGutter, - goog.webgl.LINEAR, goog.webgl.LINEAR); + mapRenderer.bindTileTexture(tile, tilePixelSize, + tileGutter * pixelRatio, goog.webgl.LINEAR, goog.webgl.LINEAR); gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4); } } @@ -276,7 +276,7 @@ ol.renderer.webgl.TileLayer.prototype.prepareFrame = this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); var tileTextureQueue = mapRenderer.getTileTextureQueue(); this.manageTilePyramid( - frameState, tileSource, tileGrid, projection, extent, z, + frameState, tileSource, tileGrid, pixelRatio, projection, extent, z, tileLayer.getPreload(), /** * @param {ol.Tile} tile Tile. @@ -289,7 +289,7 @@ ol.renderer.webgl.TileLayer.prototype.prepareFrame = tile, tileGrid.getTileCoordCenter(tile.tileCoord), tileGrid.getResolution(tile.tileCoord.z), - tileSize, tileGutter + tilePixelSize, tileGutter * pixelRatio ]); } }, this); diff --git a/src/ol/source/bingmapssource.js b/src/ol/source/bingmapssource.js index 268f949237..c060ee88e1 100644 --- a/src/ol/source/bingmapssource.js +++ b/src/ol/source/bingmapssource.js @@ -101,10 +101,11 @@ ol.source.BingMaps.prototype.handleImageryMetadataResponse = /** * @this {ol.source.BingMaps} * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {number} pixelRatio Pixel ratio. * @param {ol.proj.Projection} projection Projection. * @return {string|undefined} Tile URL. */ - function(tileCoord, projection) { + function(tileCoord, pixelRatio, projection) { goog.asserts.assert(ol.proj.equivalent( projection, this.getProjection())); if (goog.isNull(tileCoord)) { diff --git a/src/ol/source/tileimagesource.js b/src/ol/source/tileimagesource.js index bf7d0982db..fe255e2cee 100644 --- a/src/ol/source/tileimagesource.js +++ b/src/ol/source/tileimagesource.js @@ -119,14 +119,15 @@ ol.source.TileImage.prototype.expireCache = function(usedTiles) { /** * @inheritDoc */ -ol.source.TileImage.prototype.getTile = function(z, x, y, projection) { +ol.source.TileImage.prototype.getTile = + function(z, x, y, pixelRatio, projection) { var tileCoordKey = this.getKeyZXY(z, x, y); if (this.tileCache.containsKey(tileCoordKey)) { return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey)); } else { goog.asserts.assert(projection); var tileCoord = new ol.TileCoord(z, x, y); - var tileUrl = this.tileUrlFunction(tileCoord, projection); + var tileUrl = this.tileUrlFunction(tileCoord, pixelRatio, projection); var tile = new this.tileClass( tileCoord, goog.isDef(tileUrl) ? ol.TileState.IDLE : ol.TileState.EMPTY, diff --git a/src/ol/source/tilesource.js b/src/ol/source/tilesource.js index 541659d2d2..732ab63d2f 100644 --- a/src/ol/source/tilesource.js +++ b/src/ol/source/tilesource.js @@ -144,6 +144,7 @@ ol.source.Tile.prototype.getResolutions = function() { * @param {number} z Tile coordinate z. * @param {number} x Tile coordinate x. * @param {number} y Tile coordinate y. + * @param {number} pixelRatio Pixel ratio. * @param {ol.proj.Projection=} opt_projection Projection. * @return {!ol.Tile} Tile. */ @@ -158,6 +159,32 @@ ol.source.Tile.prototype.getTileGrid = function() { }; +/** + * @param {ol.proj.Projection} projection Projection. + * @return {ol.tilegrid.TileGrid} Tile grid. + */ +ol.source.Tile.prototype.getTileGridForProjection = function(projection) { + if (goog.isNull(this.tileGrid)) { + return ol.tilegrid.getForProjection(projection); + } else { + return this.tileGrid; + } +}; + + +/** + * @param {number} z Z. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @return {number} Tile size. + */ +ol.source.Tile.prototype.getTilePixelSize = + function(z, pixelRatio, projection) { + var tileGrid = this.getTileGridForProjection(projection); + return tileGrid.getTileSize(z); +}; + + /** * Marks a tile coord as being used, without triggering a load. * @param {number} z Tile coordinate z. diff --git a/src/ol/source/tilewmssource.js b/src/ol/source/tilewmssource.js index 027c711e3e..bcabf10712 100644 --- a/src/ol/source/tilewmssource.js +++ b/src/ol/source/tilewmssource.js @@ -4,6 +4,7 @@ goog.provide('ol.source.TileWMS'); goog.require('goog.array'); +goog.require('goog.asserts'); goog.require('goog.math'); goog.require('goog.object'); goog.require('goog.string'); @@ -13,6 +14,7 @@ goog.require('ol.TileUrlFunction'); goog.require('ol.extent'); goog.require('ol.source.TileImage'); goog.require('ol.source.wms'); +goog.require('ol.source.wms.ServerType'); @@ -71,6 +73,18 @@ ol.source.TileWMS = function(opt_options) { */ this.v13_ = true; + /** + * @private + * @type {ol.source.wms.ServerType|undefined} + */ + this.serverType_ = options.serverType; + + /** + * @private + * @type {boolean} + */ + this.hidpi_ = goog.isDef(options.hidpi) ? options.hidpi : true; + /** * @private * @type {string} @@ -117,6 +131,23 @@ ol.source.TileWMS.prototype.getParams = function() { }; +/** + * @param {number} z Z. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @return {number} Size. + */ +ol.source.TileWMS.prototype.getTilePixelSize = + function(z, pixelRatio, projection) { + var tileSize = goog.base(this, 'getTilePixelSize', z, pixelRatio, projection); + if (pixelRatio == 1 || !this.hidpi_ || !goog.isDef(this.serverType_)) { + return tileSize; + } else { + return (tileSize * pixelRatio + 0.5) | 0; + } +}; + + /** * @private */ @@ -132,11 +163,13 @@ ol.source.TileWMS.prototype.resetCoordKeyPrefix_ = function() { /** * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {number} pixelRatio Pixel ratio. * @param {ol.proj.Projection} projection Projection. * @private * @return {string|undefined} Tile URL. */ -ol.source.TileWMS.prototype.tileUrlFunction_ = function(tileCoord, projection) { +ol.source.TileWMS.prototype.tileUrlFunction_ = + function(tileCoord, pixelRatio, projection) { var urls = this.urls_; if (!goog.isDef(urls) || goog.array.isEmpty(urls)) { @@ -145,7 +178,7 @@ ol.source.TileWMS.prototype.tileUrlFunction_ = function(tileCoord, projection) { var tileGrid = this.getTileGrid(); if (goog.isNull(tileGrid)) { - tileGrid = ol.tilegrid.getForProjection(projection); + tileGrid = this.getTileGridForProjection(projection); } if (tileGrid.getResolutions().length <= tileCoord.z) { @@ -164,17 +197,21 @@ ol.source.TileWMS.prototype.tileUrlFunction_ = function(tileCoord, projection) { goog.object.extend(params, this.params_); var tileResolution = tileGrid.getResolution(tileCoord.z); + if (pixelRatio != 1 && (!this.hidpi_ || !goog.isDef(this.serverType_))) { + pixelRatio = 1; + } var tileSize = tileGrid.getTileSize(tileCoord.z); var gutter = this.gutter_; - if (gutter === 0) { - goog.object.set(params, 'WIDTH', tileSize); - goog.object.set(params, 'HEIGHT', tileSize); - } else { - goog.object.set(params, 'WIDTH', tileSize + 2 * gutter); - goog.object.set(params, 'HEIGHT', tileSize + 2 * gutter); + if (gutter !== 0) { + tileSize += 2 * gutter; tileExtent = ol.extent.buffer(tileExtent, tileResolution * gutter, this.tmpExtent_); } + if (pixelRatio != 1) { + tileSize = (tileSize * pixelRatio + 0.5) | 0; + } + goog.object.set(params, 'WIDTH', tileSize); + goog.object.set(params, 'HEIGHT', tileSize); params[this.v13_ ? 'CRS' : 'SRS'] = projection.getCode(); @@ -182,6 +219,24 @@ ol.source.TileWMS.prototype.tileUrlFunction_ = function(tileCoord, projection) { goog.object.set(params, 'STYLES', new String('')); } + if (pixelRatio != 1) { + switch (this.serverType_) { + case ol.source.wms.ServerType.GEOSERVER: + var dpi = (90 * pixelRatio + 0.5) | 0; + goog.object.set(params, 'FORMAT_OPTIONS', 'dpi:' + dpi); + break; + case ol.source.wms.ServerType.MAPSERVER: + goog.object.set(params, 'MAP_RESOLUTION', 90 * pixelRatio); + break; + case ol.source.wms.ServerType.QGIS: + goog.object.set(params, 'DPI', 90 * pixelRatio); + break; + default: + goog.asserts.fail(); + break; + } + } + var axisOrientation = projection.getAxisOrientation(); var bbox; if (this.v13_ && axisOrientation.substr(0, 2) == 'ne') { diff --git a/src/ol/source/wmtssource.js b/src/ol/source/wmtssource.js index 15e2aae7bb..bd0d7c514b 100644 --- a/src/ol/source/wmtssource.js +++ b/src/ol/source/wmtssource.js @@ -96,10 +96,11 @@ ol.source.WMTS = function(options) { /** * @this {ol.source.WMTS} * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {number} pixelRatio Pixel ratio. * @param {ol.proj.Projection} projection Projection. * @return {string|undefined} Tile URL. */ - function(tileCoord, projection) { + function(tileCoord, pixelRatio, projection) { if (goog.isNull(tileCoord)) { return undefined; } else { diff --git a/src/ol/source/zoomifysource.js b/src/ol/source/zoomifysource.js index 7e1a372d47..011b8056ca 100644 --- a/src/ol/source/zoomifysource.js +++ b/src/ol/source/zoomifysource.js @@ -61,10 +61,11 @@ ol.source.Zoomify = function(opt_options) { /** * @this {ol.source.TileImage} * @param {ol.TileCoord} tileCoord Tile Coordinate. + * @param {number} pixelRatio Pixel ratio. * @param {ol.proj.Projection} projection Projection. * @return {string|undefined} Tile URL. */ - function(tileCoord, projection) { + function(tileCoord, pixelRatio, projection) { if (goog.isNull(tileCoord)) { return undefined; } else { diff --git a/src/ol/tileurlfunction.js b/src/ol/tileurlfunction.js index 8b8e17620b..ed7a17d9c3 100644 --- a/src/ol/tileurlfunction.js +++ b/src/ol/tileurlfunction.js @@ -8,7 +8,7 @@ goog.require('ol.TileCoord'); /** * @typedef {function(this: ol.source.TileImage, ol.TileCoord, - * ol.proj.Projection): (string|undefined)} + * number, ol.proj.Projection): (string|undefined)} */ ol.TileUrlFunctionType; @@ -29,10 +29,11 @@ ol.TileUrlFunction.createFromTemplate = function(template) { /** * @this {ol.source.TileImage} * @param {ol.TileCoord} tileCoord Tile Coordinate. + * @param {number} pixelRatio Pixel ratio. * @param {ol.proj.Projection} projection Projection. * @return {string|undefined} Tile URL. */ - function(tileCoord, projection) { + function(tileCoord, pixelRatio, projection) { if (goog.isNull(tileCoord)) { return undefined; } else { @@ -66,16 +67,18 @@ ol.TileUrlFunction.createFromTileUrlFunctions = function(tileUrlFunctions) { /** * @this {ol.source.TileImage} * @param {ol.TileCoord} tileCoord Tile Coordinate. + * @param {number} pixelRatio Pixel ratio. * @param {ol.proj.Projection} projection Projection. * @return {string|undefined} Tile URL. */ - function(tileCoord, projection) { + function(tileCoord, pixelRatio, projection) { if (goog.isNull(tileCoord)) { return undefined; } else { var index = goog.math.modulo(tileCoord.hash(), tileUrlFunctions.length); - return tileUrlFunctions[index].call(this, tileCoord, projection); + return tileUrlFunctions[index].call( + this, tileCoord, pixelRatio, projection); } }); }; @@ -84,10 +87,12 @@ ol.TileUrlFunction.createFromTileUrlFunctions = function(tileUrlFunctions) { /** * @this {ol.source.TileImage} * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {number} pixelRatio Pixel ratio. * @param {ol.proj.Projection} projection Projection. * @return {string|undefined} Tile URL. */ -ol.TileUrlFunction.nullTileUrlFunction = function(tileCoord, projection) { +ol.TileUrlFunction.nullTileUrlFunction = + function(tileCoord, pixelRatio, projection) { return undefined; }; @@ -104,16 +109,18 @@ ol.TileUrlFunction.withTileCoordTransform = /** * @this {ol.source.TileImage} * @param {ol.TileCoord} tileCoord Tile Coordinate. + * @param {number} pixelRatio Pixel ratio. * @param {ol.proj.Projection} projection Projection. * @return {string|undefined} Tile URL. */ - function(tileCoord, projection) { + function(tileCoord, pixelRatio, projection) { if (goog.isNull(tileCoord)) { return undefined; } else { return tileUrlFunction.call( this, transformFn.call(this, tileCoord, projection, tmpTileCoord), + pixelRatio, projection); } }); diff --git a/test/spec/ol/source/tilewmssource.test.js b/test/spec/ol/source/tilewmssource.test.js index f27cee5b97..ecdc587cc4 100644 --- a/test/spec/ol/source/tilewmssource.test.js +++ b/test/spec/ol/source/tilewmssource.test.js @@ -17,7 +17,7 @@ describe('ol.source.TileWMS', function() { it('returns a tile with the expected URL', function() { var source = new ol.source.TileWMS(options); - var tile = source.getTile(3, 2, 1, ol.proj.get('EPSG:3857')); + var tile = source.getTile(3, 2, 1, 1, ol.proj.get('EPSG:3857')); expect(tile).to.be.an(ol.ImageTile); var uri = new goog.Uri(tile.src_); expect(uri.getScheme()).to.be('http'); @@ -44,7 +44,7 @@ describe('ol.source.TileWMS', function() { it('returns a larger tile when a gutter is specified', function() { options.gutter = 16; var source = new ol.source.TileWMS(options); - var tile = source.getTile(3, 2, 1, ol.proj.get('EPSG:3857')); + var tile = source.getTile(3, 2, 1, 1, ol.proj.get('EPSG:3857')); expect(tile).to.be.an(ol.ImageTile); var uri = new goog.Uri(tile.src_); var queryData = uri.getQueryData(); @@ -58,7 +58,7 @@ describe('ol.source.TileWMS', function() { it('sets the SRS query value instead of CRS if version < 1.3', function() { options.params.VERSION = '1.2'; var source = new ol.source.TileWMS(options); - var tile = source.getTile(3, 2, 1, ol.proj.get('EPSG:4326')); + var tile = source.getTile(3, 2, 1, 1, ol.proj.get('EPSG:4326')); var uri = new goog.Uri(tile.src_); var queryData = uri.getQueryData(); expect(queryData.get('CRS')).to.be(undefined); @@ -69,7 +69,7 @@ describe('ol.source.TileWMS', function() { options.params.FORMAT = 'image/jpeg'; options.params.TRANSPARENT = false; var source = new ol.source.TileWMS(options); - var tile = source.getTile(3, 2, 1, ol.proj.get('EPSG:4326')); + var tile = source.getTile(3, 2, 1, 1, ol.proj.get('EPSG:4326')); var uri = new goog.Uri(tile.src_); var queryData = uri.getQueryData(); expect(queryData.get('FORMAT')).to.be('image/jpeg'); @@ -79,7 +79,7 @@ describe('ol.source.TileWMS', function() { it('does not add a STYLES= option if one is specified', function() { options.params.STYLES = 'foo'; var source = new ol.source.TileWMS(options); - var tile = source.getTile(3, 2, 1, ol.proj.get('EPSG:4326')); + var tile = source.getTile(3, 2, 1, 1, ol.proj.get('EPSG:4326')); var uri = new goog.Uri(tile.src_); var queryData = uri.getQueryData(); expect(queryData.get('STYLES')).to.be('foo'); @@ -87,7 +87,7 @@ describe('ol.source.TileWMS', function() { it('changes the BBOX order for EN axis orientations', function() { var source = new ol.source.TileWMS(options); - var tile = source.getTile(3, 2, 1, ol.proj.get('EPSG:4326')); + var tile = source.getTile(3, 2, 1, 1, ol.proj.get('EPSG:4326')); var uri = new goog.Uri(tile.src_); var queryData = uri.getQueryData(); expect(queryData.get('BBOX')).to.be('-45,-90,0,-45'); @@ -96,7 +96,7 @@ describe('ol.source.TileWMS', function() { it('uses EN BBOX order if version < 1.3', function() { options.params.VERSION = '1.1.0'; var source = new ol.source.TileWMS(options); - var tile = source.getTile(3, 2, 1, ol.proj.get('CRS:84')); + var tile = source.getTile(3, 2, 1, 1, ol.proj.get('CRS:84')); var uri = new goog.Uri(tile.src_); var queryData = uri.getQueryData(); expect(queryData.get('BBOX')).to.be('-90,-45,-45,0');