diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 215f8d1328..e66498c204 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -499,6 +499,19 @@ * of `url` when the WMS supports multiple urls for GetMap requests. */ +/** + * @typedef {Object} ol.source.VectorOptions + * @property {Array.|undefined} attributions Attributions. + * @property {Object|string|undefined} data Data to parse. + * @property {ol.Extent|undefined} extent Extent. + * @property {string|undefined} logo Logo. + * @property {ol.parser.Parser} parser Parser instance to parse data + * provided as `data` or fetched from `url`. + * @property {ol.ProjectionLike|undefined} projection Projection. EPSG:4326 + * is assumed if not defined. + * @property {string|undefined} url Server url providing the vector data. + */ + /** * @typedef {Object} ol.source.VectorSource2Options * @property {Array.|undefined} attributions Attributions. diff --git a/src/ol/source/vectorsource.exports b/src/ol/source/vectorsource.exports index 27cbc2bea0..d7842f5b84 100644 --- a/src/ol/source/vectorsource.exports +++ b/src/ol/source/vectorsource.exports @@ -1 +1 @@ -@exportClass ol.source.Vector ol.source.SourceOptions +@exportClass ol.source.Vector ol.source.VectorOptions diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js index 80a85b5d55..1f7b6d935f 100644 --- a/src/ol/source/vectorsource.js +++ b/src/ol/source/vectorsource.js @@ -1,5 +1,8 @@ goog.provide('ol.source.Vector'); +goog.require('goog.asserts'); +goog.require('goog.net.XhrIo'); +goog.require('ol.proj'); goog.require('ol.source.Source'); @@ -7,9 +10,88 @@ goog.require('ol.source.Source'); /** * @constructor * @extends {ol.source.Source} - * @param {ol.source.SourceOptions} options Source options. + * @param {ol.source.VectorOptions} options Vector source options. */ ol.source.Vector = function(options) { - goog.base(this, options); + + /** + * @private + * @type {Object|string} + */ + this.data_ = goog.isDef(options.data) ? options.data : null; + + /** + * @private + * @type {ol.source.VectorLoadState} + */ + this.loadState_ = ol.source.VectorLoadState.IDLE; + + /** + * @private + * @type {ol.parser.Parser} + */ + this.parser_ = goog.isDef(options.parser) ? options.parser : null; + + /** + * @private + * @type {string|undefined} + */ + this.url_ = options.url; + + goog.base(this, { + attributions: options.attributions, + extent: options.extent, + logo: options.logo, + projection: goog.isDef(options.projection) ? + options.projection : ol.proj.get('EPSG:4326') + }); }; goog.inherits(ol.source.Vector, ol.source.Source); + + +/** + * @param {ol.layer.Layer} layer Layer that parses the data. + * @param {ol.Extent} extent Extent that needs to be fetched. + * @param {ol.Projection} projection Projection of the view. + * @param {Function=} opt_callback Callback which is called when features are + * parsed after loading. + * @return {ol.source.VectorLoadState} The current load state. + */ +ol.source.Vector.prototype.prepareFeatures = function(layer, extent, projection, + opt_callback) { + // TODO: Implement strategies. BBOX aware strategies will need the extent. + if (goog.isDef(this.url_) && + this.loadState_ == ol.source.VectorLoadState.IDLE) { + this.loadState_ = ol.source.VectorLoadState.LOADING; + goog.net.XhrIo.send(this.url_, goog.bind(function(event) { + var xhr = event.target; + if (xhr.isSuccess()) { + // TODO: Get source projection from data if supported by parser. + layer.parseFeatures(xhr.getResponseText(), this.parser_, projection); + this.loadState_ = ol.source.VectorLoadState.LOADED; + if (goog.isDef(opt_callback)) { + opt_callback(); + } + } else { + // TODO: Error handling. + this.loadState_ = ol.source.VectorLoadState.ERROR; + } + }, this)); + } else if (!goog.isNull(this.data_)) { + layer.parseFeatures(this.data_, this.parser_, projection); + this.data_ = null; + this.loadState_ = ol.source.VectorLoadState.LOADED; + } + return this.loadState_; +}; + + +/** + * @enum {number} + */ +ol.source.VectorLoadState = { + IDLE: 0, + LOADING: 1, + LOADED: 2, + ERROR: 3 +}; diff --git a/test/spec/ol/source/vectorsource.test.js b/test/spec/ol/source/vectorsource.test.js index bef91ced07..ee40475a22 100644 --- a/test/spec/ol/source/vectorsource.test.js +++ b/test/spec/ol/source/vectorsource.test.js @@ -11,7 +11,77 @@ describe('ol.source.Vector', function() { }); }); + describe('#prepareFeatures', function() { + it('loads and parses data from a file', function(done) { + var source = new ol.source.Vector({ + url: 'spec/ol/parser/geojson/countries.geojson', + parser: new ol.parser.GeoJSON() + }); + var layer = new ol.layer.Vector({ + source: source + }); + source.prepareFeatures(layer, [-180, 180, -90, 90], + ol.proj.get('EPSG:4326'), + function() { + expect(source.loadState_).to.be(ol.source.VectorLoadState.LOADED); + expect(goog.object.getCount( + layer.featureCache_.getFeaturesObject())).to.be(179); + done(); + }); + }); + + it('parses inline data', function() { + var source = new ol.source.Vector({ + data: { + 'type': 'FeatureCollection', + 'features': [{ + 'type': 'Feature', + 'geometry': { + 'type': 'Point', + 'coordinates': [0, -6000000] + } + }, { + 'type': 'Feature', + 'geometry': { + 'type': 'Point', + 'coordinates': [-6000000, 0] + } + }, { + 'type': 'Feature', + 'geometry': { + 'type': 'Point', + 'coordinates': [0, 6000000] + } + }, { + 'type': 'Feature', + 'geometry': { + 'type': 'Point', + 'coordinates': [6000000, 0] + } + }] + }, + parser: new ol.parser.GeoJSON(), + projection: ol.proj.get('EPSG:4326') + }); + var layer = new ol.layer.Vector({ + source: source + }); + source.prepareFeatures(layer, [-180, 180, -90, 90], + ol.proj.get('EPSG:4326'), + function() { + expect(source.loadState_).to.be(ol.source.VectorLoadState.LOADED); + expect(goog.object.getCount( + layer.featureCache_.getFeaturesObject())).to.be(4); + done(); + }); + }); + }); + }); +goog.require('goog.object'); +goog.require('ol.layer.Vector'); +goog.require('ol.parser.GeoJSON'); +goog.require('ol.proj'); goog.require('ol.source.Source'); goog.require('ol.source.Vector');