From 168e6db951e317f1cc464a8b488f3993c32cf522 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sat, 7 Oct 2017 10:03:45 -0600 Subject: [PATCH 01/15] Allow items to be removed from the cache --- src/ol/structs/lrucache.js | 28 +++++++++++++ test/spec/ol/structs/lrucache.test.js | 60 +++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/src/ol/structs/lrucache.js b/src/ol/structs/lrucache.js index bdced622cc..e0f74ec850 100644 --- a/src/ol/structs/lrucache.js +++ b/src/ol/structs/lrucache.js @@ -116,6 +116,34 @@ ol.structs.LRUCache.prototype.get = function(key) { }; +/** + * Remove an entry from the cache. + * @param {string} key The entry key. + * @return {T} The removed entry. + */ +ol.structs.LRUCache.prototype.remove = function(key) { + var entry = this.entries_[key]; + ol.asserts.assert(entry !== undefined, 15); // Tried to get a value for a key that does not exist in the cache + if (entry === this.newest_) { + this.newest_ = /** @type {ol.LRUCacheEntry} */ (entry.older); + if (this.newest_) { + this.newest_.newer = null; + } + } else if (entry === this.oldest_) { + this.oldest_ = /** @type {ol.LRUCacheEntry} */ (entry.newer); + if (this.oldest_) { + this.oldest_.older = null; + } + } else { + entry.newer.older = entry.older; + entry.older.newer = entry.newer; + } + delete this.entries_[key]; + --this.count_; + return entry.value_; +}; + + /** * @return {number} Count. */ diff --git a/test/spec/ol/structs/lrucache.test.js b/test/spec/ol/structs/lrucache.test.js index 71b662d661..fd25890692 100644 --- a/test/spec/ol/structs/lrucache.test.js +++ b/test/spec/ol/structs/lrucache.test.js @@ -188,6 +188,66 @@ describe('ol.structs.LRUCache', function() { }); }); + describe('#remove()', function() { + it('removes an item from the cache', function() { + var cache = new ol.structs.LRUCache(); + cache.set('oldest', 'oldest'); + cache.set('oldish', 'oldish'); + cache.set('newish', 'newish'); + cache.set('newest', 'newest'); + + cache.remove('oldish'); + expect(cache.getCount()).to.eql(3); + expect(cache.getValues()).to.eql(['newest', 'newish', 'oldest']); + }); + + it('works when removing the oldest item', function() { + var cache = new ol.structs.LRUCache(); + cache.set('oldest', 'oldest'); + cache.set('oldish', 'oldish'); + cache.set('newish', 'newish'); + cache.set('newest', 'newest'); + + cache.remove('oldest'); + expect(cache.getCount()).to.eql(3); + expect(cache.peekLastKey()).to.eql('oldish'); + expect(cache.getValues()).to.eql(['newest', 'newish', 'oldish']); + }); + + it('works when removing the newest item', function() { + var cache = new ol.structs.LRUCache(); + cache.set('oldest', 'oldest'); + cache.set('oldish', 'oldish'); + cache.set('newish', 'newish'); + cache.set('newest', 'newest'); + + cache.remove('newest'); + expect(cache.getCount()).to.eql(3); + expect(cache.peekFirstKey()).to.eql('newish'); + expect(cache.getValues()).to.eql(['newish', 'oldish', 'oldest']); + }); + + it('returns the removed item', function() { + var cache = new ol.structs.LRUCache(); + var item = {}; + cache.set('key', item); + + var returned = cache.remove('key'); + expect(returned).to.be(item); + }); + + it('throws if the key does not exist', function() { + var cache = new ol.structs.LRUCache(); + cache.set('foo', 'foo'); + cache.set('bar', 'bar'); + + var call = function() { + cache.remove('bam'); + }; + expect(call).to.throwException(); + }); + }); + describe('clearing the cache', function() { it('clears the cache', function() { fillLRUCache(lruCache); From b0f291973b579a63252779f72e004e663fbba8ab Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sat, 7 Oct 2017 10:04:19 -0600 Subject: [PATCH 02/15] Method for peeking at the newest cache entry key --- src/ol/structs/lrucache.js | 9 +++++++++ test/spec/ol/structs/lrucache.test.js | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/ol/structs/lrucache.js b/src/ol/structs/lrucache.js index e0f74ec850..046e8f5adf 100644 --- a/src/ol/structs/lrucache.js +++ b/src/ol/structs/lrucache.js @@ -196,6 +196,15 @@ ol.structs.LRUCache.prototype.peekLastKey = function() { }; +/** + * Get the key of the newest item in the cache. Throws if the cache is empty. + * @return {string} The newest key. + */ +ol.structs.LRUCache.prototype.peekFirstKey = function() { + return this.newest_.key_; +}; + + /** * @return {T} value Value. */ diff --git a/test/spec/ol/structs/lrucache.test.js b/test/spec/ol/structs/lrucache.test.js index fd25890692..3b4453bee3 100644 --- a/test/spec/ol/structs/lrucache.test.js +++ b/test/spec/ol/structs/lrucache.test.js @@ -164,6 +164,30 @@ describe('ol.structs.LRUCache', function() { }); }); + describe('#peekFirstKey()', function() { + it('returns the newest key in the cache', function() { + var cache = new ol.structs.LRUCache(); + cache.set('oldest', 'oldest'); + cache.set('oldish', 'oldish'); + cache.set('newish', 'newish'); + cache.set('newest', 'newest'); + expect(cache.peekFirstKey()).to.eql('newest'); + }); + + it('works if the cache has one item', function() { + var cache = new ol.structs.LRUCache(); + cache.set('key', 'value'); + expect(cache.peekFirstKey()).to.eql('key'); + }); + + it('throws if the cache is empty', function() { + var cache = new ol.structs.LRUCache(); + expect(function() { + cache.peekFirstKey(); + }).to.throwException(); + }); + }); + describe('peeking at the last value', function() { it('returns the last key', function() { fillLRUCache(lruCache); From c679e3042e92ba4043b2fd0f1e1e36f4eec4000a Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 8 Oct 2017 08:50:19 -0600 Subject: [PATCH 03/15] Functions to get key from coord and coord from key --- src/ol/tilecoord.js | 20 ++++++++++++++++++++ test/spec/ol/tilecoord.test.js | 19 +++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/ol/tilecoord.js b/src/ol/tilecoord.js index b7b082c70e..63740f7535 100644 --- a/src/ol/tilecoord.js +++ b/src/ol/tilecoord.js @@ -31,6 +31,26 @@ ol.tilecoord.getKeyZXY = function(z, x, y) { }; +/** + * Get the key for a tile coord. + * @param {ol.TileCoord} tileCoord The tile coord. + * @return {string} Key. + */ +ol.tilecoord.getKey = function(tileCoord) { + return ol.tilecoord.getKeyZXY(tileCoord[0], tileCoord[1], tileCoord[2]); +}; + + +/** + * Get a tile coord given a key. + * @param {string} key The tile coord key. + * @return {ol.TileCoord} The tile coord. + */ +ol.tilecoord.fromKey = function(key) { + return key.split('/').map(Number); +}; + + /** * @param {ol.TileCoord} tileCoord Tile coord. * @return {number} Hash. diff --git a/test/spec/ol/tilecoord.test.js b/test/spec/ol/tilecoord.test.js index a42a2f3efb..f1e4cd44fa 100644 --- a/test/spec/ol/tilecoord.test.js +++ b/test/spec/ol/tilecoord.test.js @@ -1,5 +1,3 @@ - - goog.require('ol.tilecoord'); goog.require('ol.tilegrid.TileGrid'); @@ -23,6 +21,23 @@ describe('ol.TileCoord', function() { }); }); + describe('getKey()', function() { + it('returns a key for a tile coord', function() { + var key = ol.tilecoord.getKey([1, 2, 3]); + expect(key).to.eql('1/2/3'); + }); + }); + + describe('fromKey()', function() { + it('returns a tile coord given a key', function() { + var tileCoord = [1, 2, 3]; + var key = ol.tilecoord.getKey(tileCoord); + + var returned = ol.tilecoord.fromKey(key); + expect(returned).to.eql(tileCoord); + }); + }); + describe('hash', function() { it('produces different hashes for different tile coords', function() { var tileCoord1 = [3, 2, 1]; From a4e0c54200151985925ec3177db6595d1e9e8d05 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sat, 7 Oct 2017 10:28:07 -0600 Subject: [PATCH 04/15] Maintain rendering order in the LRU cache --- src/ol/renderer/layer.js | 2 +- test/spec/ol/renderer/layer.test.js | 66 ++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/ol/renderer/layer.js b/src/ol/renderer/layer.js index f966d5c978..f72806ee27 100644 --- a/src/ol/renderer/layer.js +++ b/src/ol/renderer/layer.js @@ -256,7 +256,7 @@ ol.renderer.Layer.prototype.manageTilePyramid = function( var tileQueue = frameState.tileQueue; var minZoom = tileGrid.getMinZoom(); var tile, tileRange, tileResolution, x, y, z; - for (z = currentZ; z >= minZoom; --z) { + for (z = minZoom; z <= currentZ; ++z) { tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z, tileRange); tileResolution = tileGrid.getResolution(z); for (x = tileRange.minX; x <= tileRange.maxX; ++x) { diff --git a/test/spec/ol/renderer/layer.test.js b/test/spec/ol/renderer/layer.test.js index 01bb6ac78e..d0f3174309 100644 --- a/test/spec/ol/renderer/layer.test.js +++ b/test/spec/ol/renderer/layer.test.js @@ -1,8 +1,11 @@ - - goog.require('ol.Image'); +goog.require('ol.Map'); +goog.require('ol.View'); goog.require('ol.layer.Layer'); +goog.require('ol.layer.Tile'); goog.require('ol.renderer.Layer'); +goog.require('ol.source.XYZ'); +goog.require('ol.tilecoord'); describe('ol.renderer.Layer', function() { @@ -82,4 +85,63 @@ describe('ol.renderer.Layer', function() { }); }); + + describe('manageTilePyramid behavior', function() { + var target, map, view, source; + + beforeEach(function(done) { + target = document.createElement('div'); + Object.assign(target.style, { + position: 'absolute', + left: '-1000px', + top: '-1000px', + width: '360px', + height: '180px' + }); + document.body.appendChild(target); + + view = new ol.View({ + center: [0, 0], + zoom: 0 + }); + + source = new ol.source.XYZ({ + url: '#{x}/{y}/{z}' + }); + + map = new ol.Map({ + target: target, + view: view, + layers: [ + new ol.layer.Tile({ + source: source + }) + ] + }); + map.once('postrender', function() { + done(); + }); + }); + + afterEach(function() { + map.dispose(); + document.body.removeChild(target); + }); + + it('accesses tiles from current zoom level last', function(done) { + // expect most recent tile in the cache to be from zoom level 0 + var key = source.tileCache.peekFirstKey(); + var tileCoord = ol.tilecoord.fromKey(key); + expect(tileCoord[0]).to.be(0); + + map.once('moveend', function() { + // expect most recent tile in the cache to be from zoom level 4 + var key = source.tileCache.peekFirstKey(); + var tileCoord = ol.tilecoord.fromKey(key); + expect(tileCoord[0]).to.be(4); + done(); + }); + view.setZoom(4); + }); + }); }); From 97745c50af836ca1149aea37b584806d276bc02f Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sat, 7 Oct 2017 10:29:40 -0600 Subject: [PATCH 05/15] Prune all except for the most recent z on URL change --- src/ol/source/urltile.js | 1 + src/ol/tilecache.js | 20 +++++++++++++++++ test/spec/ol/tilecache.test.js | 39 ++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 test/spec/ol/tilecache.test.js diff --git a/src/ol/source/urltile.js b/src/ol/source/urltile.js index 39c3ffe06f..8f20a0795e 100644 --- a/src/ol/source/urltile.js +++ b/src/ol/source/urltile.js @@ -155,6 +155,7 @@ ol.source.UrlTile.prototype.setTileLoadFunction = function(tileLoadFunction) { */ ol.source.UrlTile.prototype.setTileUrlFunction = function(tileUrlFunction, opt_key) { this.tileUrlFunction = tileUrlFunction; + this.tileCache.pruneExceptNewestZ(); if (typeof opt_key !== 'undefined') { this.setKey(opt_key); } else { diff --git a/src/ol/tilecache.js b/src/ol/tilecache.js index 1ba7bbada9..088d9b1e72 100644 --- a/src/ol/tilecache.js +++ b/src/ol/tilecache.js @@ -2,6 +2,7 @@ goog.provide('ol.TileCache'); goog.require('ol'); goog.require('ol.structs.LRUCache'); +goog.require('ol.tilecoord'); /** @@ -33,3 +34,22 @@ ol.TileCache.prototype.expireCache = function(usedTiles) { } } }; + + +/** + * Prune all tiles from the cache that don't have the same z as the newest tile. + */ +ol.TileCache.prototype.pruneExceptNewestZ = function() { + if (this.getCount() === 0) { + return; + } + var key = this.peekFirstKey(); + var tileCoord = ol.tilecoord.fromKey(key); + var z = tileCoord[0]; + this.forEach(function(tile) { + if (tile.tileCoord[0] !== z) { + this.remove(ol.tilecoord.getKey(tile.tileCoord)); + tile.dispose(); + } + }, this); +}; diff --git a/test/spec/ol/tilecache.test.js b/test/spec/ol/tilecache.test.js new file mode 100644 index 0000000000..d0ab6b7c54 --- /dev/null +++ b/test/spec/ol/tilecache.test.js @@ -0,0 +1,39 @@ +goog.require('ol.Tile'); +goog.require('ol.TileCache'); +goog.require('ol.tilecoord'); + + +describe('ol.TileCache', function() { + + describe('#pruneExceptNewestZ()', function() { + it('gets rid of all entries that are not at the newest z', function() { + var tiles = [ + new ol.Tile([0, 0, 0]), + new ol.Tile([1, 0, 0]), + new ol.Tile([1, 1, 0]), + new ol.Tile([2, 0, 0]), + new ol.Tile([2, 1, 0]), + new ol.Tile([2, 2, 0]), + new ol.Tile([2, 3, 0]) // newest tile at z: 2 + ]; + var cache = new ol.TileCache(); + + sinon.spy(tiles[0], 'dispose'); + + tiles.forEach(function(tile) { + cache.set(ol.tilecoord.getKey(tile.tileCoord), tile); + }); + + cache.pruneExceptNewestZ(); + + expect(cache.getKeys()).to.eql([ + '2/3/0', + '2/2/0', + '2/1/0', + '2/0/0' + ]); + + expect(tiles[0].dispose.calledOnce).to.be(true); + }); + }); +}); From 5a1effb4839f77b3ae220897c559424b44e6e6a7 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sat, 7 Oct 2017 12:31:29 -0600 Subject: [PATCH 06/15] Get rid of unnecessary coord key prefix --- src/ol/source/tilewms.js | 43 ----------------------------- test/spec/ol/source/tilewms.test.js | 14 ++++------ 2 files changed, 6 insertions(+), 51 deletions(-) diff --git a/src/ol/source/tilewms.js b/src/ol/source/tilewms.js index 979981988d..b3e1ddcb62 100644 --- a/src/ol/source/tilewms.js +++ b/src/ol/source/tilewms.js @@ -81,13 +81,6 @@ ol.source.TileWMS = function(opt_options) { */ this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true; - /** - * @private - * @type {string} - */ - this.coordKeyPrefix_ = ''; - this.resetCoordKeyPrefix_(); - /** * @private * @type {ol.Extent} @@ -171,14 +164,6 @@ ol.source.TileWMS.prototype.getGutterInternal = function() { }; -/** - * @inheritDoc - */ -ol.source.TileWMS.prototype.getKeyZXY = function(z, x, y) { - return this.coordKeyPrefix_ + ol.source.TileImage.prototype.getKeyZXY.call(this, z, x, y); -}; - - /** * Get the user-provided params, i.e. those passed to the constructor through * the "params" option, and possibly updated using the updateParams method. @@ -273,24 +258,6 @@ ol.source.TileWMS.prototype.getTilePixelRatio = function(pixelRatio) { }; -/** - * @private - */ -ol.source.TileWMS.prototype.resetCoordKeyPrefix_ = function() { - var i = 0; - var res = []; - - if (this.urls) { - var j, jj; - for (j = 0, jj = this.urls.length; j < jj; ++j) { - res[i++] = this.urls[j]; - } - } - - this.coordKeyPrefix_ = res.join('#'); -}; - - /** * @private * @return {string} The key for the current params. @@ -352,15 +319,6 @@ ol.source.TileWMS.prototype.fixedTileUrlFunction = function(tileCoord, pixelRati pixelRatio, projection, baseParams); }; -/** - * @inheritDoc - */ -ol.source.TileWMS.prototype.setUrls = function(urls) { - ol.source.TileImage.prototype.setUrls.call(this, urls); - this.resetCoordKeyPrefix_(); -}; - - /** * Update the user-provided params. * @param {Object} params Params. @@ -368,7 +326,6 @@ ol.source.TileWMS.prototype.setUrls = function(urls) { */ ol.source.TileWMS.prototype.updateParams = function(params) { ol.obj.assign(this.params_, params); - this.resetCoordKeyPrefix_(); this.updateV13_(); this.setKey(this.getKeyForParams_()); }; diff --git a/test/spec/ol/source/tilewms.test.js b/test/spec/ol/source/tilewms.test.js index 478a0bf27a..8045d184c5 100644 --- a/test/spec/ol/source/tilewms.test.js +++ b/test/spec/ol/source/tilewms.test.js @@ -275,15 +275,13 @@ describe('ol.source.TileWMS', function() { }); describe('#setUrls()', function() { - it ('resets coordKeyPrefix_', function() { - var urls = ['u1', 'u2']; - var source1 = new ol.source.TileWMS({ - urls: urls + it ('updates the source key', function() { + var source = new ol.source.TileWMS({ + urls: ['u1', 'u2'] }); - var source2 = new ol.source.TileWMS({}); - expect(source2.coordKeyPrefix_).to.be.empty(); - source2.setUrls(urls); - expect(source2.coordKeyPrefix_).to.equal(source1.coordKeyPrefix_); + var originalKey = source.getKey(); + source.setUrls(['u3', 'u4']); + expect(source.getKey() !== originalKey).to.be(true); }); }); }); From 177fcc7bcca140a1fe71bbb0223aa3eb8387286b Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sat, 7 Oct 2017 12:37:55 -0600 Subject: [PATCH 07/15] Consistent use of ol.tilecoord.getKeyZXY() --- src/ol/source/tile.js | 12 +----------- src/ol/source/tiledebug.js | 3 ++- src/ol/source/tileimage.js | 5 +++-- src/ol/source/tileutfgrid.js | 5 +++-- src/ol/source/urltile.js | 3 ++- src/ol/source/vectortile.js | 5 +++-- test/spec/ol/source/tile.test.js | 4 +++- test/spec/ol/source/tileimage.test.js | 5 ++--- 8 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/ol/source/tile.js b/src/ol/source/tile.js index a757935d88..ddc39273c3 100644 --- a/src/ol/source/tile.js +++ b/src/ol/source/tile.js @@ -120,7 +120,7 @@ ol.source.Tile.prototype.forEachLoadedTile = function(projection, z, tileRange, var tile, tileCoordKey, loaded; for (var x = tileRange.minX; x <= tileRange.maxX; ++x) { for (var y = tileRange.minY; y <= tileRange.maxY; ++y) { - tileCoordKey = this.getKeyZXY(z, x, y); + tileCoordKey = ol.tilecoord.getKeyZXY(z, x, y); loaded = false; if (tileCache.containsKey(tileCoordKey)) { tile = /** @type {!ol.Tile} */ (tileCache.get(tileCoordKey)); @@ -170,16 +170,6 @@ ol.source.Tile.prototype.setKey = function(key) { }; -/** - * @param {number} z Z. - * @param {number} x X. - * @param {number} y Y. - * @return {string} Key. - * @protected - */ -ol.source.Tile.prototype.getKeyZXY = ol.tilecoord.getKeyZXY; - - /** * @param {ol.proj.Projection} projection Projection. * @return {boolean} Opaque. diff --git a/src/ol/source/tiledebug.js b/src/ol/source/tiledebug.js index 76772fd62b..80dfa8013f 100644 --- a/src/ol/source/tiledebug.js +++ b/src/ol/source/tiledebug.js @@ -6,6 +6,7 @@ goog.require('ol.TileState'); goog.require('ol.dom'); goog.require('ol.size'); goog.require('ol.source.Tile'); +goog.require('ol.tilecoord'); /** @@ -38,7 +39,7 @@ ol.inherits(ol.source.TileDebug, ol.source.Tile); * @inheritDoc */ ol.source.TileDebug.prototype.getTile = function(z, x, y) { - var tileCoordKey = this.getKeyZXY(z, x, y); + var tileCoordKey = ol.tilecoord.getKeyZXY(z, x, y); if (this.tileCache.containsKey(tileCoordKey)) { return /** @type {!ol.source.TileDebug.Tile_} */ (this.tileCache.get(tileCoordKey)); } else { diff --git a/src/ol/source/tileimage.js b/src/ol/source/tileimage.js index acffdf50bf..4d5d8f9b5c 100644 --- a/src/ol/source/tileimage.js +++ b/src/ol/source/tileimage.js @@ -9,6 +9,7 @@ goog.require('ol.events.EventType'); goog.require('ol.proj'); goog.require('ol.reproj.Tile'); goog.require('ol.source.UrlTile'); +goog.require('ol.tilecoord'); goog.require('ol.tilegrid'); @@ -245,7 +246,7 @@ ol.source.TileImage.prototype.getTile = function(z, x, y, pixelRatio, projection var cache = this.getTileCacheForProjection(projection); var tileCoord = [z, x, y]; var tile; - var tileCoordKey = this.getKeyZXY.apply(this, tileCoord); + var tileCoordKey = ol.tilecoord.getKey(tileCoord); if (cache.containsKey(tileCoordKey)) { tile = /** @type {!ol.Tile} */ (cache.get(tileCoordKey)); } @@ -293,7 +294,7 @@ ol.source.TileImage.prototype.getTile = function(z, x, y, pixelRatio, projection */ ol.source.TileImage.prototype.getTileInternal = function(z, x, y, pixelRatio, projection) { var tile = null; - var tileCoordKey = this.getKeyZXY(z, x, y); + var tileCoordKey = ol.tilecoord.getKeyZXY(z, x, y); var key = this.getKey(); if (!this.tileCache.containsKey(tileCoordKey)) { tile = this.createTile_(z, x, y, pixelRatio, projection, key); diff --git a/src/ol/source/tileutfgrid.js b/src/ol/source/tileutfgrid.js index 023fffe843..4826c9901a 100644 --- a/src/ol/source/tileutfgrid.js +++ b/src/ol/source/tileutfgrid.js @@ -13,6 +13,7 @@ goog.require('ol.net'); goog.require('ol.proj'); goog.require('ol.source.State'); goog.require('ol.source.Tile'); +goog.require('ol.tilecoord'); goog.require('ol.tilegrid'); @@ -223,7 +224,7 @@ ol.source.TileUTFGrid.prototype.handleTileJSONResponse = function(tileJSON) { * @inheritDoc */ ol.source.TileUTFGrid.prototype.getTile = function(z, x, y, pixelRatio, projection) { - var tileCoordKey = this.getKeyZXY(z, x, y); + var tileCoordKey = ol.tilecoord.getKeyZXY(z, x, y); if (this.tileCache.containsKey(tileCoordKey)) { return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey)); } else { @@ -248,7 +249,7 @@ ol.source.TileUTFGrid.prototype.getTile = function(z, x, y, pixelRatio, projecti * @inheritDoc */ ol.source.TileUTFGrid.prototype.useTile = function(z, x, y) { - var tileCoordKey = this.getKeyZXY(z, x, y); + var tileCoordKey = ol.tilecoord.getKeyZXY(z, x, y); if (this.tileCache.containsKey(tileCoordKey)) { this.tileCache.get(tileCoordKey); } diff --git a/src/ol/source/urltile.js b/src/ol/source/urltile.js index 8f20a0795e..48e7d77003 100644 --- a/src/ol/source/urltile.js +++ b/src/ol/source/urltile.js @@ -5,6 +5,7 @@ goog.require('ol.TileState'); goog.require('ol.TileUrlFunction'); goog.require('ol.source.Tile'); goog.require('ol.source.TileEventType'); +goog.require('ol.tilecoord'); /** @@ -195,7 +196,7 @@ ol.source.UrlTile.prototype.setUrls = function(urls) { * @inheritDoc */ ol.source.UrlTile.prototype.useTile = function(z, x, y) { - var tileCoordKey = this.getKeyZXY(z, x, y); + var tileCoordKey = ol.tilecoord.getKeyZXY(z, x, y); if (this.tileCache.containsKey(tileCoordKey)) { this.tileCache.get(tileCoordKey); } diff --git a/src/ol/source/vectortile.js b/src/ol/source/vectortile.js index e54f680bd7..f8801c948e 100644 --- a/src/ol/source/vectortile.js +++ b/src/ol/source/vectortile.js @@ -5,8 +5,9 @@ goog.require('ol.TileState'); goog.require('ol.VectorImageTile'); goog.require('ol.VectorTile'); goog.require('ol.size'); -goog.require('ol.tilegrid'); goog.require('ol.source.UrlTile'); +goog.require('ol.tilecoord'); +goog.require('ol.tilegrid'); /** @@ -110,7 +111,7 @@ ol.source.VectorTile.prototype.clear = function() { * @inheritDoc */ ol.source.VectorTile.prototype.getTile = function(z, x, y, pixelRatio, projection) { - var tileCoordKey = this.getKeyZXY(z, x, y); + var tileCoordKey = ol.tilecoord.getKeyZXY(z, x, y); if (this.tileCache.containsKey(tileCoordKey)) { return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey)); } else { diff --git a/test/spec/ol/source/tile.test.js b/test/spec/ol/source/tile.test.js index c7f454aa8d..5f04aac69f 100644 --- a/test/spec/ol/source/tile.test.js +++ b/test/spec/ol/source/tile.test.js @@ -5,8 +5,10 @@ goog.require('ol.proj'); goog.require('ol.proj.Projection'); goog.require('ol.source.Source'); goog.require('ol.source.Tile'); +goog.require('ol.tilecoord'); goog.require('ol.tilegrid.TileGrid'); + /** * Tile source for tests that uses a EPSG:4326 based grid with 4 resolutions and * 256x256 tiles. @@ -40,7 +42,7 @@ ol.inherits(MockTile, ol.source.Tile); * @inheritDoc */ MockTile.prototype.getTile = function(z, x, y) { - var key = this.getKeyZXY(z, x, y); + var key = ol.tilecoord.getKeyZXY(z, x, y); if (this.tileCache.containsKey(key)) { return /** @type {!ol.Tile} */ (this.tileCache.get(key)); } else { diff --git a/test/spec/ol/source/tileimage.test.js b/test/spec/ol/source/tileimage.test.js index 4f4c549417..9b9b98b185 100644 --- a/test/spec/ol/source/tileimage.test.js +++ b/test/spec/ol/source/tileimage.test.js @@ -1,5 +1,3 @@ - - goog.require('ol.ImageTile'); goog.require('ol.TileState'); goog.require('ol.TileUrlFunction'); @@ -9,6 +7,7 @@ goog.require('ol.proj.EPSG3857'); goog.require('ol.proj.Projection'); goog.require('ol.reproj.Tile'); goog.require('ol.source.TileImage'); +goog.require('ol.tilecoord'); goog.require('ol.tilegrid'); @@ -52,7 +51,7 @@ describe('ol.source.TileImage', function() { expect(source.getKey()).to.be(''); source.getTileInternal(0, 0, -1, 1, ol.proj.get('EPSG:3857')); expect(source.tileCache.getCount()).to.be(1); - tile = source.tileCache.get(source.getKeyZXY(0, 0, -1)); + tile = source.tileCache.get(ol.tilecoord.getKeyZXY(0, 0, -1)); }); it('gets the tile from the cache', function() { From 623dcd881f953385f150dc85ab17a6aab77d61f3 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 9 Oct 2017 18:12:40 -0600 Subject: [PATCH 08/15] Avoid unnecessary transition on raster sources --- examples/color-manipulation.js | 3 ++- examples/sea-level.js | 3 ++- examples/shaded-relief.js | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/color-manipulation.js b/examples/color-manipulation.js index ef5d3fa3e9..d54d7af4de 100644 --- a/examples/color-manipulation.js +++ b/examples/color-manipulation.js @@ -102,7 +102,8 @@ function xyz2rgb(x) { var raster = new ol.source.Raster({ sources: [new ol.source.Stamen({ - layer: 'watercolor' + layer: 'watercolor', + transition: 0 })], operation: function(pixels, data) { var hcl = rgb2hcl(pixels[0]); diff --git a/examples/sea-level.js b/examples/sea-level.js index 58783bc396..269627e82d 100644 --- a/examples/sea-level.js +++ b/examples/sea-level.js @@ -26,7 +26,8 @@ function flood(pixels, data) { var key = 'pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg'; var elevation = new ol.source.XYZ({ url: 'https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.pngraw?access_token=' + key, - crossOrigin: 'anonymous' + crossOrigin: 'anonymous', + transition: 0 }); var raster = new ol.source.Raster({ diff --git a/examples/shaded-relief.js b/examples/shaded-relief.js index 92c6ade1b6..9d190c449a 100644 --- a/examples/shaded-relief.js +++ b/examples/shaded-relief.js @@ -104,7 +104,8 @@ function shade(inputs, data) { var elevation = new ol.source.XYZ({ url: 'https://{a-d}.tiles.mapbox.com/v3/aj.sf-dem/{z}/{x}/{y}.png', - crossOrigin: 'anonymous' + crossOrigin: 'anonymous', + transition: 0 }); var raster = new ol.source.Raster({ From 90b08cdc1ccd9218f21dc7cf9fd80f75725ed856 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 9 Oct 2017 18:01:08 -0600 Subject: [PATCH 09/15] Set time and handle frame animation in raster source --- src/ol/source/raster.js | 7 ++ .../rendering/ol/source/expected/raster-1.png | Bin 0 -> 12872 bytes test/rendering/ol/source/raster.test.js | 84 ++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 test/rendering/ol/source/expected/raster-1.png create mode 100644 test/rendering/ol/source/raster.test.js diff --git a/src/ol/source/raster.js b/src/ol/source/raster.js index 3588573adb..df63094356 100644 --- a/src/ol/source/raster.js +++ b/src/ol/source/raster.js @@ -181,6 +181,8 @@ ol.source.Raster.prototype.updateFrameState_ = function(extent, resolution, proj frameState.focus = center; frameState.size[0] = Math.round(ol.extent.getWidth(extent) / resolution); frameState.size[1] = Math.round(ol.extent.getHeight(extent) / resolution); + frameState.time = Date.now(); + frameState.animate = false; var viewState = frameState.viewState; viewState.center = center; @@ -234,6 +236,11 @@ ol.source.Raster.prototype.getImage = function(extent, resolution, pixelRatio, p } frameState.tileQueue.loadMoreTiles(16, 16); + + if (frameState.animate) { + requestAnimationFrame(this.changed.bind(this)); + } + return this.renderedImageCanvas_; }; diff --git a/test/rendering/ol/source/expected/raster-1.png b/test/rendering/ol/source/expected/raster-1.png new file mode 100644 index 0000000000000000000000000000000000000000..ef6dc6cb599a7a8dd9720a63887d252704a3d044 GIT binary patch literal 12872 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 Date: Mon, 9 Oct 2017 18:45:55 +0200 Subject: [PATCH 10/15] Take pixel ratio into account for text stroke --- src/ol/render/canvas/textreplay.js | 2 +- .../ol/style/expected/text-canvas-hidpi.png | Bin 0 -> 8748 bytes test/rendering/ol/style/text.test.js | 10 ++++++++-- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 test/rendering/ol/style/expected/text-canvas-hidpi.png diff --git a/src/ol/render/canvas/textreplay.js b/src/ol/render/canvas/textreplay.js index d39abc05bf..2ff5fd3792 100644 --- a/src/ol/render/canvas/textreplay.js +++ b/src/ol/render/canvas/textreplay.js @@ -326,7 +326,7 @@ ol.render.canvas.TextReplay.prototype.getImage_ = function(text, fill, stroke) { context.font = textState.font; if (stroke) { context.strokeStyle = strokeState.strokeStyle; - context.lineWidth = strokeState.lineWidth; + context.lineWidth = strokeWidth * pixelRatio; context.lineCap = strokeState.lineCap; context.lineJoin = strokeState.lineJoin; context.miterLimit = strokeState.miterLimit; diff --git a/test/rendering/ol/style/expected/text-canvas-hidpi.png b/test/rendering/ol/style/expected/text-canvas-hidpi.png new file mode 100644 index 0000000000000000000000000000000000000000..4c8a444edb405a4b727f47f4c5d74a5b4cd367b7 GIT binary patch literal 8748 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 Date: Mon, 9 Oct 2017 18:46:28 +0200 Subject: [PATCH 11/15] Allow reference image creation without existing reference image --- test/test-extensions.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/test-extensions.js b/test/test-extensions.js index 9844f74edc..52f70574f1 100644 --- a/test/test-extensions.js +++ b/test/test-extensions.js @@ -383,6 +383,14 @@ goog.require('ol.has'); }; function resembleCanvas(canvas, referenceImage, tolerance, done) { + if (showMap) { + var wrapper = document.createElement('div'); + wrapper.style.width = canvas.width + 'px'; + wrapper.style.height = canvas.height + 'px'; + wrapper.appendChild(canvas); + document.body.appendChild(wrapper); + document.body.appendChild(document.createTextNode(referenceImage)); + } var width = canvas.width; var height = canvas.height; var image = new Image(); @@ -394,14 +402,6 @@ goog.require('ol.has'); referenceCanvas.height = image.height; var referenceContext = referenceCanvas.getContext('2d'); referenceContext.drawImage(image, 0, 0, image.width, image.height); - if (showMap) { - var wrapper = document.createElement('div'); - wrapper.style.width = canvas.width + 'px'; - wrapper.style.height = canvas.height + 'px'; - wrapper.appendChild(canvas); - document.body.appendChild(wrapper); - document.body.appendChild(document.createTextNode(referenceImage)); - } var context = canvas.getContext('2d'); var output = context.createImageData(canvas.width, canvas.height); var mismatchPx = pixelmatch( From 1141c82da11be89283f07b8fdf00c83603ad34d2 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Wed, 11 Oct 2017 00:32:45 +0200 Subject: [PATCH 12/15] Handle different lineWidth scaling in Safari --- src/ol/render/canvas/textreplay.js | 2 +- .../ol/style/expected/text-canvas-hidpi.png | Bin 8748 -> 5469 bytes test/rendering/ol/style/text.test.js | 7 ++++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ol/render/canvas/textreplay.js b/src/ol/render/canvas/textreplay.js index 2ff5fd3792..842b8e148b 100644 --- a/src/ol/render/canvas/textreplay.js +++ b/src/ol/render/canvas/textreplay.js @@ -326,7 +326,7 @@ ol.render.canvas.TextReplay.prototype.getImage_ = function(text, fill, stroke) { context.font = textState.font; if (stroke) { context.strokeStyle = strokeState.strokeStyle; - context.lineWidth = strokeWidth * pixelRatio; + context.lineWidth = strokeWidth * (ol.has.SAFARI ? pixelRatio : 1); context.lineCap = strokeState.lineCap; context.lineJoin = strokeState.lineJoin; context.miterLimit = strokeState.miterLimit; diff --git a/test/rendering/ol/style/expected/text-canvas-hidpi.png b/test/rendering/ol/style/expected/text-canvas-hidpi.png index 4c8a444edb405a4b727f47f4c5d74a5b4cd367b7..7577d3b62f10ce6ed2c90f92342a3bb53b0451dd 100644 GIT binary patch literal 5469 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 Ar~m)} literal 8748 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 Date: Wed, 11 Oct 2017 15:40:51 +0200 Subject: [PATCH 13/15] Pre-render text images for configured scale --- src/ol/render/canvas/textreplay.js | 25 ++++++++-------- .../ol/style/expected/text-canvas-scale.png | Bin 0 -> 6722 bytes .../expected/text-linestring-nice-scale.png | Bin 0 -> 19970 bytes test/rendering/ol/style/text.test.js | 27 ++++++++++++++---- 4 files changed, 35 insertions(+), 17 deletions(-) create mode 100644 test/rendering/ol/style/expected/text-canvas-scale.png create mode 100644 test/rendering/ol/style/expected/text-linestring-nice-scale.png diff --git a/src/ol/render/canvas/textreplay.js b/src/ol/render/canvas/textreplay.js index 842b8e148b..717b034ff7 100644 --- a/src/ol/render/canvas/textreplay.js +++ b/src/ol/render/canvas/textreplay.js @@ -309,6 +309,7 @@ ol.render.canvas.TextReplay.prototype.getImage_ = function(text, fill, stroke) { var fillState = this.textFillState_; var textState = this.textState_; var pixelRatio = this.pixelRatio; + var scale = this.textScale_ * pixelRatio; var align = ol.render.replay.TEXT_ALIGN[textState.textAlign || ol.render.canvas.defaultTextAlign]; var strokeWidth = stroke && strokeState.lineWidth ? strokeState.lineWidth : 0; @@ -318,15 +319,15 @@ ol.render.canvas.TextReplay.prototype.getImage_ = function(text, fill, stroke) { var height = lineHeight * numLines; var renderWidth = (width + 2 * strokeWidth); var context = ol.dom.createCanvasContext2D( - Math.ceil(renderWidth * pixelRatio), - Math.ceil((height + 2 * strokeWidth) * pixelRatio)); + Math.ceil(renderWidth * scale), + Math.ceil((height + 2 * strokeWidth) * scale)); label = context.canvas; ol.render.canvas.TextReplay.labelCache_.set(key, label); - context.scale(pixelRatio, pixelRatio); + context.scale(scale, scale); context.font = textState.font; if (stroke) { context.strokeStyle = strokeState.strokeStyle; - context.lineWidth = strokeWidth * (ol.has.SAFARI ? pixelRatio : 1); + context.lineWidth = strokeWidth * (ol.has.SAFARI ? scale : 1); context.lineCap = strokeState.lineCap; context.lineJoin = strokeState.lineJoin; context.miterLimit = strokeState.miterLimit; @@ -341,7 +342,7 @@ ol.render.canvas.TextReplay.prototype.getImage_ = function(text, fill, stroke) { context.textBaseline = 'top'; context.textAlign = 'center'; var leftRight = (0.5 - align); - var x = align * label.width / pixelRatio + leftRight * 2 * strokeWidth; + var x = align * label.width / scale + leftRight * 2 * strokeWidth; var i; if (stroke) { for (i = 0; i < numLines; ++i) { @@ -377,12 +378,12 @@ ol.render.canvas.TextReplay.prototype.drawTextImage_ = function(label, begin, en this.instructions.push([ol.render.canvas.Instruction.DRAW_IMAGE, begin, end, label, (anchorX - this.textOffsetX_) * pixelRatio, (anchorY - this.textOffsetY_) * pixelRatio, label.height, 1, 0, 0, this.textRotateWithView_, this.textRotation_, - this.textScale_, true, label.width + 1, true, label.width ]); this.hitDetectionInstructions.push([ol.render.canvas.Instruction.DRAW_IMAGE, begin, end, label, (anchorX - this.textOffsetX_) * pixelRatio, (anchorY - this.textOffsetY_) * pixelRatio, label.height, 1, 0, 0, this.textRotateWithView_, this.textRotation_, - this.textScale_ / pixelRatio, true, label.width + 1 / pixelRatio, true, label.width ]); }; @@ -423,14 +424,14 @@ ol.render.canvas.TextReplay.prototype.drawChars_ = function(begin, end) { this.instructions.push([ol.render.canvas.Instruction.DRAW_CHARS, begin, end, labels, baseline, textState.exceedLength, textState.maxAngle, - ol.render.canvas.TextReplay.getTextWidth.bind(widths, context, pixelRatio), - offsetY, this.text_, align, this.textScale_ + ol.render.canvas.TextReplay.getTextWidth.bind(widths, context, pixelRatio * this.textScale_), + offsetY, this.text_, align, 1 ]); this.hitDetectionInstructions.push([ol.render.canvas.Instruction.DRAW_CHARS, begin, end, labels, baseline, textState.exceedLength, textState.maxAngle, - ol.render.canvas.TextReplay.getTextWidth.bind(widths, context, 1), - offsetY, this.text_, align, this.textScale_ / pixelRatio + ol.render.canvas.TextReplay.getTextWidth.bind(widths, context, this.textScale_), + offsetY, this.text_, align, 1 / pixelRatio ]); }; @@ -530,7 +531,7 @@ ol.render.canvas.TextReplay.prototype.setTextStyle = function(textStyle) { strokeState.lineCap + strokeState.lineDashOffset + '|' + strokeState.lineWidth + strokeState.lineJoin + strokeState.miterLimit + '[' + strokeState.lineDash.join() + ']' : ''; - this.textKey_ = textState.font + textState.textAlign; + this.textKey_ = textState.font + (textState.textAlign || '?') + this.textScale_; this.fillKey_ = fillState ? (typeof fillState.fillStyle == 'string' ? fillState.fillStyle : ('|' + ol.getUid(fillState.fillStyle))) : ''; diff --git a/test/rendering/ol/style/expected/text-canvas-scale.png b/test/rendering/ol/style/expected/text-canvas-scale.png new file mode 100644 index 0000000000000000000000000000000000000000..a569c3e11596b0d1bda6bb9bf45ad3319417ac39 GIT binary patch literal 6722 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 Xhua$OUNHrp?Z)8g>gTe~DWM4fM&wKx literal 0 HcmV?d00001 diff --git a/test/rendering/ol/style/text.test.js b/test/rendering/ol/style/text.test.js index 8c561f947a..3d130150c4 100644 --- a/test/rendering/ol/style/text.test.js +++ b/test/rendering/ol/style/text.test.js @@ -46,15 +46,17 @@ describe('ol.rendering.style.Text', function() { describe('#render', function() { - function createFeatures() { + function createFeatures(opt_scale) { + var scale = opt_scale || 1; var feature; feature = new ol.Feature({ geometry: new ol.geom.Point([-20, 18]) }); feature.setStyle(new ol.style.Style({ text: new ol.style.Text({ + scale: scale, text: 'hello', - font: '10px' + font: '10px sans-serif' }) })); vectorSource.addFeature(feature); @@ -64,10 +66,11 @@ describe('ol.rendering.style.Text', function() { }); feature.setStyle(new ol.style.Style({ text: new ol.style.Text({ + scale: scale, text: 'hello', fill: new ol.style.Fill({ color: 'red', - font: '12px' + font: '12px sans-serif' }), stroke: new ol.style.Stroke({ color: '#000', @@ -82,9 +85,10 @@ describe('ol.rendering.style.Text', function() { }); feature.setStyle(new ol.style.Style({ text: new ol.style.Text({ + scale: scale, rotateWithView: true, text: 'hello', - font: '10px', + font: '10px sans-serif', stroke: new ol.style.Stroke({ color: [10, 10, 10, 0.5] }) @@ -100,7 +104,7 @@ describe('ol.rendering.style.Text', function() { var uglyPath = [163, 22, 159, 30, 150, 30, 143, 24, 151, 17]; var polygon = [151, 17, 163, 22, 159, 30, 150, 30, 143, 24, 151, 17]; - function createLineString(coords, textAlign, maxAngle, strokeColor, strokeWidth) { + function createLineString(coords, textAlign, maxAngle, strokeColor, strokeWidth, scale) { var geom = new ol.geom.LineString(); geom.setFlatCoordinates('XY', coords); var style = new ol.style.Style({ @@ -110,6 +114,7 @@ describe('ol.rendering.style.Text', function() { text: new ol.style.Text({ text: 'Hello world', font: 'bold 14px sans-serif', + scale: scale || 1, textAlign: textAlign, maxAngle: maxAngle, placement: 'line', @@ -161,6 +166,12 @@ describe('ol.rendering.style.Text', function() { expectResemble(map, 'rendering/ol/style/expected/text-canvas-hidpi.png', 2.8, done); }); + it('renders text correctly with scale != 1', function(done) { + createMap('canvas'); + createFeatures(3); + expectResemble(map, 'rendering/ol/style/expected/text-canvas-scale.png', 6, done); + }); + it('renders multiline text with alignment options', function(done) { createMap('canvas'); var feature; @@ -293,6 +304,12 @@ describe('ol.rendering.style.Text', function() { expectResemble(map, 'rendering/ol/style/expected/text-linestring-nice.png', 2.8, done); }); + it('renders text along a linestring with scale != 1', function(done) { + createMap('canvas'); + createLineString(nicePath, undefined, undefined, undefined, undefined, 2); + expectResemble(map, 'rendering/ol/style/expected/text-linestring-nice-scale.png', 8, done); + }); + it('aligns text along a linestring correctly with `textBaseline` option', function(done) { createMap('canvas'); createLineString(nicePath, undefined, undefined, 'cyan', 3); From d47dc523824c77309bcde4b44b1bb1f4625c01f2 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Thu, 12 Oct 2017 00:35:22 +0200 Subject: [PATCH 14/15] Calculate correct text box size --- src/ol/render/canvas/textreplay.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ol/render/canvas/textreplay.js b/src/ol/render/canvas/textreplay.js index 717b034ff7..fe3ae39e50 100644 --- a/src/ol/render/canvas/textreplay.js +++ b/src/ol/render/canvas/textreplay.js @@ -317,10 +317,10 @@ ol.render.canvas.TextReplay.prototype.getImage_ = function(text, fill, stroke) { var width = ol.render.canvas.TextReplay.measureTextWidths(textState.font, lines, widths); var lineHeight = ol.render.canvas.TextReplay.measureTextHeight(textState.font); var height = lineHeight * numLines; - var renderWidth = (width + 2 * strokeWidth); + var renderWidth = (width + strokeWidth); var context = ol.dom.createCanvasContext2D( Math.ceil(renderWidth * scale), - Math.ceil((height + 2 * strokeWidth) * scale)); + Math.ceil((height + strokeWidth) * scale)); label = context.canvas; ol.render.canvas.TextReplay.labelCache_.set(key, label); context.scale(scale, scale); @@ -342,16 +342,16 @@ ol.render.canvas.TextReplay.prototype.getImage_ = function(text, fill, stroke) { context.textBaseline = 'top'; context.textAlign = 'center'; var leftRight = (0.5 - align); - var x = align * label.width / scale + leftRight * 2 * strokeWidth; + var x = align * label.width / scale + leftRight * strokeWidth; var i; if (stroke) { for (i = 0; i < numLines; ++i) { - context.strokeText(lines[i], x + leftRight * widths[i], strokeWidth + i * lineHeight); + context.strokeText(lines[i], x + leftRight * widths[i], 0.5 * strokeWidth + i * lineHeight); } } if (fill) { for (i = 0; i < numLines; ++i) { - context.fillText(lines[i], x + leftRight * widths[i], strokeWidth + i * lineHeight); + context.fillText(lines[i], x + leftRight * widths[i], 0.5 * strokeWidth + i * lineHeight); } } } From f6eeee37d2e4b13f9ff65acb6b671d9d4c35b505 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Fri, 13 Oct 2017 14:48:25 -0600 Subject: [PATCH 15/15] Bumping versions and logging changes for v4.4.2 --- changelog/v4.4.2.md | 12 ++++++++++++ package.json | 2 +- package/package.json | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 changelog/v4.4.2.md diff --git a/changelog/v4.4.2.md b/changelog/v4.4.2.md new file mode 100644 index 0000000000..8710c18746 --- /dev/null +++ b/changelog/v4.4.2.md @@ -0,0 +1,12 @@ +# 4.4.2 + +The v4.4.2 release fixes a number of rendering issues in the 4.4 releases. + +## Fixes + + * [#7327](https://github.com/openlayers/openlayers/pull/7327) - Prune the tile cache after updating a source's URL ([@tschaub](https://github.com/tschaub)) + * [#7341](https://github.com/openlayers/openlayers/pull/7341) - Proper rendering of raster sources when there is a tile transition ([@tschaub](https://github.com/tschaub)) + * [#7339](https://github.com/openlayers/openlayers/pull/7339) - Use correct text stroke on HiDPI devices ([@ahocevar](https://github.com/ahocevar)) + * [#7345](https://github.com/openlayers/openlayers/pull/7345) - Handle different lineWidth scaling in Safari ([@ahocevar](https://github.com/ahocevar)) + * [#7346](https://github.com/openlayers/openlayers/pull/7346) - Pre-render text images for configured scale ([@ahocevar](https://github.com/ahocevar)) + * [#7350](https://github.com/openlayers/openlayers/pull/7350) - Calculate correct text box size ([@ahocevar](https://github.com/ahocevar)) diff --git a/package.json b/package.json index 48960df307..d27aeda50e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openlayers", - "version": "4.4.1", + "version": "4.4.2", "description": "Build tools and sources for developing OpenLayers based mapping applications", "keywords": [ "map", diff --git a/package/package.json b/package/package.json index a22edee852..28edbfe016 100644 --- a/package/package.json +++ b/package/package.json @@ -1,6 +1,6 @@ { "name": "ol", - "version": "4.4.1", + "version": "4.4.2", "description": "OpenLayers as ES2015 modules", "main": "index.js", "module": "index.js",