diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index c9410a7a5d..422a3ded51 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -707,6 +707,26 @@ * @property {Array.|undefined} urls URLs. */ +/** + * @typedef {Object} olx.source.TileVectorOptions + * @property {Array.|undefined} attributions Attributions. + * @property {ol.proj.ProjectionLike} defaultProjection Default projection. + * @property {ol.Extent|undefined} extent Extent. + * @property {string|undefined} logo Logo. + * @property {GeoJSONObject|undefined} object Object. + * @property {ol.proj.ProjectionLike} projection Destination projection. If + * provided, features will be transformed to this projection. If not + * provided, features will not be transformed. + * @property {ol.tilegrid.TileGrid} tileGrid Tile grid. + * @property {ol.TileUrlFunctionType|undefined} tileUrlFunction Optional + * function to get tile URL given a tile coordinate and the projection. + * Required if url or urls are not provided. + * @property {string|undefined} url URL template. Must include `{x}`, `{y}`, + * and `{z}` placeholders. + * @property {Array.|undefined} urls An array of URL templates. + * @property {number} z Z value of tiles to request. FIXME remove! + */ + /** * @typedef {Object} olx.source.TopoJSONOptions * @property {Array.|undefined} attributions Attributions. diff --git a/src/ol/source/tilevectorsource.exports b/src/ol/source/tilevectorsource.exports new file mode 100644 index 0000000000..aca6bdb3e8 --- /dev/null +++ b/src/ol/source/tilevectorsource.exports @@ -0,0 +1 @@ +@exportSymbol ol.source.TileVector diff --git a/src/ol/source/tilevectorsource.js b/src/ol/source/tilevectorsource.js new file mode 100644 index 0000000000..f79b7686e1 --- /dev/null +++ b/src/ol/source/tilevectorsource.js @@ -0,0 +1,284 @@ +// FIXME implement getClosestFeatureToCoordinate +// FIXME implement getExtent +// FIXME handle different Zs + +goog.provide('ol.source.TileVector'); + +goog.require('goog.array'); +goog.require('goog.object'); +goog.require('ol.TileCoord'); +goog.require('ol.TileUrlFunction'); +goog.require('ol.source.FormatVector'); +goog.require('ol.source.State'); +goog.require('ol.tilegrid.TileGrid'); + + + +/** + * @constructor + * @extends {ol.source.FormatVector} + * @param {olx.source.TileVectorOptions} options Options. + */ +ol.source.TileVector = function(options) { + + goog.base(this, { + attributions: options.attributions, + extent: options.extent, + format: options.format, + logo: options.logo, + projection: options.projection + }); + + var tileGrid = options.tileGrid; + var z = options.z; + + /** + * @private + * @type {number} + */ + this.resolution_ = tileGrid.getResolution(z); + + /** + * @private + * @type {ol.tilegrid.TileGrid} + */ + this.tileGrid_ = options.tileGrid; + + /** + * @private + * @type {ol.TileUrlFunctionType} + */ + this.tileUrlFunction_ = ol.TileUrlFunction.nullTileUrlFunction; + + /** + * @private + * @type {ol.TileCoordTransformType} + */ + this.tileCoordTransform_ = tileGrid.createTileCoordTransform({ + extent: options.extent + }); + + /** + * @private + * @type {Object.>} + */ + this.tiles_ = {}; + + /** + * @private + * @type {number} + */ + this.z_ = z; + + if (goog.isDef(options.tileUrlFunction)) { + this.setTileUrlFunction(options.tileUrlFunction); + } else if (goog.isDef(options.urls)) { + this.setUrls(options.urls); + } else if (goog.isDef(options.url)) { + this.setUrl(options.url); + } + +}; +goog.inherits(ol.source.TileVector, ol.source.FormatVector); + + +/** + * @inheritDoc + */ +ol.source.TileVector.prototype.addFeature = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.source.TileVector.prototype.addFeatures = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.source.TileVector.prototype.clear = function() { + goog.object.clear(this.tiles_); +}; + + +/** + * @inheritDoc + */ +ol.source.TileVector.prototype.forEachFeature = function(f, opt_this) { + var tiles = this.tiles_; + var tileKey; + for (tileKey in tiles) { + var features = tiles[tileKey]; + var i, ii; + for (i = 0, ii = features.length; i < ii; ++i) { + var result = f.call(opt_this, features[i]); + if (result) { + return result; + } + } + } + return undefined; +}; + + +/** + * @inheritDoc + */ +ol.source.TileVector.prototype.forEachFeatureInExtent = + function(extent, f, opt_this) { + var resolution = this.resolution_; + var tileGrid = this.tileGrid_; + var tiles = this.tiles_; + var z = this.z_; + var tileRange = + tileGrid.getTileRangeForExtentAndResolution(extent, resolution); + var x, y; + for (x = tileRange.minX; x <= tileRange.maxX; ++x) { + for (y = tileRange.minY; y <= tileRange.maxY; ++y) { + var tileKey = this.getTileKeyZXY_(z, x, y); + var features = tiles[tileKey]; + if (goog.isDef(features)) { + var i, ii; + for (i = 0, ii = features.length; i < ii; ++i) { + var result = f.call(opt_this, features[i]); + if (result) { + return result; + } + } + } + } + } + return undefined; +}; + + +/** + * @inheritDoc + */ +ol.source.TileVector.prototype.getClosestFeatureToCoordinate = + goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.source.TileVector.prototype.getExtent = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.source.TileVector.prototype.getFeatures = function() { + var tiles = this.tiles_; + var features = []; + var tileKey; + for (tileKey in tiles) { + goog.array.extend(features, tiles[tileKey]); + } + return features; +}; + + +/** + * @inheritDoc + */ +ol.source.TileVector.prototype.getFeaturesInExtent = function(extent) { + var features = []; + this.forEachFeatureInExtent(extent, + /** + * @param {ol.Feature} feature Feature. + */ + function(feature) { + features.push(feature); + }); + return features; +}; + + +/** + * @param {number} z Z. + * @param {number} x X. + * @param {number} y Y. + * @private + * @return {string} Tile key. + */ +ol.source.TileVector.prototype.getTileKeyZXY_ = function(z, x, y) { + return z + '/' + x + '/' + y; +}; + + +/** + * @inheritDoc + */ +ol.source.TileVector.prototype.loadFeatures = + function(extent, resolution, projection) { + // FIXME should use resolution argument rather than this.resolution_ + var tileCoordTransform = this.tileCoordTransform_; + var tileGrid = this.tileGrid_; + var tileUrlFunction = this.tileUrlFunction_; + var tiles = this.tiles_; + var z = this.z_; + var tileRange = + tileGrid.getTileRangeForExtentAndResolution(extent, this.resolution_); + var tileCoord = new ol.TileCoord(z, 0, 0); + var x, y; + for (x = tileRange.minX; x <= tileRange.maxX; ++x) { + for (y = tileRange.minY; y <= tileRange.maxY; ++y) { + var tileKey = this.getTileKeyZXY_(z, x, y); + if (!(tileKey in tiles)) { + tileCoord.z = z; + tileCoord.x = x; + tileCoord.y = y; + tileCoordTransform(tileCoord, projection, tileCoord); + var url = tileUrlFunction(tileCoord, 1, projection); + if (goog.isDef(url)) { + tiles[tileKey] = []; + this.loadFeaturesFromURL(url, goog.partial( + /** + * @param {string} tileKey Tile key. + * @param {Array.} features Features. + * @this {ol.source.TileVector} + */ + function(tileKey, features) { + tiles[tileKey] = features; + this.setState(ol.source.State.READY); + }, tileKey), this); + } + } + } + } +}; + + +/** + * @inheritDoc + */ +ol.source.TileVector.prototype.removeFeature = goog.abstractMethod; + + +/** + * @param {ol.TileUrlFunctionType} tileUrlFunction Tile URL function. + */ +ol.source.TileVector.prototype.setTileUrlFunction = function(tileUrlFunction) { + this.tileUrlFunction_ = tileUrlFunction; + this.dispatchChangeEvent(); +}; + + +/** + * @param {string} url URL. + * @todo stability experimental + */ +ol.source.TileVector.prototype.setUrl = function(url) { + this.setTileUrlFunction(ol.TileUrlFunction.createFromTemplates( + ol.TileUrlFunction.expandUrl(url))); +}; + + +/** + * @param {Array.} urls URLs. + */ +ol.source.TileVector.prototype.setUrls = function(urls) { + this.setTileUrlFunction(ol.TileUrlFunction.createFromTemplates(urls)); +};