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');