Merge pull request #5329 from tschaub/utfgrid

UTFGrid updates.
This commit is contained in:
Tim Schaub
2016-05-13 12:59:56 -06:00
6 changed files with 199 additions and 48 deletions

View File

@@ -4,6 +4,23 @@
This option is no longer needed, so it was removed from the API.
#### XHR loading for `ol.source.TileUTFGrid`
The `ol.source.TileUTFGrid` now uses XMLHttpRequest to load UTFGrid tiles by default. This works out of the box with the v4 Mapbox API. To work with the v3 API, you must use the new `jsonp` option on the source. See the examples below for detail.
```js
// To work with the v4 API
var v4source = new ol.source.TileUTFGrid({
url: 'https://api.tiles.mapbox.com/v4/example.json?access_token=' + YOUR_KEY_HERE
});
// To work with the v3 API
var v3source = new ol.source.TileUTFGrid({
jsonp: true, // <--- this is required for v3
url: 'http://api.tiles.mapbox.com/v3/example.json'
});
```
### v3.15.0
#### Internet Explorer 9 support

View File

@@ -6,6 +6,8 @@ docs: >
<p>Point to a country to see its name and flag.</p>
Tiles made with [TileMill](http://tilemill.com). Hosting on MapBox.com or with open-source [TileServer](https://github.com/klokantech/tileserver-php/).
tags: "utfgrid, tileutfgrid, tilejson"
cloak:
pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiRk1kMWZaSSJ9.E5BkluenyWQMsBLsuByrmg: Your Mapbox access token from http://mapbox.com/ here
---
<div id="map" class="map"></div>
<div style="display: none;">

View File

@@ -5,14 +5,17 @@ goog.require('ol.layer.Tile');
goog.require('ol.source.TileJSON');
goog.require('ol.source.TileUTFGrid');
var key = 'pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiRk1kMWZaSSJ9.E5BkluenyWQMsBLsuByrmg';
var mapLayer = new ol.layer.Tile({
source: new ol.source.TileJSON({
url: 'http://api.tiles.mapbox.com/v3/mapbox.geography-class.json'
url: 'http://api.tiles.mapbox.com/v4/mapbox.geography-class.json?access_token=' + key
})
});
var gridSource = new ol.source.TileUTFGrid({
url: 'http://api.tiles.mapbox.com/v3/mapbox.geography-class.json'
url: 'http://api.tiles.mapbox.com/v4/mapbox.geography-class.json?access_token=' + key
});
var gridLayer = new ol.layer.Tile({source: gridSource});

View File

@@ -4193,6 +4193,7 @@ olx.source.ClusterOptions.prototype.wrapX;
/**
* @typedef {{preemptive: (boolean|undefined),
* jsonp: (boolean|undefined),
* tileJSON: (TileJSON|undefined),
* url: (string|undefined)}}
* @api
@@ -4200,6 +4201,15 @@ olx.source.ClusterOptions.prototype.wrapX;
olx.source.TileUTFGridOptions;
/**
* Use JSONP with callback to load the TileJSON. Useful when the server
* does not support CORS. Default is `false`.
* @type {boolean|undefined}
* @api
*/
olx.source.TileUTFGridOptions.prototype.jsonp;
/**
* If `true` the TileUTFGrid source loads the tiles based on their "visibility".
* This improves the speed of response, but increases traffic.

View File

@@ -49,8 +49,23 @@ ol.source.TileUTFGrid = function(options) {
*/
this.template_ = undefined;
/**
* @private
* @type {boolean}
*/
this.jsonp_ = options.jsonp || false;
if (options.url) {
ol.net.jsonp(options.url, this.handleTileJSONResponse.bind(this));
if (this.jsonp_) {
ol.net.jsonp(options.url, this.handleTileJSONResponse.bind(this),
this.handleTileJSONError.bind(this));
} else {
var client = new XMLHttpRequest();
client.addEventListener('load', this.onXHRLoad_.bind(this));
client.addEventListener('error', this.onXHRError_.bind(this));
client.open('GET', options.url);
client.send();
}
} else if (options.tileJSON) {
this.handleTileJSONResponse(options.tileJSON);
} else {
@@ -60,6 +75,36 @@ ol.source.TileUTFGrid = function(options) {
goog.inherits(ol.source.TileUTFGrid, ol.source.Tile);
/**
* @private
* @param {Event} event The load event.
*/
ol.source.TileUTFGrid.prototype.onXHRLoad_ = function(event) {
var client = /** @type {XMLHttpRequest} */ (event.target);
if (client.status >= 200 && client.status < 300) {
var response;
try {
response = /** @type {TileJSON} */(JSON.parse(client.responseText));
} catch (err) {
this.handleTileJSONError();
return;
}
this.handleTileJSONResponse(response);
} else {
this.handleTileJSONError();
}
};
/**
* @private
* @param {Event} event The error event.
*/
ol.source.TileUTFGrid.prototype.onXHRError_ = function(event) {
this.handleTileJSONError();
};
/**
* Return the template from TileJSON.
* @return {string|undefined} The template from TileJSON.
@@ -76,7 +121,7 @@ ol.source.TileUTFGrid.prototype.getTemplate = function() {
* in case of an error).
* @param {ol.Coordinate} coordinate Coordinate.
* @param {number} resolution Resolution.
* @param {function(this: T, Object)} callback Callback.
* @param {function(this: T, *)} callback Callback.
* @param {T=} opt_this The object to use as `this` in the callback.
* @param {boolean=} opt_request If `true` the callback is always async.
* The tile data is requested if not yet loaded.
@@ -103,6 +148,14 @@ ol.source.TileUTFGrid.prototype.forDataAtCoordinateAndResolution = function(
};
/**
* @protected
*/
ol.source.TileUTFGrid.prototype.handleTileJSONError = function() {
this.setState(ol.source.State.ERROR);
};
/**
* TODO: very similar to ol.source.TileJSON#handleTileJSONResponse
* @protected
@@ -185,7 +238,8 @@ ol.source.TileUTFGrid.prototype.getTile = function(z, x, y, pixelRatio, projecti
tileUrl !== undefined ? ol.TileState.IDLE : ol.TileState.EMPTY,
tileUrl !== undefined ? tileUrl : '',
this.tileGrid.getTileCoordExtent(tileCoord),
this.preemptive_);
this.preemptive_,
this.jsonp_);
this.tileCache.set(tileCoordKey, tile);
return tile;
}
@@ -211,9 +265,10 @@ ol.source.TileUTFGrid.prototype.useTile = function(z, x, y) {
* @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).
* @param {boolean} jsonp Load the tile as a script.
* @private
*/
ol.source.TileUTFGridTile_ = function(tileCoord, state, src, extent, preemptive) {
ol.source.TileUTFGridTile_ = function(tileCoord, state, src, extent, preemptive, jsonp) {
goog.base(this, tileCoord, state);
@@ -252,6 +307,14 @@ ol.source.TileUTFGridTile_ = function(tileCoord, state, src, extent, preemptive)
* @type {Object.<string, Object>|undefined}
*/
this.data_ = null;
/**
* @private
* @type {boolean}
*/
this.jsonp_ = jsonp;
};
goog.inherits(ol.source.TileUTFGridTile_, ol.Tile);
@@ -270,10 +333,10 @@ ol.source.TileUTFGridTile_.prototype.getImage = function(opt_context) {
/**
* Synchronously returns data at given coordinate (if available).
* @param {ol.Coordinate} coordinate Coordinate.
* @return {Object} The data.
* @return {*} The data.
*/
ol.source.TileUTFGridTile_.prototype.getData = function(coordinate) {
if (!this.grid_ || !this.keys_ || !this.data_) {
if (!this.grid_ || !this.keys_) {
return null;
}
var xRelative = (coordinate[0] - this.extent_[0]) /
@@ -296,7 +359,16 @@ ol.source.TileUTFGridTile_.prototype.getData = function(coordinate) {
}
code -= 32;
return (code in this.keys_) ? this.data_[this.keys_[code]] : null;
var data = null;
if (code in this.keys_) {
var id = this.keys_[code];
if (this.data_ && id in this.data_) {
data = this.data_[id];
} else {
data = id;
}
}
return data;
};
@@ -304,7 +376,7 @@ ol.source.TileUTFGridTile_.prototype.getData = function(coordinate) {
* 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 {function(this: T, *)} callback Callback.
* @param {T=} opt_this The object to use as `this` in the callback.
* @param {boolean=} opt_request If `true` the callback is always async.
* The tile data is requested if not yet loaded.
@@ -365,12 +437,50 @@ ol.source.TileUTFGridTile_.prototype.handleLoad_ = function(json) {
ol.source.TileUTFGridTile_.prototype.loadInternal_ = function() {
if (this.state == ol.TileState.IDLE) {
this.state = ol.TileState.LOADING;
ol.net.jsonp(this.src_, this.handleLoad_.bind(this),
this.handleError_.bind(this));
if (this.jsonp_) {
ol.net.jsonp(this.src_, this.handleLoad_.bind(this),
this.handleError_.bind(this));
} else {
var client = new XMLHttpRequest();
client.addEventListener('load', this.onXHRLoad_.bind(this));
client.addEventListener('error', this.onXHRError_.bind(this));
client.open('GET', this.src_);
client.send();
}
}
};
/**
* @private
* @param {Event} event The load event.
*/
ol.source.TileUTFGridTile_.prototype.onXHRLoad_ = function(event) {
var client = /** @type {XMLHttpRequest} */ (event.target);
if (client.status >= 200 && client.status < 300) {
var response;
try {
response = /** @type {!UTFGridJSON} */(JSON.parse(client.responseText));
} catch (err) {
this.handleError_();
return;
}
this.handleLoad_(response);
} else {
this.handleError_();
}
};
/**
* @private
* @param {Event} event The error event.
*/
ol.source.TileUTFGridTile_.prototype.onXHRError_ = function(event) {
this.handleError_();
};
/**
* Load not yet loaded URI.
*/

View File

@@ -2,11 +2,10 @@ goog.provide('ol.test.source.TileUTFGrid');
describe('ol.source.TileUTFGrid', function() {
var url = 'spec/ol/data/tileutfgrid.json';
var tileJson = null;
// Called once for this describe section, this method loads a local
// tileutfgrid and stores it in a variable to compare it in the actual
// tests. This way we can test `handleTileJSONResponse` and also remove
// the dependency from the external service / URL.
// Load and parse the UTFGrid fixture
before(function(done) {
var client = new XMLHttpRequest();
client.addEventListener('load', function() {
@@ -14,68 +13,77 @@ describe('ol.source.TileUTFGrid', function() {
done();
});
client.addEventListener('error', function() {
done(new Error('Failed to fetch local tileutfgrid.json'))
done(new Error('Failed to fetch ' + url));
});
client.open('GET', 'spec/ol/data/tileutfgrid.json');
client.open('GET', url);
client.send();
});
after(function() {
tileJson = null;
});
var url = 'some-tileutfgrid-url';
function getTileUTFGrid() {
var source = new ol.source.TileUTFGrid({
return new ol.source.TileUTFGrid({
url: url
});
return source;
}
describe('constructor', function() {
it('needs to be constructed with url option', function() {
var source;
var source = new ol.source.TileUTFGrid({url: url});
expect(source).to.be.an(ol.source.TileUTFGrid);
expect(source).to.be.an(ol.source.Tile);
expect(function() {
// no options: will throw
source = new ol.source.TileUTFGrid();
return new ol.source.TileUTFGrid();
}).to.throwException();
expect(source).to.be(undefined);
expect(function() {
// no url-option: will throw
source = new ol.source.TileUTFGrid({});
return new ol.source.TileUTFGrid({});
}).to.throwException();
expect(source).to.be(undefined);
expect(function() {
// with a (bogus) url option all is fine
source = new ol.source.TileUTFGrid({
url: url
});
}).to.not.throwException();
expect(source).to.be.an(ol.source.TileUTFGrid);
expect(function() {
// also test our utility method
source = getTileUTFGrid();
}).to.not.throwException();
expect(source).to.be.an(ol.source.TileUTFGrid);
expect(getTileUTFGrid()).to.be.an(ol.source.TileUTFGrid);
});
it('immediately fetches the passed URL', function() {
// spy on the jsonp method
var jsonpSpy = sinon.spy(ol.net, 'jsonp');
});
getTileUTFGrid();
expect(jsonpSpy.calledOnce).to.be(true);
expect(jsonpSpy.lastCall.calledWith(url)).to.be(true);
describe('change event (ready)', function() {
it('is fired when the source is ready', function(done) {
var source = new ol.source.TileUTFGrid({
url: url
});
expect(source.getState()).to.be(ol.source.State.LOADING);
expect(source.tileGrid).to.be(null);
// cleanup
ol.net.jsonp.restore();
jsonpSpy = null;
source.on('change', function(event) {
if (source.getState() === ol.source.State.READY) {
expect(source.tileGrid).to.be.an(ol.tilegrid.TileGrid);
done();
}
});
});
});
describe('change event (error)', function(done) {
it('is fired when the source fails to initialize', function(done) {
var source = new ol.source.TileUTFGrid({
url: 'Bogus UTFGrid URL'
});
expect(source.getState()).to.be(ol.source.State.LOADING);
expect(source.tileGrid).to.be(null);
source.on('change', function(event) {
if (source.getState() === ol.source.State.ERROR) {
expect(source.tileGrid).to.be(null);
done();
}
});
});
});
describe('#handleTileJSONResponse', function() {
@@ -281,6 +289,7 @@ describe('ol.source.TileUTFGrid', function() {
goog.require('ol.net');
goog.require('ol.proj');
goog.require('ol.source.State');
goog.require('ol.source.Tile');
goog.require('ol.source.TileUTFGrid');
goog.require('ol.tilegrid.TileGrid');
goog.require('ol.TileState');