From 794c7950c8a84b1a3eca65962761102401015b6c Mon Sep 17 00:00:00 2001 From: Petr Sloup Date: Thu, 25 Sep 2014 19:19:55 +0200 Subject: [PATCH 01/17] Added ol.source.TileUTFGrid --- externs/olx.js | 7 + src/ol/source/tileutfgridsource.js | 226 +++++++++++++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 src/ol/source/tileutfgridsource.js diff --git a/externs/olx.js b/externs/olx.js index 05013fc9b4..e6a6ff92ed 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -3541,6 +3541,13 @@ olx.source.GPXOptions.prototype.url; olx.source.GPXOptions.prototype.urls; +/** + * @typedef {{url: string}} + * @api + */ +olx.source.TileUTFGridOptions; + + /** * @typedef {{attributions: (Array.|undefined), * crossOrigin: (null|string|undefined), diff --git a/src/ol/source/tileutfgridsource.js b/src/ol/source/tileutfgridsource.js new file mode 100644 index 0000000000..470e3c9090 --- /dev/null +++ b/src/ol/source/tileutfgridsource.js @@ -0,0 +1,226 @@ +goog.provide('ol.source.TileUTFGrid'); + +goog.require('goog.net.Jsonp'); +goog.require('ol.Attribution'); +goog.require('ol.Tile'); +goog.require('ol.TileState'); +goog.require('ol.TileUrlFunction'); +goog.require('ol.extent'); +goog.require('ol.proj'); +goog.require('ol.source.Tile'); +goog.require('ol.tilegrid.TileGrid'); +goog.require('ol.tilegrid.XYZ'); + + + +/** + * @classdesc + * TODO: desc + * TODO: caching + * TODO: getTilePixelSize ? + * + * @constructor + * @extends {ol.source.Tile} + * @param {olx.source.TileUTFGridOptions} options Source options. + * @api + */ +ol.source.TileUTFGrid = function(options) { + goog.base(this, { + projection: ol.proj.get('EPSG:3857'), + state: ol.source.State.LOADING + }); + + /** + * @protected + * @type {ol.TileCache} + */ + this.tileCache = new ol.TileCache(); + + var request = new goog.net.Jsonp(options.url); + request.send(undefined, goog.bind(this.handleTileJSONResponse, this)); +}; +goog.inherits(ol.source.TileUTFGrid, ol.source.Tile); + + +/** + * @inheritDoc + */ +ol.source.TileUTFGrid.prototype.canExpireCache = function() { + return this.tileCache.canExpireCache(); +}; + + +/** + * @inheritDoc + */ +ol.source.TileUTFGrid.prototype.expireCache = function(usedTiles) { + this.tileCache.expireCache(usedTiles); +}; + + +/** + * TODO: very similar to ol.source.TileJSON#handleTileJSONResponse + * @protected + * @param {TileJSON} tileJSON Tile JSON. + */ +ol.source.TileUTFGrid.prototype.handleTileJSONResponse = function(tileJSON) { + + var epsg4326Projection = ol.proj.get('EPSG:4326'); + + var sourceProjection = this.getProjection(); + var extent; + if (goog.isDef(tileJSON.bounds)) { + var transform = ol.proj.getTransformFromProjections( + epsg4326Projection, sourceProjection); + extent = ol.extent.applyTransform(tileJSON.bounds, transform); + } + + if (goog.isDef(tileJSON.scheme)) { + goog.asserts.assert(tileJSON.scheme == 'xyz'); + } + var minZoom = tileJSON.minzoom || 0; + var maxZoom = tileJSON.maxzoom || 22; + var tileGrid = new ol.tilegrid.XYZ({ + extent: ol.tilegrid.extentFromProjection(sourceProjection), + maxZoom: maxZoom, + minZoom: minZoom + }); + this.tileGrid = tileGrid; + + this.tileUrlFunction = ol.TileUrlFunction.withTileCoordTransform( + tileGrid.createTileCoordTransform({ + extent: extent + }), + ol.TileUrlFunction.createFromTemplates(tileJSON.grids)); + + if (goog.isDef(tileJSON.attribution)) { + var attributionExtent = goog.isDef(extent) ? + extent : epsg4326Projection.getExtent(); + /** @type {Object.>} */ + var tileRanges = {}; + var z, zKey; + for (z = minZoom; z <= maxZoom; ++z) { + zKey = z.toString(); + tileRanges[zKey] = + [tileGrid.getTileRangeForExtentAndZ(attributionExtent, z)]; + } + this.setAttributions([ + new ol.Attribution({ + html: tileJSON.attribution, + tileRanges: tileRanges + }) + ]); + } + + this.setState(ol.source.State.READY); + +}; + + +/** + * @inheritDoc + */ +ol.source.TileUTFGrid.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 = [z, x, y]; + var tileUrl = this.tileUrlFunction(tileCoord, pixelRatio, projection); + var tile = new ol.source.TileUTFGridTile_( + tileCoord, + goog.isDef(tileUrl) ? ol.TileState.IDLE : ol.TileState.EMPTY, + goog.isDef(tileUrl) ? tileUrl : ''); + this.tileCache.set(tileCoordKey, tile); + return tile; + } +}; + + +/** + * @inheritDoc + */ +ol.source.TileUTFGrid.prototype.useTile = function(z, x, y) { + var tileCoordKey = this.getKeyZXY(z, x, y); + if (this.tileCache.containsKey(tileCoordKey)) { + this.tileCache.get(tileCoordKey); + } +}; + + + +/** + * @constructor + * @extends {ol.Tile} + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {ol.TileState} state State. + * @param {string} src Image source URI. + * @private + */ +ol.source.TileUTFGridTile_ = function(tileCoord, state, src) { + + goog.base(this, tileCoord, state); + + /** + * @private + * @type {string} + */ + this.src_ = src; + + /** + * @private + * @type {?Object} + */ + this.data_ = null; +}; +goog.inherits(ol.source.TileUTFGridTile_, ol.Tile); + + +/** + * @inheritDoc + */ +ol.source.TileUTFGridTile_.prototype.getImage = goog.nullFunction; + + +/** + * @inheritDoc + */ +ol.source.TileUTFGridTile_.prototype.getKey = function() { + return this.src_; +}; + + +/** + * @private + */ +ol.source.TileUTFGridTile_.prototype.handleError_ = function() { + this.state = ol.TileState.ERROR; + this.changed(); +}; + + +/** + * @param {Object} json + * @private + */ +ol.source.TileUTFGridTile_.prototype.handleLoad_ = function(json) { + this.data_ = json; + + this.state = ol.TileState.EMPTY; + this.changed(); +}; + + +/** + * Load not yet loaded URI. + */ +ol.source.TileUTFGridTile_.prototype.load = function() { + if (this.state == ol.TileState.IDLE) { + this.state = ol.TileState.LOADING; + var request = new goog.net.Jsonp(this.src_); + request.send(undefined, goog.bind(this.handleLoad_, this), + goog.bind(this.handleError_, this)); + } +}; From 641a829918a24cf00c3df280c91bc1672dd04f5a Mon Sep 17 00:00:00 2001 From: Petr Sloup Date: Thu, 25 Sep 2014 19:59:31 +0200 Subject: [PATCH 02/17] Incomplete getFeatureInfo implementation --- src/ol/source/tileutfgridsource.js | 32 ++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/ol/source/tileutfgridsource.js b/src/ol/source/tileutfgridsource.js index 470e3c9090..e05fb186b8 100644 --- a/src/ol/source/tileutfgridsource.js +++ b/src/ol/source/tileutfgridsource.js @@ -16,8 +16,8 @@ goog.require('ol.tilegrid.XYZ'); /** * @classdesc * TODO: desc - * TODO: caching * TODO: getTilePixelSize ? + * TODO: lazy loading * * @constructor * @extends {ol.source.Tile} @@ -58,6 +58,27 @@ ol.source.TileUTFGrid.prototype.expireCache = function(usedTiles) { }; +/** + * @param {ol.Coordinate} coordinate Coordinate. + * @param {number} resolution Resolution. + * @param {function(Object)} callback Info callback. + */ +ol.source.TileUTFGrid.prototype.getFeatureInfo = function( + coordinate, resolution, callback) { + if (!goog.isNull(this.tileGrid)) { + var tileCoord = this.tileGrid.getTileCoordForCoordAndResolution( + coordinate, resolution); + var tileCoordKey = this.getKeyZXY.apply(this, tileCoord); + if (this.tileCache.containsKey(tileCoordKey)) { + var tile = /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey)); + callback(tile.getData()); + } else { + //TODO: async? + } + } +}; + + /** * TODO: very similar to ol.source.TileJSON#handleTileJSONResponse * @protected @@ -126,7 +147,6 @@ ol.source.TileUTFGrid.prototype.getTile = if (this.tileCache.containsKey(tileCoordKey)) { return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey)); } else { - goog.asserts.assert(projection); var tileCoord = [z, x, y]; var tileUrl = this.tileUrlFunction(tileCoord, pixelRatio, projection); var tile = new ol.source.TileUTFGridTile_( @@ -184,6 +204,14 @@ goog.inherits(ol.source.TileUTFGridTile_, ol.Tile); ol.source.TileUTFGridTile_.prototype.getImage = goog.nullFunction; +/** + * @return {Object} + */ +ol.source.TileUTFGridTile_.prototype.getData = function() { //TODO: coordinate + return this.data_; +}; + + /** * @inheritDoc */ From 9f7e94ec85fb89c0ae89dbaaaf7a2779a4c64f09 Mon Sep 17 00:00:00 2001 From: Petr Sloup Date: Fri, 26 Sep 2014 14:43:38 +0200 Subject: [PATCH 03/17] Added UTFGridJSON to the TileJSON externs --- externs/tilejson.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/externs/tilejson.js b/externs/tilejson.js index 6e982cc325..00ca83a8ee 100644 --- a/externs/tilejson.js +++ b/externs/tilejson.js @@ -1,6 +1,7 @@ /** * @externs * @see https://github.com/mapbox/tilejson-spec + * @see https://github.com/mapbox/utfgrid-spec */ @@ -93,3 +94,28 @@ TileJSON.prototype.bounds; * @type {!Array.|undefined} */ TileJSON.prototype.center; + + + +/** + * @constructor + */ +var UTFGridJSON = function() {}; + + +/** + * @type {!Array.} + */ +UTFGridJSON.prototype.grid; + + +/** + * @type {!Array.} + */ +UTFGridJSON.prototype.keys; + + +/** + * @type {!Object.|undefined} + */ +UTFGridJSON.prototype.data; From 99eab24b20bb32f339a4a317d7b6ea14f218e606 Mon Sep 17 00:00:00 2001 From: Petr Sloup Date: Fri, 26 Sep 2014 14:48:05 +0200 Subject: [PATCH 04/17] Implementation of the ol.source.TileUTFGridTile_#getData method --- src/ol/source/tileutfgridsource.js | 77 +++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/src/ol/source/tileutfgridsource.js b/src/ol/source/tileutfgridsource.js index e05fb186b8..727a4a391a 100644 --- a/src/ol/source/tileutfgridsource.js +++ b/src/ol/source/tileutfgridsource.js @@ -1,14 +1,16 @@ goog.provide('ol.source.TileUTFGrid'); +goog.require('goog.asserts') goog.require('goog.net.Jsonp'); goog.require('ol.Attribution'); goog.require('ol.Tile'); +goog.require('ol.TileCache') goog.require('ol.TileState'); goog.require('ol.TileUrlFunction'); goog.require('ol.extent'); goog.require('ol.proj'); +goog.require('ol.source.State') goog.require('ol.source.Tile'); -goog.require('ol.tilegrid.TileGrid'); goog.require('ol.tilegrid.XYZ'); @@ -62,16 +64,18 @@ ol.source.TileUTFGrid.prototype.expireCache = function(usedTiles) { * @param {ol.Coordinate} coordinate Coordinate. * @param {number} resolution Resolution. * @param {function(Object)} callback Info callback. + * @api */ ol.source.TileUTFGrid.prototype.getFeatureInfo = function( coordinate, resolution, callback) { if (!goog.isNull(this.tileGrid)) { var tileCoord = this.tileGrid.getTileCoordForCoordAndResolution( - coordinate, resolution); + coordinate, resolution); var tileCoordKey = this.getKeyZXY.apply(this, tileCoord); if (this.tileCache.containsKey(tileCoordKey)) { - var tile = /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey)); - callback(tile.getData()); + var tile = /** @type {!ol.source.TileUTFGridTile_} */ + (this.tileCache.get(tileCoordKey)); + callback(tile.getData(coordinate)); } else { //TODO: async? } @@ -152,7 +156,8 @@ ol.source.TileUTFGrid.prototype.getTile = var tile = new ol.source.TileUTFGridTile_( tileCoord, goog.isDef(tileUrl) ? ol.TileState.IDLE : ol.TileState.EMPTY, - goog.isDef(tileUrl) ? tileUrl : ''); + goog.isDef(tileUrl) ? tileUrl : '', + this.tileGrid.getTileCoordExtent(tileCoord)); this.tileCache.set(tileCoordKey, tile); return tile; } @@ -177,9 +182,10 @@ ol.source.TileUTFGrid.prototype.useTile = function(z, x, y) { * @param {ol.TileCoord} tileCoord Tile coordinate. * @param {ol.TileState} state State. * @param {string} src Image source URI. + * @param {ol.Extent} extent Extent of the tile. * @private */ -ol.source.TileUTFGridTile_ = function(tileCoord, state, src) { +ol.source.TileUTFGridTile_ = function(tileCoord, state, src, extent) { goog.base(this, tileCoord, state); @@ -191,7 +197,25 @@ ol.source.TileUTFGridTile_ = function(tileCoord, state, src) { /** * @private - * @type {?Object} + * @type {ol.Extent} + */ + this.extent_ = extent; + + /** + * @private + * @type {?Array.} + */ + this.grid_ = null; + + /** + * @private + * @type {?Array.} + */ + this.keys_ = null; + + /** + * @private + * @type {?Object.|undefined} */ this.data_ = null; }; @@ -205,10 +229,37 @@ ol.source.TileUTFGridTile_.prototype.getImage = goog.nullFunction; /** - * @return {Object} + * @param {ol.Coordinate} coordinate Coordinate. + * @return {Object|undefined} */ -ol.source.TileUTFGridTile_.prototype.getData = function() { //TODO: coordinate - return this.data_; +ol.source.TileUTFGridTile_.prototype.getData = function(coordinate) { + if (goog.isNull(this.grid_) || goog.isNull(this.keys_) || + goog.isNull(this.data_)) { + return undefined; + } + var xRelative = (coordinate[0] - this.extent_[0]) / + (this.extent_[2] - this.extent_[0]); + var yRelative = (coordinate[1] - this.extent_[1]) / + (this.extent_[3] - this.extent_[1]); + + var row = this.grid_[Math.floor((1 - yRelative) * this.grid_.length)]; + + if (!goog.isString(row)) { + return undefined; + } + + var code = row.charCodeAt(Math.floor(xRelative * row.length)); + if (code >= 93) code--; + if (code >= 35) code--; + code -= 32; + + var key = this.keys_[code]; + + if (!goog.isDefAndNotNull(key)) { + return undefined; + } + + return this.data_[key]; }; @@ -230,11 +281,13 @@ ol.source.TileUTFGridTile_.prototype.handleError_ = function() { /** - * @param {Object} json + * @param {!UTFGridJSON} json * @private */ ol.source.TileUTFGridTile_.prototype.handleLoad_ = function(json) { - this.data_ = json; + this.grid_ = json.grid; + this.keys_ = json.keys; + this.data_ = json.data; this.state = ol.TileState.EMPTY; this.changed(); From ef816d30ed9163eac93761abbf2307c386e3c9bc Mon Sep 17 00:00:00 2001 From: Petr Sloup Date: Fri, 26 Sep 2014 17:16:07 +0200 Subject: [PATCH 05/17] The preemptive loading of UTFGrid tiles is now optional --- externs/olx.js | 3 +- src/ol/source/tileutfgridsource.js | 112 ++++++++++++++++++++++------- 2 files changed, 87 insertions(+), 28 deletions(-) diff --git a/externs/olx.js b/externs/olx.js index e6a6ff92ed..f9606851f6 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -3542,7 +3542,8 @@ olx.source.GPXOptions.prototype.urls; /** - * @typedef {{url: string}} + * @typedef {{url: string, + * preemptive: (boolean|undefined)}} * @api */ olx.source.TileUTFGridOptions; diff --git a/src/ol/source/tileutfgridsource.js b/src/ol/source/tileutfgridsource.js index 727a4a391a..058c8963cf 100644 --- a/src/ol/source/tileutfgridsource.js +++ b/src/ol/source/tileutfgridsource.js @@ -1,15 +1,17 @@ goog.provide('ol.source.TileUTFGrid'); -goog.require('goog.asserts') +goog.require('goog.asserts'); +goog.require('goog.events'); +goog.require('goog.events.EventType'); goog.require('goog.net.Jsonp'); goog.require('ol.Attribution'); goog.require('ol.Tile'); -goog.require('ol.TileCache') +goog.require('ol.TileCache'); goog.require('ol.TileState'); goog.require('ol.TileUrlFunction'); goog.require('ol.extent'); goog.require('ol.proj'); -goog.require('ol.source.State') +goog.require('ol.source.State'); goog.require('ol.source.Tile'); goog.require('ol.tilegrid.XYZ'); @@ -17,9 +19,7 @@ goog.require('ol.tilegrid.XYZ'); /** * @classdesc - * TODO: desc - * TODO: getTilePixelSize ? - * TODO: lazy loading + * Layer source for UTFGrid interaction data loaded from TileJSON format. * * @constructor * @extends {ol.source.Tile} @@ -32,6 +32,19 @@ ol.source.TileUTFGrid = function(options) { state: ol.source.State.LOADING }); + /** + * @private + * @type {boolean} + */ + this.preemptive_ = goog.isDef(options.preemptive) ? + options.preemptive : false; + + /** + * @protected + * @type {ol.TileUrlFunctionType} + */ + this.tileUrlFunction = ol.TileUrlFunction.nullTileUrlFunction; + /** * @protected * @type {ol.TileCache} @@ -63,22 +76,20 @@ ol.source.TileUTFGrid.prototype.expireCache = function(usedTiles) { /** * @param {ol.Coordinate} coordinate Coordinate. * @param {number} resolution Resolution. - * @param {function(Object)} callback Info callback. + * @param {function(this: T, (Object|undefined))} f Callback. + * @param {T=} opt_this The object to use as `this` in `f`. + * @param {boolean=} opt_noRequest Only process already loaded data. + * @template T * @api */ -ol.source.TileUTFGrid.prototype.getFeatureInfo = function( - coordinate, resolution, callback) { +ol.source.TileUTFGrid.prototype.forDataAtCoordinateAndResolution = function( + coordinate, resolution, f, opt_this, opt_noRequest) { if (!goog.isNull(this.tileGrid)) { var tileCoord = this.tileGrid.getTileCoordForCoordAndResolution( coordinate, resolution); - var tileCoordKey = this.getKeyZXY.apply(this, tileCoord); - if (this.tileCache.containsKey(tileCoordKey)) { - var tile = /** @type {!ol.source.TileUTFGridTile_} */ - (this.tileCache.get(tileCoordKey)); - callback(tile.getData(coordinate)); - } else { - //TODO: async? - } + var tile = /** @type {!ol.source.TileUTFGridTile_} */(this.getTile( + tileCoord[0], tileCoord[1], tileCoord[2], 1, this.getProjection())); + tile.forDataAtCoordinate(coordinate, f, opt_this, opt_noRequest); } }; @@ -112,11 +123,17 @@ ol.source.TileUTFGrid.prototype.handleTileJSONResponse = function(tileJSON) { }); this.tileGrid = tileGrid; + var grids = tileJSON.grids; + if (!goog.isDefAndNotNull(grids)) { + this.setState(ol.source.State.ERROR); + return; + } + this.tileUrlFunction = ol.TileUrlFunction.withTileCoordTransform( tileGrid.createTileCoordTransform({ extent: extent }), - ol.TileUrlFunction.createFromTemplates(tileJSON.grids)); + ol.TileUrlFunction.createFromTemplates(grids)); if (goog.isDef(tileJSON.attribution)) { var attributionExtent = goog.isDef(extent) ? @@ -151,13 +168,15 @@ ol.source.TileUTFGrid.prototype.getTile = if (this.tileCache.containsKey(tileCoordKey)) { return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey)); } else { + goog.asserts.assert(projection); var tileCoord = [z, x, y]; var tileUrl = this.tileUrlFunction(tileCoord, pixelRatio, projection); var tile = new ol.source.TileUTFGridTile_( tileCoord, goog.isDef(tileUrl) ? ol.TileState.IDLE : ol.TileState.EMPTY, goog.isDef(tileUrl) ? tileUrl : '', - this.tileGrid.getTileCoordExtent(tileCoord)); + this.tileGrid.getTileCoordExtent(tileCoord), + this.preemptive_); this.tileCache.set(tileCoordKey, tile); return tile; } @@ -183,9 +202,11 @@ ol.source.TileUTFGrid.prototype.useTile = function(z, x, y) { * @param {ol.TileState} state State. * @param {string} src Image source URI. * @param {ol.Extent} extent Extent of the tile. + * @param {boolean} preemptive Load the tile when visible (before it's needed). * @private */ -ol.source.TileUTFGridTile_ = function(tileCoord, state, src, extent) { +ol.source.TileUTFGridTile_ = + function(tileCoord, state, src, extent, preemptive) { goog.base(this, tileCoord, state); @@ -201,6 +222,12 @@ ol.source.TileUTFGridTile_ = function(tileCoord, state, src, extent) { */ this.extent_ = extent; + /** + * @private + * @type {boolean} + */ + this.preemptive_ = preemptive; + /** * @private * @type {?Array.} @@ -225,10 +252,13 @@ goog.inherits(ol.source.TileUTFGridTile_, ol.Tile); /** * @inheritDoc */ -ol.source.TileUTFGridTile_.prototype.getImage = goog.nullFunction; +ol.source.TileUTFGridTile_.prototype.getImage = function(opt_context) { + return null; +}; /** + * Synchronously returns data at given coordinate (if available). * @param {ol.Coordinate} coordinate Coordinate. * @return {Object|undefined} */ @@ -255,11 +285,29 @@ ol.source.TileUTFGridTile_.prototype.getData = function(coordinate) { var key = this.keys_[code]; - if (!goog.isDefAndNotNull(key)) { - return undefined; - } + return goog.isDefAndNotNull(key) ? this.data_[key] : undefined; +}; - return this.data_[key]; + +/** + * Calls `f` when the data for given coordinate is available. + * @param {ol.Coordinate} coordinate Coordinate. + * @param {function(this: T, (Object|undefined))} f Callback. + * @param {T=} opt_this The object to use as `this` in `f`. + * @param {boolean=} opt_noRequest If not loaded, callback with `undefined` + * without loading the tile. + * @template T + */ +ol.source.TileUTFGridTile_.prototype.forDataAtCoordinate = + function(coordinate, f, opt_this, opt_noRequest) { + if (this.state == ol.TileState.IDLE && opt_noRequest !== true) { + this.listenOnce(goog.events.EventType.CHANGE, function(e) { + f.call(opt_this, this.getData(coordinate)); + }, false, this); + this.loadInternal_(); + } else { + f.call(opt_this, this.getData(coordinate)); + } }; @@ -295,9 +343,9 @@ ol.source.TileUTFGridTile_.prototype.handleLoad_ = function(json) { /** - * Load not yet loaded URI. + * @private */ -ol.source.TileUTFGridTile_.prototype.load = function() { +ol.source.TileUTFGridTile_.prototype.loadInternal_ = function() { if (this.state == ol.TileState.IDLE) { this.state = ol.TileState.LOADING; var request = new goog.net.Jsonp(this.src_); @@ -305,3 +353,13 @@ ol.source.TileUTFGridTile_.prototype.load = function() { goog.bind(this.handleError_, this)); } }; + + +/** + * Load not yet loaded URI. + */ +ol.source.TileUTFGridTile_.prototype.load = function() { + if (this.preemptive_) { + this.loadInternal_(); + } +}; From 4b605d40d9ed01abf0810c7ce4abbd4f40ddc598 Mon Sep 17 00:00:00 2001 From: Petr Sloup Date: Tue, 30 Sep 2014 11:43:24 +0200 Subject: [PATCH 06/17] getTemplate method to get the Mustache template from the TileJSON --- src/ol/source/tileutfgridsource.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/ol/source/tileutfgridsource.js b/src/ol/source/tileutfgridsource.js index 058c8963cf..16d3caa068 100644 --- a/src/ol/source/tileutfgridsource.js +++ b/src/ol/source/tileutfgridsource.js @@ -51,6 +51,12 @@ ol.source.TileUTFGrid = function(options) { */ this.tileCache = new ol.TileCache(); + /** + * @private + * @type {string|undefined} + */ + this.template_ = undefined; + var request = new goog.net.Jsonp(options.url); request.send(undefined, goog.bind(this.handleTileJSONResponse, this)); }; @@ -73,6 +79,15 @@ ol.source.TileUTFGrid.prototype.expireCache = function(usedTiles) { }; +/** + * @return {string|undefined} The template from TileJSON. + * @api + */ +ol.source.TileUTFGrid.prototype.getTemplate = function() { + return this.template_; +}; + + /** * @param {ol.Coordinate} coordinate Coordinate. * @param {number} resolution Resolution. @@ -123,6 +138,8 @@ ol.source.TileUTFGrid.prototype.handleTileJSONResponse = function(tileJSON) { }); this.tileGrid = tileGrid; + this.template_ = tileJSON.template; + var grids = tileJSON.grids; if (!goog.isDefAndNotNull(grids)) { this.setState(ol.source.State.ERROR); From c8bc3620d35e8b5310055668e2a0dc7e67babf97 Mon Sep 17 00:00:00 2001 From: Petr Sloup Date: Fri, 5 Dec 2014 17:33:30 +0100 Subject: [PATCH 07/17] Added TileUTFGrid example --- examples/tileutfgrid.html | 58 +++++++++++++++++++++++++++++++++++++++ examples/tileutfgrid.js | 46 +++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 examples/tileutfgrid.html create mode 100644 examples/tileutfgrid.js diff --git a/examples/tileutfgrid.html b/examples/tileutfgrid.html new file mode 100644 index 0000000000..e2141f747c --- /dev/null +++ b/examples/tileutfgrid.html @@ -0,0 +1,58 @@ + + + + + + + + + + + TileUTFGrid example + + + + + +
+ +
+
+
+
+
+ +
+ +
+

TileUTFGrid example

+

This example shows how to read data from a TileUTFGrid layer.

+
+

Tiles made with TileMill. Hosting on MapBox.com or with open-source TileServer.

+

See the tileutfgrid.js source to see how this is done.

+
+
utfgrid, tileutfgrid, tilejson
+
+
+
+ +
 
+
+
+ +
+ +
+ + + + + + + diff --git a/examples/tileutfgrid.js b/examples/tileutfgrid.js new file mode 100644 index 0000000000..d0240e3728 --- /dev/null +++ b/examples/tileutfgrid.js @@ -0,0 +1,46 @@ +goog.require('ol.Map'); +goog.require('ol.View'); +goog.require('ol.layer.Tile'); +goog.require('ol.source.TileJSON'); +goog.require('ol.source.TileUTFGrid'); + +var mapLayer = new ol.layer.Tile({ + source: new ol.source.TileJSON({ + url: 'http://api.tiles.mapbox.com/v3/mapbox.geography-class.json' + }) +}); + +var gridSource = new ol.source.TileUTFGrid({ + url: 'http://api.tiles.mapbox.com/v3/mapbox.geography-class.json', + preemptive: true +}); + +var gridLayer = new ol.layer.Tile({source: gridSource}); + +var view = new ol.View({ + center: [0, 0], + zoom: 1 +}); + +var mapElement = document.getElementById('map'); +var map = new ol.Map({ + layers: [mapLayer, gridLayer], + target: mapElement, + view: view +}); + +var flag = document.getElementById('flag'); +var admin_name = document.getElementById('admin_name'); +map.on('pointermove', function(evt) { + var viewResolution = /** @type {number} */ (view.getResolution()); + gridSource.forDataAtCoordinateAndResolution(evt.coordinate, viewResolution, + function(data) { + // If you want to use the template from the TileJSON, + // load the mustache.js library separately and call + // info.innerHTML = Mustache.render(gridSource.getTemplate(), data); + mapElement.style.cursor = data ? 'pointer' : ''; + flag.src = data ? 'data:image/png;base64,' + data['flag_png'] : ''; + flag.style.visibility = data ? 'visible' : 'hidden'; + admin_name.innerHTML = data ? data['admin'] : ' '; + }); +}); From 363ac54509b8937ca168c27ccc0099c85354ed85 Mon Sep 17 00:00:00 2001 From: Petr Sloup Date: Fri, 5 Dec 2014 20:21:06 +0100 Subject: [PATCH 08/17] Suppress warning about dot notation (compiler would report undefined properties) --- examples/tileutfgrid.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/tileutfgrid.js b/examples/tileutfgrid.js index d0240e3728..5f0fca89a8 100644 --- a/examples/tileutfgrid.js +++ b/examples/tileutfgrid.js @@ -39,8 +39,10 @@ map.on('pointermove', function(evt) { // load the mustache.js library separately and call // info.innerHTML = Mustache.render(gridSource.getTemplate(), data); mapElement.style.cursor = data ? 'pointer' : ''; + /* jshint -W069 */ flag.src = data ? 'data:image/png;base64,' + data['flag_png'] : ''; flag.style.visibility = data ? 'visible' : 'hidden'; admin_name.innerHTML = data ? data['admin'] : ' '; + /* jshint +W069 */ }); }); From 250a1e4da3f93d665ca633a3087113b609669fc6 Mon Sep 17 00:00:00 2001 From: Petr Sloup Date: Sat, 6 Dec 2014 14:13:22 +0100 Subject: [PATCH 09/17] Various code style fixes --- examples/tileutfgrid.js | 4 +-- src/ol/source/tileutfgridsource.js | 40 ++++++++++++++++-------------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/examples/tileutfgrid.js b/examples/tileutfgrid.js index 5f0fca89a8..cfaf33e972 100644 --- a/examples/tileutfgrid.js +++ b/examples/tileutfgrid.js @@ -30,7 +30,7 @@ var map = new ol.Map({ }); var flag = document.getElementById('flag'); -var admin_name = document.getElementById('admin_name'); +var adminName = document.getElementById('admin_name'); map.on('pointermove', function(evt) { var viewResolution = /** @type {number} */ (view.getResolution()); gridSource.forDataAtCoordinateAndResolution(evt.coordinate, viewResolution, @@ -42,7 +42,7 @@ map.on('pointermove', function(evt) { /* jshint -W069 */ flag.src = data ? 'data:image/png;base64,' + data['flag_png'] : ''; flag.style.visibility = data ? 'visible' : 'hidden'; - admin_name.innerHTML = data ? data['admin'] : ' '; + adminName.innerHTML = data ? data['admin'] : ' '; /* jshint +W069 */ }); }); diff --git a/src/ol/source/tileutfgridsource.js b/src/ol/source/tileutfgridsource.js index 16d3caa068..7043902816 100644 --- a/src/ol/source/tileutfgridsource.js +++ b/src/ol/source/tileutfgridsource.js @@ -91,20 +91,20 @@ ol.source.TileUTFGrid.prototype.getTemplate = function() { /** * @param {ol.Coordinate} coordinate Coordinate. * @param {number} resolution Resolution. - * @param {function(this: T, (Object|undefined))} f Callback. - * @param {T=} opt_this The object to use as `this` in `f`. + * @param {function(this: T, (Object|undefined))} callback Callback. + * @param {T=} opt_this The object to use as `this` in the callback. * @param {boolean=} opt_noRequest Only process already loaded data. * @template T * @api */ ol.source.TileUTFGrid.prototype.forDataAtCoordinateAndResolution = function( - coordinate, resolution, f, opt_this, opt_noRequest) { + coordinate, resolution, callback, opt_this, opt_noRequest) { if (!goog.isNull(this.tileGrid)) { var tileCoord = this.tileGrid.getTileCoordForCoordAndResolution( coordinate, resolution); var tile = /** @type {!ol.source.TileUTFGridTile_} */(this.getTile( tileCoord[0], tileCoord[1], tileCoord[2], 1, this.getProjection())); - tile.forDataAtCoordinate(coordinate, f, opt_this, opt_noRequest); + tile.forDataAtCoordinate(coordinate, callback, opt_this, opt_noRequest); } }; @@ -247,19 +247,19 @@ ol.source.TileUTFGridTile_ = /** * @private - * @type {?Array.} + * @type {Array.} */ this.grid_ = null; /** * @private - * @type {?Array.} + * @type {Array.} */ this.keys_ = null; /** * @private - * @type {?Object.|undefined} + * @type {Object.|undefined} */ this.data_ = null; }; @@ -285,9 +285,9 @@ ol.source.TileUTFGridTile_.prototype.getData = function(coordinate) { return undefined; } var xRelative = (coordinate[0] - this.extent_[0]) / - (this.extent_[2] - this.extent_[0]); + (this.extent_[2] - this.extent_[0]); var yRelative = (coordinate[1] - this.extent_[1]) / - (this.extent_[3] - this.extent_[1]); + (this.extent_[3] - this.extent_[1]); var row = this.grid_[Math.floor((1 - yRelative) * this.grid_.length)]; @@ -296,8 +296,12 @@ ol.source.TileUTFGridTile_.prototype.getData = function(coordinate) { } var code = row.charCodeAt(Math.floor(xRelative * row.length)); - if (code >= 93) code--; - if (code >= 35) code--; + if (code >= 93) { + code--; + } + if (code >= 35) { + code--; + } code -= 32; var key = this.keys_[code]; @@ -307,23 +311,23 @@ ol.source.TileUTFGridTile_.prototype.getData = function(coordinate) { /** - * Calls `f` when the data for given coordinate is available. + * Calls the callback when the data for given coordinate is available. * @param {ol.Coordinate} coordinate Coordinate. - * @param {function(this: T, (Object|undefined))} f Callback. - * @param {T=} opt_this The object to use as `this` in `f`. + * @param {function(this: T, (Object|undefined))} callback Callback. + * @param {T=} opt_this The object to use as `this` in the callback. * @param {boolean=} opt_noRequest If not loaded, callback with `undefined` * without loading the tile. * @template T */ ol.source.TileUTFGridTile_.prototype.forDataAtCoordinate = - function(coordinate, f, opt_this, opt_noRequest) { + function(coordinate, callback, opt_this, opt_noRequest) { if (this.state == ol.TileState.IDLE && opt_noRequest !== true) { - this.listenOnce(goog.events.EventType.CHANGE, function(e) { - f.call(opt_this, this.getData(coordinate)); + goog.events.listenOnce(this, goog.events.EventType.CHANGE, function(e) { + callback.call(opt_this, this.getData(coordinate)); }, false, this); this.loadInternal_(); } else { - f.call(opt_this, this.getData(coordinate)); + callback.call(opt_this, this.getData(coordinate)); } }; From ac144d934f93caefe095a7376459dd9e604ddc95 Mon Sep 17 00:00:00 2001 From: Petr Sloup Date: Sat, 6 Dec 2014 14:19:56 +0100 Subject: [PATCH 10/17] Documentation blocks for the option members --- externs/olx.js | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/externs/olx.js b/externs/olx.js index f9606851f6..9d240642b5 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -3542,13 +3542,30 @@ olx.source.GPXOptions.prototype.urls; /** - * @typedef {{url: string, - * preemptive: (boolean|undefined)}} + * @typedef {{preemptive: (boolean|undefined), + * url: string}} * @api */ olx.source.TileUTFGridOptions; +/** + * If true, the TileUTFGrid source loads the tiles based on their "visibility". + * This can be used to improve the speed of response, but increases traffic. + * Default is `false`. + * @type {boolean|undefined} + * @api + */ +olx.source.TileUTFGridOptions.prototype.preemptive; + + +/** + * @type {string} + * @api + */ +olx.source.TileUTFGridOptions.prototype.url; + + /** * @typedef {{attributions: (Array.|undefined), * crossOrigin: (null|string|undefined), From ca77a20b577a2416736bbc94223f5bee48e0108f Mon Sep 17 00:00:00 2001 From: Petr Sloup Date: Sat, 6 Dec 2014 15:07:52 +0100 Subject: [PATCH 11/17] Improved TileUTFGrid example --- examples/tileutfgrid.html | 25 ++++++++++++++++++------- examples/tileutfgrid.js | 26 +++++++++++++++++++------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/examples/tileutfgrid.html b/examples/tileutfgrid.html index e2141f747c..23c39c577c 100644 --- a/examples/tileutfgrid.html +++ b/examples/tileutfgrid.html @@ -8,6 +8,14 @@ + TileUTFGrid example @@ -30,26 +38,29 @@
-
+

TileUTFGrid example

This example shows how to read data from a TileUTFGrid layer.

+

Point to a country to see its name and flag.

Tiles made with TileMill. Hosting on MapBox.com or with open-source TileServer.

See the tileutfgrid.js source to see how this is done.

utfgrid, tileutfgrid, tilejson
-
-
- -
 
-
-
+
+ +
+
 
+ +
+
+ diff --git a/examples/tileutfgrid.js b/examples/tileutfgrid.js index cfaf33e972..66e864714e 100644 --- a/examples/tileutfgrid.js +++ b/examples/tileutfgrid.js @@ -1,4 +1,5 @@ goog.require('ol.Map'); +goog.require('ol.Overlay'); goog.require('ol.View'); goog.require('ol.layer.Tile'); goog.require('ol.source.TileJSON'); @@ -29,8 +30,17 @@ var map = new ol.Map({ view: view }); -var flag = document.getElementById('flag'); -var adminName = document.getElementById('admin_name'); +var infoElement = document.getElementById('country-info'); +var flagElement = document.getElementById('country-flag'); +var nameElement = document.getElementById('country-name'); + +var infoOverlay = new ol.Overlay({ + element: infoElement, + offset: [15, 15], + stopEvent: false +}); +map.addOverlay(infoOverlay); + map.on('pointermove', function(evt) { var viewResolution = /** @type {number} */ (view.getResolution()); gridSource.forDataAtCoordinateAndResolution(evt.coordinate, viewResolution, @@ -39,10 +49,12 @@ map.on('pointermove', function(evt) { // load the mustache.js library separately and call // info.innerHTML = Mustache.render(gridSource.getTemplate(), data); mapElement.style.cursor = data ? 'pointer' : ''; - /* jshint -W069 */ - flag.src = data ? 'data:image/png;base64,' + data['flag_png'] : ''; - flag.style.visibility = data ? 'visible' : 'hidden'; - adminName.innerHTML = data ? data['admin'] : ' '; - /* jshint +W069 */ + if (data) { + /* jshint -W069 */ + flagElement.src = 'data:image/png;base64,' + data['flag_png']; + nameElement.innerHTML = data['admin']; + /* jshint +W069 */ + } + infoOverlay.setPosition(data ? evt.coordinate : undefined); }); }); From 48478c6868521b40f159c2b3ee5073d328e91e1e Mon Sep 17 00:00:00 2001 From: Petr Sloup Date: Sat, 6 Dec 2014 15:21:14 +0100 Subject: [PATCH 12/17] Listen 'mousemove' and 'click' rather than 'pointermove' in the TileUTFGrid example --- examples/tileutfgrid.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/examples/tileutfgrid.js b/examples/tileutfgrid.js index 66e864714e..d95a2a92db 100644 --- a/examples/tileutfgrid.js +++ b/examples/tileutfgrid.js @@ -41,9 +41,9 @@ var infoOverlay = new ol.Overlay({ }); map.addOverlay(infoOverlay); -map.on('pointermove', function(evt) { +var displayCountryInfo = function(coordinate) { var viewResolution = /** @type {number} */ (view.getResolution()); - gridSource.forDataAtCoordinateAndResolution(evt.coordinate, viewResolution, + gridSource.forDataAtCoordinateAndResolution(coordinate, viewResolution, function(data) { // If you want to use the template from the TileJSON, // load the mustache.js library separately and call @@ -55,6 +55,15 @@ map.on('pointermove', function(evt) { nameElement.innerHTML = data['admin']; /* jshint +W069 */ } - infoOverlay.setPosition(data ? evt.coordinate : undefined); + infoOverlay.setPosition(data ? coordinate : undefined); }); +}; + +$(map.getViewport()).on('mousemove', function(evt) { + var coordinate = map.getEventCoordinate(evt.originalEvent); + displayCountryInfo(coordinate); +}); + +map.on('click', function(evt) { + displayCountryInfo(evt.coordinate); }); From b03b762500dda21c9347476410b787483a85a182 Mon Sep 17 00:00:00 2001 From: Petr Sloup Date: Sat, 6 Dec 2014 15:26:49 +0100 Subject: [PATCH 13/17] More properties made private --- src/ol/source/tileutfgridsource.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/ol/source/tileutfgridsource.js b/src/ol/source/tileutfgridsource.js index 7043902816..3d829d1583 100644 --- a/src/ol/source/tileutfgridsource.js +++ b/src/ol/source/tileutfgridsource.js @@ -40,16 +40,16 @@ ol.source.TileUTFGrid = function(options) { options.preemptive : false; /** - * @protected - * @type {ol.TileUrlFunctionType} + * @private + * @type {!ol.TileUrlFunctionType} */ - this.tileUrlFunction = ol.TileUrlFunction.nullTileUrlFunction; + this.tileUrlFunction_ = ol.TileUrlFunction.nullTileUrlFunction; /** - * @protected - * @type {ol.TileCache} + * @private + * @type {!ol.TileCache} */ - this.tileCache = new ol.TileCache(); + this.tileCache_ = new ol.TileCache(); /** * @private @@ -67,7 +67,7 @@ goog.inherits(ol.source.TileUTFGrid, ol.source.Tile); * @inheritDoc */ ol.source.TileUTFGrid.prototype.canExpireCache = function() { - return this.tileCache.canExpireCache(); + return this.tileCache_.canExpireCache(); }; @@ -75,7 +75,7 @@ ol.source.TileUTFGrid.prototype.canExpireCache = function() { * @inheritDoc */ ol.source.TileUTFGrid.prototype.expireCache = function(usedTiles) { - this.tileCache.expireCache(usedTiles); + this.tileCache_.expireCache(usedTiles); }; @@ -146,7 +146,7 @@ ol.source.TileUTFGrid.prototype.handleTileJSONResponse = function(tileJSON) { return; } - this.tileUrlFunction = ol.TileUrlFunction.withTileCoordTransform( + this.tileUrlFunction_ = ol.TileUrlFunction.withTileCoordTransform( tileGrid.createTileCoordTransform({ extent: extent }), @@ -182,19 +182,19 @@ ol.source.TileUTFGrid.prototype.handleTileJSONResponse = function(tileJSON) { ol.source.TileUTFGrid.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)); + if (this.tileCache_.containsKey(tileCoordKey)) { + return /** @type {!ol.Tile} */ (this.tileCache_.get(tileCoordKey)); } else { goog.asserts.assert(projection); var tileCoord = [z, x, y]; - var tileUrl = this.tileUrlFunction(tileCoord, pixelRatio, projection); + var tileUrl = this.tileUrlFunction_(tileCoord, pixelRatio, projection); var tile = new ol.source.TileUTFGridTile_( tileCoord, goog.isDef(tileUrl) ? ol.TileState.IDLE : ol.TileState.EMPTY, goog.isDef(tileUrl) ? tileUrl : '', this.tileGrid.getTileCoordExtent(tileCoord), this.preemptive_); - this.tileCache.set(tileCoordKey, tile); + this.tileCache_.set(tileCoordKey, tile); return tile; } }; @@ -205,8 +205,8 @@ ol.source.TileUTFGrid.prototype.getTile = */ ol.source.TileUTFGrid.prototype.useTile = function(z, x, y) { var tileCoordKey = this.getKeyZXY(z, x, y); - if (this.tileCache.containsKey(tileCoordKey)) { - this.tileCache.get(tileCoordKey); + if (this.tileCache_.containsKey(tileCoordKey)) { + this.tileCache_.get(tileCoordKey); } }; From d28ffc4808df656a6f582553f9bff697574b4e50 Mon Sep 17 00:00:00 2001 From: Petr Sloup Date: Sat, 6 Dec 2014 15:42:40 +0100 Subject: [PATCH 14/17] Use null instead of undefined in the callback --- src/ol/source/tileutfgridsource.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ol/source/tileutfgridsource.js b/src/ol/source/tileutfgridsource.js index 3d829d1583..716b2ab4ff 100644 --- a/src/ol/source/tileutfgridsource.js +++ b/src/ol/source/tileutfgridsource.js @@ -91,7 +91,7 @@ ol.source.TileUTFGrid.prototype.getTemplate = function() { /** * @param {ol.Coordinate} coordinate Coordinate. * @param {number} resolution Resolution. - * @param {function(this: T, (Object|undefined))} callback Callback. + * @param {function(this: T, Object)} callback Callback. * @param {T=} opt_this The object to use as `this` in the callback. * @param {boolean=} opt_noRequest Only process already loaded data. * @template T @@ -277,12 +277,12 @@ ol.source.TileUTFGridTile_.prototype.getImage = function(opt_context) { /** * Synchronously returns data at given coordinate (if available). * @param {ol.Coordinate} coordinate Coordinate. - * @return {Object|undefined} + * @return {Object} */ ol.source.TileUTFGridTile_.prototype.getData = function(coordinate) { if (goog.isNull(this.grid_) || goog.isNull(this.keys_) || goog.isNull(this.data_)) { - return undefined; + return null; } var xRelative = (coordinate[0] - this.extent_[0]) / (this.extent_[2] - this.extent_[0]); @@ -292,7 +292,7 @@ ol.source.TileUTFGridTile_.prototype.getData = function(coordinate) { var row = this.grid_[Math.floor((1 - yRelative) * this.grid_.length)]; if (!goog.isString(row)) { - return undefined; + return null; } var code = row.charCodeAt(Math.floor(xRelative * row.length)); @@ -306,16 +306,16 @@ ol.source.TileUTFGridTile_.prototype.getData = function(coordinate) { var key = this.keys_[code]; - return goog.isDefAndNotNull(key) ? this.data_[key] : undefined; + return goog.isDefAndNotNull(key) ? this.data_[key] : null; }; /** * Calls the callback when the data for given coordinate is available. * @param {ol.Coordinate} coordinate Coordinate. - * @param {function(this: T, (Object|undefined))} callback Callback. + * @param {function(this: T, Object)} callback Callback. * @param {T=} opt_this The object to use as `this` in the callback. - * @param {boolean=} opt_noRequest If not loaded, callback with `undefined` + * @param {boolean=} opt_noRequest If not loaded, callback with `null` * without loading the tile. * @template T */ From 82a30b4cd538d126811f81170932244fabd5ad3c Mon Sep 17 00:00:00 2001 From: Petr Sloup Date: Fri, 12 Dec 2014 11:47:35 +0100 Subject: [PATCH 15/17] Modified the sync/async behavior logic of the forDataAtCoordinate* methods --- src/ol/source/tileutfgridsource.js | 38 +++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/ol/source/tileutfgridsource.js b/src/ol/source/tileutfgridsource.js index 716b2ab4ff..545864d976 100644 --- a/src/ol/source/tileutfgridsource.js +++ b/src/ol/source/tileutfgridsource.js @@ -1,6 +1,7 @@ goog.provide('ol.source.TileUTFGrid'); goog.require('goog.asserts'); +goog.require('goog.async.nextTick'); goog.require('goog.events'); goog.require('goog.events.EventType'); goog.require('goog.net.Jsonp'); @@ -89,22 +90,34 @@ ol.source.TileUTFGrid.prototype.getTemplate = function() { /** + * Calls the callback (synchronously by default) with the available data + * for given coordinate and resolution (or `null` if not yet loaded or + * in case of an error). * @param {ol.Coordinate} coordinate Coordinate. * @param {number} resolution Resolution. * @param {function(this: T, Object)} callback Callback. * @param {T=} opt_this The object to use as `this` in the callback. - * @param {boolean=} opt_noRequest Only process already loaded data. + * @param {boolean=} opt_request If `true` the callback is always async. + * The tile data is requested if not yet loaded. * @template T * @api */ ol.source.TileUTFGrid.prototype.forDataAtCoordinateAndResolution = function( - coordinate, resolution, callback, opt_this, opt_noRequest) { + coordinate, resolution, callback, opt_this, opt_request) { if (!goog.isNull(this.tileGrid)) { var tileCoord = this.tileGrid.getTileCoordForCoordAndResolution( coordinate, resolution); var tile = /** @type {!ol.source.TileUTFGridTile_} */(this.getTile( tileCoord[0], tileCoord[1], tileCoord[2], 1, this.getProjection())); - tile.forDataAtCoordinate(coordinate, callback, opt_this, opt_noRequest); + tile.forDataAtCoordinate(coordinate, callback, opt_this, opt_request); + } else { + if (opt_request == true) { + goog.async.nextTick(function() { + callback.call(opt_this, null); + }); + } else { + callback.call(opt_this, null); + } } }; @@ -311,23 +324,30 @@ ol.source.TileUTFGridTile_.prototype.getData = function(coordinate) { /** - * Calls the callback when the data for given coordinate is available. + * Calls the callback (synchronously by default) with the available data + * for given coordinate (or `null` if not yet loaded). * @param {ol.Coordinate} coordinate Coordinate. * @param {function(this: T, Object)} callback Callback. * @param {T=} opt_this The object to use as `this` in the callback. - * @param {boolean=} opt_noRequest If not loaded, callback with `null` - * without loading the tile. + * @param {boolean=} opt_request If `true` the callback is always async. + * The tile data is requested if not yet loaded. * @template T */ ol.source.TileUTFGridTile_.prototype.forDataAtCoordinate = - function(coordinate, callback, opt_this, opt_noRequest) { - if (this.state == ol.TileState.IDLE && opt_noRequest !== true) { + function(coordinate, callback, opt_this, opt_request) { + if (this.state == ol.TileState.IDLE && opt_request == true) { goog.events.listenOnce(this, goog.events.EventType.CHANGE, function(e) { callback.call(opt_this, this.getData(coordinate)); }, false, this); this.loadInternal_(); } else { - callback.call(opt_this, this.getData(coordinate)); + if (opt_request == true) { + goog.async.nextTick(function() { + callback.call(opt_this, this.getData(coordinate)); + }, this); + } else { + callback.call(opt_this, this.getData(coordinate)); + } } }; From 7bcd925cdb9f96a4f4fef07f2b9c82760b82e301 Mon Sep 17 00:00:00 2001 From: Petr Sloup Date: Fri, 12 Dec 2014 11:55:55 +0100 Subject: [PATCH 16/17] Minor code style fix --- src/ol/source/tileutfgridsource.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ol/source/tileutfgridsource.js b/src/ol/source/tileutfgridsource.js index 545864d976..40dbbf055e 100644 --- a/src/ol/source/tileutfgridsource.js +++ b/src/ol/source/tileutfgridsource.js @@ -111,7 +111,7 @@ ol.source.TileUTFGrid.prototype.forDataAtCoordinateAndResolution = function( tileCoord[0], tileCoord[1], tileCoord[2], 1, this.getProjection())); tile.forDataAtCoordinate(coordinate, callback, opt_this, opt_request); } else { - if (opt_request == true) { + if (opt_request === true) { goog.async.nextTick(function() { callback.call(opt_this, null); }); @@ -335,13 +335,13 @@ ol.source.TileUTFGridTile_.prototype.getData = function(coordinate) { */ ol.source.TileUTFGridTile_.prototype.forDataAtCoordinate = function(coordinate, callback, opt_this, opt_request) { - if (this.state == ol.TileState.IDLE && opt_request == true) { + if (this.state == ol.TileState.IDLE && opt_request === true) { goog.events.listenOnce(this, goog.events.EventType.CHANGE, function(e) { callback.call(opt_this, this.getData(coordinate)); }, false, this); this.loadInternal_(); } else { - if (opt_request == true) { + if (opt_request === true) { goog.async.nextTick(function() { callback.call(opt_this, this.getData(coordinate)); }, this); From 65583a8b774071aef2edb020267a3691db947d34 Mon Sep 17 00:00:00 2001 From: Petr Sloup Date: Fri, 12 Dec 2014 12:19:20 +0100 Subject: [PATCH 17/17] preemptive is now true by default + improved documentation --- examples/tileutfgrid.js | 3 +-- externs/olx.js | 9 ++++++--- src/ol/source/tileutfgridsource.js | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/tileutfgrid.js b/examples/tileutfgrid.js index d95a2a92db..b6462e37dd 100644 --- a/examples/tileutfgrid.js +++ b/examples/tileutfgrid.js @@ -12,8 +12,7 @@ var mapLayer = new ol.layer.Tile({ }); var gridSource = new ol.source.TileUTFGrid({ - url: 'http://api.tiles.mapbox.com/v3/mapbox.geography-class.json', - preemptive: true + url: 'http://api.tiles.mapbox.com/v3/mapbox.geography-class.json' }); var gridLayer = new ol.layer.Tile({source: gridSource}); diff --git a/externs/olx.js b/externs/olx.js index 9d240642b5..d2ce77c48a 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -3550,9 +3550,12 @@ olx.source.TileUTFGridOptions; /** - * If true, the TileUTFGrid source loads the tiles based on their "visibility". - * This can be used to improve the speed of response, but increases traffic. - * Default is `false`. + * If `true` the TileUTFGrid source loads the tiles based on their "visibility". + * This improves the speed of response, but increases traffic. + * Note that if set to `false`, you need to pass `true` as `opt_request` + * to the `forDataAtCoordinateAndResolution` method otherwise no data + * will ever be loaded. + * Default is `true`. * @type {boolean|undefined} * @api */ diff --git a/src/ol/source/tileutfgridsource.js b/src/ol/source/tileutfgridsource.js index 40dbbf055e..4a58b2bdfa 100644 --- a/src/ol/source/tileutfgridsource.js +++ b/src/ol/source/tileutfgridsource.js @@ -38,7 +38,7 @@ ol.source.TileUTFGrid = function(options) { * @type {boolean} */ this.preemptive_ = goog.isDef(options.preemptive) ? - options.preemptive : false; + options.preemptive : true; /** * @private