diff --git a/examples/vector-osm.js b/examples/vector-osm.js
index 7f870fd26e..5834ef230e 100644
--- a/examples/vector-osm.js
+++ b/examples/vector-osm.js
@@ -1,13 +1,17 @@
goog.require('ol.Map');
goog.require('ol.View2D');
+goog.require('ol.format.OSMXML');
goog.require('ol.layer.Tile');
goog.require('ol.layer.Vector');
+goog.require('ol.loadingstrategy');
+goog.require('ol.proj');
goog.require('ol.source.BingMaps');
-goog.require('ol.source.OSMXML');
+goog.require('ol.source.ServerVector');
goog.require('ol.style.Circle');
goog.require('ol.style.Fill');
goog.require('ol.style.Stroke');
goog.require('ol.style.Style');
+goog.require('ol.tilegrid.XYZ');
var styles = {
'amenity': {
@@ -83,9 +87,21 @@ var styles = {
}
};
-var vectorSource = new ol.source.OSMXML({
- projection: 'EPSG:3857',
- url: 'data/osm/map.osm'
+var vectorSource = new ol.source.ServerVector({
+ format: new ol.format.OSMXML(),
+ loader: function(extent, resolution, projection) {
+ var transform = ol.proj.getTransform(projection, 'EPSG:4326');
+ var epsg4326Extent = transform(extent, []);
+ var url = 'http://overpass-api.de/api/xapi?map?bbox=' +
+ epsg4326Extent.join(',');
+ $.ajax(url).then(function(response) {
+ vectorSource.addFeatures(vectorSource.readFeatures(response));
+ });
+ },
+ strategy: ol.loadingstrategy.createTile(new ol.tilegrid.XYZ({
+ maxZoom: 19
+ })),
+ projection: 'EPSG:3857'
});
var vector = new ol.layer.Vector({
@@ -117,6 +133,7 @@ var map = new ol.Map({
target: document.getElementById('map'),
view: new ol.View2D({
center: [739218, 5906096],
+ maxZoom: 19,
zoom: 17
})
});
diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc
index 3083d5e950..f3b1a22684 100644
--- a/src/objectliterals.jsdoc
+++ b/src/objectliterals.jsdoc
@@ -668,6 +668,15 @@
* @todo stability experimental
*/
+/**
+ * @typedef {Object} olx.source.FormatVectorOptions
+ * @property {Array.
|undefined} attributions Attributions.
+ * @property {ol.Extent|undefined} extent Extent.
+ * @property {ol.format.Feature} format Format.
+ * @property {string|undefined} logo Logo.
+ * @property {ol.proj.ProjectionLike} projection Projection.
+ */
+
/**
* @typedef {Object} olx.source.GeoJSONOptions
* @property {Array.|undefined} attributions Attributions.
@@ -698,6 +707,25 @@
* @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.
+ */
+
/**
* @typedef {Object} olx.source.TopoJSONOptions
* @property {Array.|undefined} attributions Attributions.
@@ -895,6 +923,17 @@
* @property {string} url Url.
*/
+/**
+ * @typedef {Object} olx.source.ServerVectorOptions
+ * @property {Array.|undefined} attributions Attributions.
+ * @property {ol.Extent|undefined} extent Extent.
+ * @property {ol.format.Feature} format Format.
+ * @property {function(this: ol.source.ServerVector, ol.Extent, number, ol.proj.Projection)} loader Loading function.
+ * @property {function(ol.Extent, number): Array.|undefined} strategy Loading strategy. Default is `ol.loadingstrategy.bbox`.
+ * @property {string|undefined} logo Logo.
+ * @property {ol.proj.ProjectionLike} projection Projection.
+ */
+
/**
* @typedef {Object} olx.source.TileJSONOptions
* @property {null|string|undefined} crossOrigin crossOrigin setting for image
@@ -952,7 +991,7 @@
*/
/**
- * @typedef {Object} olx.source.VectorFileOptions
+ * @typedef {Object} olx.source.StaticVectorOptions
* @property {ArrayBuffer|undefined} arrayBuffer Array buffer.
* @property {Array.|undefined} attributions Attributions.
* @property {Document|undefined} doc Document.
diff --git a/src/ol/extent.js b/src/ol/extent.js
index ba146637fc..10f36db01d 100644
--- a/src/ol/extent.js
+++ b/src/ol/extent.js
@@ -597,6 +597,17 @@ ol.extent.isEmpty = function(extent) {
};
+/**
+ * @param {ol.Extent} extent Extent.
+ * @return {boolean} Is infinite.
+ * @todo stability experimental
+ */
+ol.extent.isInfinite = function(extent) {
+ return extent[0] == -Infinity || extent[1] == -Infinity ||
+ extent[2] == Infinity || extent[3] == Infinity;
+};
+
+
/**
* @param {ol.Extent} extent Extent.
* @param {ol.Coordinate} coordinate Coordinate.
diff --git a/src/ol/format/osmxmlformat.exports b/src/ol/format/osmxmlformat.exports
new file mode 100644
index 0000000000..e9bb1a6835
--- /dev/null
+++ b/src/ol/format/osmxmlformat.exports
@@ -0,0 +1 @@
+@exportSymbol ol.format.OSMXML
diff --git a/src/ol/loadingstrategy.exports b/src/ol/loadingstrategy.exports
new file mode 100644
index 0000000000..711262d776
--- /dev/null
+++ b/src/ol/loadingstrategy.exports
@@ -0,0 +1,3 @@
+@exportSymbol ol.loadingstrategy.all
+@exportSymbol ol.loadingstrategy.bbox
+@exportSymbol ol.loadingstrategy.createTile
diff --git a/src/ol/loadingstrategy.js b/src/ol/loadingstrategy.js
new file mode 100644
index 0000000000..58a5dd4b3b
--- /dev/null
+++ b/src/ol/loadingstrategy.js
@@ -0,0 +1,52 @@
+goog.provide('ol.loadingstrategy');
+
+goog.require('ol.TileCoord');
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @return {Array.} Extents.
+ */
+ol.loadingstrategy.all = function(extent, resolution) {
+ return [[-Infinity, -Infinity, Infinity, Infinity]];
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @return {Array.} Extents.
+ */
+ol.loadingstrategy.bbox = function(extent, resolution) {
+ return [extent];
+};
+
+
+/**
+ * @param {ol.tilegrid.TileGrid} tileGrid Tile grid.
+ * @return {function(ol.Extent, number): Array.} Loading strategy.
+ */
+ol.loadingstrategy.createTile = function(tileGrid) {
+ return (
+ /**
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @return {Array.} Extents.
+ */
+ function(extent, resolution) {
+ var z = tileGrid.getZForResolution(resolution);
+ var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
+ /** @type {Array.} */
+ var extents = [];
+ var tileCoord = new ol.TileCoord(z, 0, 0);
+ for (tileCoord.x = tileRange.minX; tileCoord.x <= tileRange.maxX;
+ ++tileCoord.x) {
+ for (tileCoord.y = tileRange.minY; tileCoord.y <= tileRange.maxY;
+ ++tileCoord.y) {
+ extents.push(tileGrid.getTileCoordExtent(tileCoord));
+ }
+ }
+ return extents;
+ });
+};
diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js
index 4a59c94c9d..aa148b4d77 100644
--- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js
+++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js
@@ -170,7 +170,9 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame =
}
var frameStateExtent = frameState.extent;
- var frameStateResolution = frameState.view2DState.resolution;
+ var view2DState = frameState.view2DState;
+ var projection = view2DState.projection;
+ var resolution = view2DState.resolution;
var pixelRatio = frameState.pixelRatio;
var vectorLayerRevision = vectorLayer.getRevision();
var vectorLayerRenderOrder = vectorLayer.getRenderOrder();
@@ -179,7 +181,7 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame =
}
if (!this.dirty_ &&
- this.renderedResolution_ == frameStateResolution &&
+ this.renderedResolution_ == resolution &&
this.renderedRevision_ == vectorLayerRevision &&
this.renderedRenderOrder_ == vectorLayerRenderOrder &&
ol.extent.containsExtent(this.renderedExtent_, frameStateExtent)) {
@@ -204,9 +206,10 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame =
if (!goog.isDef(styleFunction)) {
styleFunction = ol.feature.defaultStyleFunction;
}
- var tolerance = frameStateResolution / (2 * pixelRatio);
- var replayGroup = new ol.render.canvas.ReplayGroup(tolerance, extent,
- frameStateResolution);
+ var tolerance = resolution / (2 * pixelRatio);
+ var replayGroup =
+ new ol.render.canvas.ReplayGroup(tolerance, extent, resolution);
+ vectorSource.loadFeatures(extent, resolution, projection);
var renderFeature =
/**
* @param {ol.Feature} feature Feature.
@@ -215,19 +218,28 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame =
function(feature) {
goog.asserts.assert(goog.isDef(styleFunction));
var dirty = this.renderFeature(
- feature, frameStateResolution, pixelRatio, styleFunction, replayGroup);
+ feature, resolution, pixelRatio, styleFunction, replayGroup);
this.dirty_ = this.dirty_ || dirty;
};
if (!goog.isNull(vectorLayerRenderOrder)) {
- var features = vectorSource.getFeaturesInExtent(extent);
+ /** @type {Array.} */
+ var features = [];
+ vectorSource.forEachFeatureInExtentAtResolution(extent, resolution,
+ /**
+ * @param {ol.Feature} feature Feature.
+ */
+ function(feature) {
+ features.push(feature);
+ }, this);
goog.array.sort(features, vectorLayerRenderOrder);
goog.array.forEach(features, renderFeature, this);
} else {
- vectorSource.forEachFeatureInExtent(extent, renderFeature, this);
+ vectorSource.forEachFeatureInExtentAtResolution(
+ extent, resolution, renderFeature, this);
}
replayGroup.finish();
- this.renderedResolution_ = frameStateResolution;
+ this.renderedResolution_ = resolution;
this.renderedRevision_ = vectorLayerRevision;
this.renderedRenderOrder_ = vectorLayerRenderOrder;
this.replayGroup_ = replayGroup;
diff --git a/src/ol/source/bingmapssource.js b/src/ol/source/bingmapssource.js
index edc223891a..fa93ddb4d5 100644
--- a/src/ol/source/bingmapssource.js
+++ b/src/ol/source/bingmapssource.js
@@ -91,6 +91,7 @@ ol.source.BingMaps.prototype.handleImageryMetadataResponse =
this.tileGrid = tileGrid;
var culture = this.culture_;
+ var sourceProjection = this.getProjection();
this.tileUrlFunction = ol.TileUrlFunction.withTileCoordTransform(
tileGrid.createTileCoordTransform(),
ol.TileUrlFunction.createFromTileUrlFunctions(
@@ -102,7 +103,6 @@ ol.source.BingMaps.prototype.handleImageryMetadataResponse =
.replace('{culture}', culture);
return (
/**
- * @this {ol.source.BingMaps}
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @param {number} pixelRatio Pixel ratio.
* @param {ol.proj.Projection} projection Projection.
@@ -110,7 +110,7 @@ ol.source.BingMaps.prototype.handleImageryMetadataResponse =
*/
function(tileCoord, pixelRatio, projection) {
goog.asserts.assert(ol.proj.equivalent(
- projection, this.getProjection()));
+ projection, sourceProjection));
if (goog.isNull(tileCoord)) {
return undefined;
} else {
diff --git a/src/ol/source/formatvectorsource.js b/src/ol/source/formatvectorsource.js
new file mode 100644
index 0000000000..3b3ad39fd6
--- /dev/null
+++ b/src/ol/source/formatvectorsource.js
@@ -0,0 +1,135 @@
+// FIXME consider delaying feature reading so projection can be provided by
+// consumer (e.g. the view)
+
+goog.provide('ol.source.FormatVector');
+
+goog.require('goog.asserts');
+goog.require('goog.dispose');
+goog.require('goog.events');
+goog.require('goog.net.EventType');
+goog.require('goog.net.XhrIo');
+goog.require('goog.net.XhrIo.ResponseType');
+goog.require('goog.userAgent');
+goog.require('ol.BrowserFeature');
+goog.require('ol.format.FormatType');
+goog.require('ol.proj');
+goog.require('ol.source.State');
+goog.require('ol.source.Vector');
+goog.require('ol.xml');
+
+
+
+/**
+ * @constructor
+ * @extends {ol.source.Vector}
+ * @param {olx.source.FormatVectorOptions} options Options.
+ * @todo stability experimental
+ */
+ol.source.FormatVector = function(options) {
+
+ goog.base(this, {
+ attributions: options.attributions,
+ extent: options.extent,
+ logo: options.logo,
+ projection: options.projection
+ });
+
+ /**
+ * @protected
+ * @type {ol.format.Feature}
+ */
+ this.format = options.format;
+
+};
+goog.inherits(ol.source.FormatVector, ol.source.Vector);
+
+
+/**
+ * @param {goog.Uri|string} url URL.
+ * @param {function(this: T, Array.)} callback Callback.
+ * @param {T} thisArg Value to use as `this` when executing `callback`.
+ * @template T
+ */
+ol.source.FormatVector.prototype.loadFeaturesFromURL =
+ function(url, callback, thisArg) {
+ var xhrIo = new goog.net.XhrIo();
+ var type = this.format.getType();
+ var responseType;
+ // FIXME maybe use ResponseType.DOCUMENT?
+ if (type == ol.format.FormatType.BINARY &&
+ ol.BrowserFeature.HAS_ARRAY_BUFFER) {
+ responseType = goog.net.XhrIo.ResponseType.ARRAY_BUFFER;
+ } else {
+ responseType = goog.net.XhrIo.ResponseType.TEXT;
+ }
+ xhrIo.setResponseType(responseType);
+ goog.events.listen(xhrIo, goog.net.EventType.COMPLETE,
+ /**
+ * @param {Event} event Event.
+ * @private
+ * @this {ol.source.FormatVector}
+ */
+ function(event) {
+ var xhrIo = event.target;
+ goog.asserts.assertInstanceof(xhrIo, goog.net.XhrIo);
+ if (xhrIo.isSuccess()) {
+ var type = this.format.getType();
+ /** @type {ArrayBuffer|Document|Node|Object|string|undefined} */
+ var source;
+ if (type == ol.format.FormatType.BINARY &&
+ ol.BrowserFeature.HAS_ARRAY_BUFFER) {
+ source = xhrIo.getResponse();
+ goog.asserts.assertInstanceof(source, ArrayBuffer);
+ } else if (type == ol.format.FormatType.JSON) {
+ source = xhrIo.getResponseText();
+ } else if (type == ol.format.FormatType.TEXT) {
+ source = xhrIo.getResponseText();
+ } else if (type == ol.format.FormatType.XML) {
+ if (!goog.userAgent.IE) {
+ source = xhrIo.getResponseXml();
+ }
+ if (!goog.isDefAndNotNull(source)) {
+ source = ol.xml.load(xhrIo.getResponseText());
+ }
+ } else {
+ goog.asserts.fail();
+ }
+ if (goog.isDefAndNotNull(source)) {
+ callback.call(thisArg, this.readFeatures(source));
+ } else {
+ this.setState(ol.source.State.ERROR);
+ goog.asserts.fail();
+ }
+ } else {
+ this.setState(ol.source.State.ERROR);
+ }
+ goog.dispose(xhrIo);
+ }, false, this);
+ xhrIo.send(url);
+};
+
+
+/**
+ * @param {ArrayBuffer|Document|Node|Object|string} source Source.
+ * @return {Array.} Features.
+ */
+ol.source.FormatVector.prototype.readFeatures = function(source) {
+ var format = this.format;
+ var features = format.readFeatures(source);
+ var featureProjection = format.readProjection(source);
+ var projection = this.getProjection();
+ if (!goog.isNull(projection)) {
+ if (!ol.proj.equivalent(featureProjection, projection)) {
+ var transform = ol.proj.getTransform(featureProjection, projection);
+ var i, ii;
+ for (i = 0, ii = features.length; i < ii; ++i) {
+ var feature = features[i];
+ var geometry = feature.getGeometry();
+ if (!goog.isNull(geometry)) {
+ geometry.transform(transform);
+ }
+ }
+ }
+ }
+ return features;
+};
diff --git a/src/ol/source/geojsonsource.js b/src/ol/source/geojsonsource.js
index 1ee7532fd3..230a79b105 100644
--- a/src/ol/source/geojsonsource.js
+++ b/src/ol/source/geojsonsource.js
@@ -1,13 +1,13 @@
goog.provide('ol.source.GeoJSON');
goog.require('ol.format.GeoJSON');
-goog.require('ol.source.VectorFile');
+goog.require('ol.source.StaticVector');
/**
* @constructor
- * @extends {ol.source.VectorFile}
+ * @extends {ol.source.StaticVector}
* @fires {@link ol.source.VectorEvent} ol.source.VectorEvent
* @param {olx.source.GeoJSONOptions=} opt_options Options.
* @todo stability experimental
@@ -31,4 +31,4 @@ ol.source.GeoJSON = function(opt_options) {
});
};
-goog.inherits(ol.source.GeoJSON, ol.source.VectorFile);
+goog.inherits(ol.source.GeoJSON, ol.source.StaticVector);
diff --git a/src/ol/source/gpxsource.js b/src/ol/source/gpxsource.js
index 1db7f23603..447751e7dd 100644
--- a/src/ol/source/gpxsource.js
+++ b/src/ol/source/gpxsource.js
@@ -1,13 +1,13 @@
goog.provide('ol.source.GPX');
goog.require('ol.format.GPX');
-goog.require('ol.source.VectorFile');
+goog.require('ol.source.StaticVector');
/**
* @constructor
- * @extends {ol.source.VectorFile}
+ * @extends {ol.source.StaticVector}
* @fires {@link ol.source.VectorEvent} ol.source.VectorEvent
* @param {olx.source.GPXOptions=} opt_options Options.
* @todo stability experimental
@@ -30,4 +30,4 @@ ol.source.GPX = function(opt_options) {
});
};
-goog.inherits(ol.source.GPX, ol.source.VectorFile);
+goog.inherits(ol.source.GPX, ol.source.StaticVector);
diff --git a/src/ol/source/igcsource.js b/src/ol/source/igcsource.js
index c8f19cddfd..320dda80da 100644
--- a/src/ol/source/igcsource.js
+++ b/src/ol/source/igcsource.js
@@ -1,13 +1,13 @@
goog.provide('ol.source.IGC');
goog.require('ol.format.IGC');
-goog.require('ol.source.VectorFile');
+goog.require('ol.source.StaticVector');
/**
* @constructor
- * @extends {ol.source.VectorFile}
+ * @extends {ol.source.StaticVector}
* @fires {@link ol.source.VectorEvent} ol.source.VectorEvent
* @param {olx.source.IGCOptions=} opt_options Options.
* @todo stability experimental
@@ -27,4 +27,4 @@ ol.source.IGC = function(opt_options) {
});
};
-goog.inherits(ol.source.IGC, ol.source.VectorFile);
+goog.inherits(ol.source.IGC, ol.source.StaticVector);
diff --git a/src/ol/source/imagevectorsource.js b/src/ol/source/imagevectorsource.js
index f7bf16b53c..7f27f19ce6 100644
--- a/src/ol/source/imagevectorsource.js
+++ b/src/ol/source/imagevectorsource.js
@@ -106,7 +106,7 @@ ol.source.ImageVector.prototype.canvasFunctionInternal_ =
resolution);
var loading = false;
- this.source_.forEachFeatureInExtent(extent,
+ this.source_.forEachFeatureInExtentAtResolution(extent, resolution,
/**
* @param {ol.Feature} feature Feature.
*/
diff --git a/src/ol/source/kmlsource.js b/src/ol/source/kmlsource.js
index 00b3891a54..c94b70bf69 100644
--- a/src/ol/source/kmlsource.js
+++ b/src/ol/source/kmlsource.js
@@ -1,13 +1,13 @@
goog.provide('ol.source.KML');
goog.require('ol.format.KML');
-goog.require('ol.source.VectorFile');
+goog.require('ol.source.StaticVector');
/**
* @constructor
- * @extends {ol.source.VectorFile}
+ * @extends {ol.source.StaticVector}
* @fires {@link ol.source.VectorEvent} ol.source.VectorEvent
* @param {olx.source.KMLOptions=} opt_options Options.
* @todo stability experimental
@@ -32,4 +32,4 @@ ol.source.KML = function(opt_options) {
});
};
-goog.inherits(ol.source.KML, ol.source.VectorFile);
+goog.inherits(ol.source.KML, ol.source.StaticVector);
diff --git a/src/ol/source/osmxmlsource.js b/src/ol/source/osmxmlsource.js
index b93ca145b2..2baecbaa20 100644
--- a/src/ol/source/osmxmlsource.js
+++ b/src/ol/source/osmxmlsource.js
@@ -1,13 +1,13 @@
goog.provide('ol.source.OSMXML');
goog.require('ol.format.OSMXML');
-goog.require('ol.source.VectorFile');
+goog.require('ol.source.StaticVector');
/**
* @constructor
- * @extends {ol.source.VectorFile}
+ * @extends {ol.source.StaticVector}
* @fires {@link ol.source.VectorEvent} ol.source.VectorEvent
* @param {olx.source.OSMXMLOptions=} opt_options Options.
*/
@@ -29,4 +29,4 @@ ol.source.OSMXML = function(opt_options) {
});
};
-goog.inherits(ol.source.OSMXML, ol.source.VectorFile);
+goog.inherits(ol.source.OSMXML, ol.source.StaticVector);
diff --git a/src/ol/source/servervectorsource.exports b/src/ol/source/servervectorsource.exports
new file mode 100644
index 0000000000..8d0a5d6a63
--- /dev/null
+++ b/src/ol/source/servervectorsource.exports
@@ -0,0 +1,2 @@
+@exportSymbol ol.source.ServerVector
+@exportProperty ol.source.ServerVector.prototype.readFeatures
diff --git a/src/ol/source/servervectorsource.js b/src/ol/source/servervectorsource.js
new file mode 100644
index 0000000000..f10085c2b8
--- /dev/null
+++ b/src/ol/source/servervectorsource.js
@@ -0,0 +1,99 @@
+// FIXME cache expiration
+
+goog.provide('ol.source.ServerVector');
+
+goog.require('ol.extent');
+goog.require('ol.loadingstrategy');
+goog.require('ol.source.FormatVector');
+goog.require('ol.structs.RBush');
+
+
+
+/**
+ * @constructor
+ * @extends {ol.source.FormatVector}
+ * @param {olx.source.ServerVectorOptions} options Options.
+ */
+ol.source.ServerVector = function(options) {
+
+ goog.base(this, {
+ attributions: options.attributions,
+ extent: options.extent,
+ format: options.format,
+ logo: options.logo,
+ projection: options.projection
+ });
+
+ /**
+ * @private
+ * @type {ol.structs.RBush.<{extent: ol.Extent}>}
+ */
+ this.loadedExtents_ = new ol.structs.RBush();
+
+ /**
+ * @private
+ * @type {function(this: ol.source.ServerVector, ol.Extent, number,
+ * ol.proj.Projection): string}
+ */
+ this.loader_ = options.loader;
+
+ /**
+ * @private
+ * @type {function(ol.Extent, number): Array.}
+ */
+ this.strategy_ = goog.isDef(options.strategy) ?
+ options.strategy : ol.loadingstrategy.bbox;
+
+ /**
+ * @private
+ * @type {Object.}
+ */
+ this.loadedFeatures_ = {};
+
+};
+goog.inherits(ol.source.ServerVector, ol.source.FormatVector);
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.ServerVector.prototype.addFeaturesInternal = function(features) {
+ /** @type {Array.} */
+ var notLoadedFeatures = [];
+ var i, ii;
+ for (i = 0, ii = features.length; i < ii; ++i) {
+ var feature = features[i];
+ var featureId = feature.getId();
+ if (!(featureId in this.loadedFeatures_)) {
+ notLoadedFeatures.push(feature);
+ this.loadedFeatures_[featureId] = true;
+ }
+ }
+ goog.base(this, 'addFeaturesInternal', notLoadedFeatures);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.ServerVector.prototype.loadFeatures =
+ function(extent, resolution, projection) {
+ var loadedExtents = this.loadedExtents_;
+ var extentsToLoad = this.strategy_(extent, resolution);
+ var i, ii;
+ for (i = 0, ii = extentsToLoad.length; i < ii; ++i) {
+ var extentToLoad = extentsToLoad[i];
+ var alreadyLoaded = loadedExtents.forEachInExtent(extentToLoad,
+ /**
+ * @param {{extent: ol.Extent}} object Object.
+ * @return {boolean} Contains.
+ */
+ function(object) {
+ return ol.extent.containsExtent(object.extent, extentToLoad);
+ });
+ if (!alreadyLoaded) {
+ this.loader_.call(this, extentToLoad, resolution, projection);
+ loadedExtents.insert(extentToLoad, {extent: extentToLoad.slice()});
+ }
+ }
+};
diff --git a/src/ol/source/staticvectorsource.exports b/src/ol/source/staticvectorsource.exports
new file mode 100644
index 0000000000..1c283475e6
--- /dev/null
+++ b/src/ol/source/staticvectorsource.exports
@@ -0,0 +1 @@
+@exportSymbol ol.source.StaticVector
diff --git a/src/ol/source/staticvectorsource.js b/src/ol/source/staticvectorsource.js
new file mode 100644
index 0000000000..6ebc8ad278
--- /dev/null
+++ b/src/ol/source/staticvectorsource.js
@@ -0,0 +1,76 @@
+goog.provide('ol.source.StaticVector');
+
+goog.require('ol.source.FormatVector');
+goog.require('ol.source.State');
+
+
+
+/**
+ * @constructor
+ * @extends {ol.source.FormatVector}
+ * @fires {@link ol.source.VectorEvent} ol.source.VectorEvent
+ * @param {olx.source.StaticVectorOptions} options Options.
+ * @todo stability experimental
+ */
+ol.source.StaticVector = function(options) {
+
+ goog.base(this, {
+ attributions: options.attributions,
+ extent: options.extent,
+ format: options.format,
+ logo: options.logo,
+ projection: options.projection
+ });
+
+ if (goog.isDef(options.arrayBuffer)) {
+ this.addFeaturesInternal(this.readFeatures(options.arrayBuffer));
+ }
+
+ if (goog.isDef(options.doc)) {
+ this.addFeaturesInternal(this.readFeatures(options.doc));
+ }
+
+ if (goog.isDef(options.node)) {
+ this.addFeaturesInternal(this.readFeatures(options.node));
+ }
+
+ if (goog.isDef(options.object)) {
+ this.addFeaturesInternal(this.readFeatures(options.object));
+ }
+
+ if (goog.isDef(options.text)) {
+ this.addFeaturesInternal(this.readFeatures(options.text));
+ }
+
+ if (goog.isDef(options.url) || goog.isDef(options.urls)) {
+ this.setState(ol.source.State.LOADING);
+ if (goog.isDef(options.url)) {
+ this.loadFeaturesFromURL(options.url,
+ /**
+ * @param {Array.} features Features.
+ * @this {ol.source.StaticVector}
+ */
+ function(features) {
+ this.addFeaturesInternal(features);
+ this.setState(ol.source.State.READY);
+ }, this);
+ }
+ if (goog.isDef(options.urls)) {
+ var urls = options.urls;
+ var i, ii;
+ for (i = 0, ii = urls.length; i < ii; ++i) {
+ this.loadFeaturesFromURL(urls[i],
+ /**
+ * @param {Array.} features Features.
+ * @this {ol.source.StaticVector}
+ */
+ function(features) {
+ this.addFeaturesInternal(features);
+ this.setState(ol.source.State.READY);
+ }, this);
+ }
+ }
+ }
+
+};
+goog.inherits(ol.source.StaticVector, ol.source.FormatVector);
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..cf64f35430
--- /dev/null
+++ b/src/ol/source/tilevectorsource.js
@@ -0,0 +1,245 @@
+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;
+
+ /**
+ * @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_ = {};
+
+ 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 = goog.abstractMethod;
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileVector.prototype.forEachFeatureInExtent = goog.abstractMethod;
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.TileVector.prototype.forEachFeatureInExtentAtResolution =
+ function(extent, resolution, f, opt_this) {
+ var tileGrid = this.tileGrid_;
+ var tiles = this.tiles_;
+ var z = tileGrid.getZForResolution(resolution);
+ var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
+ 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 = goog.abstractMethod;
+
+
+/**
+ * @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) {
+ var tileCoordTransform = this.tileCoordTransform_;
+ var tileGrid = this.tileGrid_;
+ var tileUrlFunction = this.tileUrlFunction_;
+ var tiles = this.tiles_;
+ var z = tileGrid.getZForResolution(resolution);
+ var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
+ 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));
+};
diff --git a/src/ol/source/topojsonsource.js b/src/ol/source/topojsonsource.js
index 991c72afb8..cd65cf76ba 100644
--- a/src/ol/source/topojsonsource.js
+++ b/src/ol/source/topojsonsource.js
@@ -1,13 +1,13 @@
goog.provide('ol.source.TopoJSON');
goog.require('ol.format.TopoJSON');
-goog.require('ol.source.VectorFile');
+goog.require('ol.source.StaticVector');
/**
* @constructor
- * @extends {ol.source.VectorFile}
+ * @extends {ol.source.StaticVector}
* @fires {@link ol.source.VectorEvent} ol.source.VectorEvent
* @param {olx.source.TopoJSONOptions=} opt_options Options.
* @todo stability experimental
@@ -30,4 +30,4 @@ ol.source.TopoJSON = function(opt_options) {
});
};
-goog.inherits(ol.source.TopoJSON, ol.source.VectorFile);
+goog.inherits(ol.source.TopoJSON, ol.source.StaticVector);
diff --git a/src/ol/source/vectorfilesource.exports b/src/ol/source/vectorfilesource.exports
deleted file mode 100644
index acd6140571..0000000000
--- a/src/ol/source/vectorfilesource.exports
+++ /dev/null
@@ -1 +0,0 @@
-@exportSymbol ol.source.VectorFile
diff --git a/src/ol/source/vectorfilesource.js b/src/ol/source/vectorfilesource.js
deleted file mode 100644
index 885a69335a..0000000000
--- a/src/ol/source/vectorfilesource.js
+++ /dev/null
@@ -1,168 +0,0 @@
-// FIXME consider delaying feature reading so projection can be provided by
-// consumer (e.g. the view)
-
-goog.provide('ol.source.VectorFile');
-
-goog.require('goog.asserts');
-goog.require('goog.dispose');
-goog.require('goog.events');
-goog.require('goog.net.EventType');
-goog.require('goog.net.XhrIo');
-goog.require('goog.net.XhrIo.ResponseType');
-goog.require('goog.userAgent');
-goog.require('ol.BrowserFeature');
-goog.require('ol.format.FormatType');
-goog.require('ol.proj');
-goog.require('ol.source.State');
-goog.require('ol.source.Vector');
-goog.require('ol.xml');
-
-
-
-/**
- * @constructor
- * @extends {ol.source.Vector}
- * @fires {@link ol.source.VectorEvent} ol.source.VectorEvent
- * @param {olx.source.VectorFileOptions=} opt_options Options.
- * @todo stability experimental
- */
-ol.source.VectorFile = function(opt_options) {
-
- var options = goog.isDef(opt_options) ? opt_options : {};
-
- goog.base(this, {
- attributions: options.attributions,
- extent: options.extent,
- logo: options.logo,
- projection: options.projection
- });
-
- /**
- * @type {ol.format.Feature}
- * @protected
- */
- this.format = options.format;
-
- if (goog.isDef(options.doc)) {
- this.readFeatures_(options.doc);
- }
-
- if (goog.isDef(options.node)) {
- this.readFeatures_(options.node);
- }
-
- if (goog.isDef(options.object)) {
- this.readFeatures_(options.object);
- }
-
- if (goog.isDef(options.text)) {
- this.readFeatures_(options.text);
- }
-
- if (goog.isDef(options.arrayBuffer)) {
- this.readFeatures_(options.arrayBuffer);
- }
-
- if (goog.isDef(options.url) || goog.isDef(options.urls)) {
- this.setState(ol.source.State.LOADING);
-
- var type = this.format.getType();
- var responseType;
- if (type == ol.format.FormatType.BINARY &&
- ol.BrowserFeature.HAS_ARRAY_BUFFER) {
- responseType = goog.net.XhrIo.ResponseType.ARRAY_BUFFER;
- } else {
- responseType = goog.net.XhrIo.ResponseType.TEXT;
- }
- var xhrIo;
- if (goog.isDef(options.url)) {
- xhrIo = new goog.net.XhrIo();
- xhrIo.setResponseType(responseType);
- goog.events.listen(xhrIo, goog.net.EventType.COMPLETE,
- goog.bind(this.handleXhrIo_, this));
- xhrIo.send(options.url);
- }
- if (goog.isDef(options.urls)) {
- var urls = options.urls;
- var i, ii;
- for (i = 0, ii = urls.length; i < ii; ++i) {
- xhrIo = new goog.net.XhrIo();
- xhrIo.setResponseType(responseType);
- goog.events.listen(xhrIo, goog.net.EventType.COMPLETE,
- goog.bind(this.handleXhrIo_, this));
- xhrIo.send(urls[i]);
- }
- }
- }
-
-};
-goog.inherits(ol.source.VectorFile, ol.source.Vector);
-
-
-/**
- * @param {Event} event Event.
- * @private
- */
-ol.source.VectorFile.prototype.handleXhrIo_ = function(event) {
- var xhrIo = event.target;
- goog.asserts.assertInstanceof(xhrIo, goog.net.XhrIo);
- if (xhrIo.isSuccess()) {
- var type = this.format.getType();
- /** @type {ArrayBuffer|Document|Node|Object|string|undefined} */
- var source;
- if (type == ol.format.FormatType.BINARY &&
- ol.BrowserFeature.HAS_ARRAY_BUFFER) {
- source = xhrIo.getResponse();
- goog.asserts.assertInstanceof(source, ArrayBuffer);
- } else if (type == ol.format.FormatType.JSON) {
- source = xhrIo.getResponseText();
- } else if (type == ol.format.FormatType.TEXT) {
- source = xhrIo.getResponseText();
- } else if (type == ol.format.FormatType.XML) {
- if (!goog.userAgent.IE) {
- source = xhrIo.getResponseXml();
- }
- if (!goog.isDefAndNotNull(source)) {
- source = ol.xml.load(xhrIo.getResponseText());
- }
- } else {
- goog.asserts.fail();
- }
- goog.dispose(xhrIo);
- if (goog.isDefAndNotNull(source)) {
- this.readFeatures_(source);
- } else {
- this.setState(ol.source.State.ERROR);
- goog.asserts.fail();
- }
- } else {
- this.setState(ol.source.State.ERROR);
- }
-};
-
-
-/**
- * @param {ArrayBuffer|Document|Node|Object|string} source Source.
- * @private
- */
-ol.source.VectorFile.prototype.readFeatures_ = function(source) {
- var format = this.format;
- var features = format.readFeatures(source);
- var featureProjection = format.readProjection(source);
- var projection = this.getProjection();
- if (!goog.isNull(projection)) {
- if (!ol.proj.equivalent(featureProjection, projection)) {
- var transform = ol.proj.getTransform(featureProjection, projection);
- var i, ii;
- for (i = 0, ii = features.length; i < ii; ++i) {
- var feature = features[i];
- var geometry = feature.getGeometry();
- if (!goog.isNull(geometry)) {
- geometry.transform(transform);
- }
- }
- }
- }
- this.addFeaturesInternal(features);
- this.setState(ol.source.State.READY);
-};
diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js
index 6383b6dcd9..94ab64349a 100644
--- a/src/ol/source/vectorsource.js
+++ b/src/ol/source/vectorsource.js
@@ -13,6 +13,7 @@ goog.require('goog.events.Event');
goog.require('goog.events.EventType');
goog.require('goog.object');
goog.require('ol.ObjectEventType');
+goog.require('ol.proj');
goog.require('ol.source.Source');
goog.require('ol.structs.RBush');
@@ -207,6 +208,21 @@ ol.source.Vector.prototype.forEachFeatureInExtent =
};
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @param {function(this: T, ol.Feature): S} f Callback.
+ * @param {T=} opt_this The object to use as `this` in `f`.
+ * @return {S|undefined}
+ * @template T,S
+ * @todo stability experimental
+ */
+ol.source.Vector.prototype.forEachFeatureInExtentAtResolution =
+ function(extent, resolution, f, opt_this) {
+ return this.forEachFeatureInExtent(extent, f, opt_this);
+};
+
+
/**
* @return {Array.} Features.
* @todo stability experimental
@@ -337,6 +353,14 @@ ol.source.Vector.prototype.isEmpty = function() {
};
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @param {ol.proj.Projection} projection Projection.
+ */
+ol.source.Vector.prototype.loadFeatures = goog.nullFunction;
+
+
/**
* @param {ol.Feature} feature Feature.
* @todo stability experimental
diff --git a/src/ol/tileurlfunction.js b/src/ol/tileurlfunction.js
index 0626f0ec80..f99bfbcee2 100644
--- a/src/ol/tileurlfunction.js
+++ b/src/ol/tileurlfunction.js
@@ -10,11 +10,10 @@ goog.require('ol.TileCoord');
* A function that takes an {@link ol.TileCoord} for the tile coordinate,
* a `{number}` representing the pixel ratio and an {@link ol.proj.Projection}
* for the projection as arguments and returns a `{string}` or
- * undefined representing the tile URL. The this keyword inside the function
- * references the {@link ol.source.TileImage}.
+ * undefined representing the tile URL.
*
- * @typedef {function(this: ol.source.TileImage, ol.TileCoord,
- * number, ol.proj.Projection): (string|undefined)}
+ * @typedef {function(ol.TileCoord, number,
+ * ol.proj.Projection): (string|undefined)}
* @todo stability experimental
*/
ol.TileUrlFunctionType;
@@ -34,7 +33,6 @@ ol.TileCoordTransformType;
ol.TileUrlFunction.createFromTemplate = function(template) {
return (
/**
- * @this {ol.source.TileImage}
* @param {ol.TileCoord} tileCoord Tile Coordinate.
* @param {number} pixelRatio Pixel ratio.
* @param {ol.proj.Projection} projection Projection.
@@ -72,7 +70,6 @@ ol.TileUrlFunction.createFromTileUrlFunctions = function(tileUrlFunctions) {
}
return (
/**
- * @this {ol.source.TileImage}
* @param {ol.TileCoord} tileCoord Tile Coordinate.
* @param {number} pixelRatio Pixel ratio.
* @param {ol.proj.Projection} projection Projection.
@@ -84,15 +81,13 @@ ol.TileUrlFunction.createFromTileUrlFunctions = function(tileUrlFunctions) {
} else {
var index =
goog.math.modulo(tileCoord.hash(), tileUrlFunctions.length);
- return tileUrlFunctions[index].call(
- this, tileCoord, pixelRatio, projection);
+ return tileUrlFunctions[index](tileCoord, pixelRatio, projection);
}
});
};
/**
- * @this {ol.source.TileImage}
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @param {number} pixelRatio Pixel ratio.
* @param {ol.proj.Projection} projection Projection.
@@ -114,7 +109,6 @@ ol.TileUrlFunction.withTileCoordTransform =
var tmpTileCoord = new ol.TileCoord(0, 0, 0);
return (
/**
- * @this {ol.source.TileImage}
* @param {ol.TileCoord} tileCoord Tile Coordinate.
* @param {number} pixelRatio Pixel ratio.
* @param {ol.proj.Projection} projection Projection.
@@ -124,9 +118,8 @@ ol.TileUrlFunction.withTileCoordTransform =
if (goog.isNull(tileCoord)) {
return undefined;
} else {
- return tileUrlFunction.call(
- this,
- transformFn.call(this, tileCoord, projection, tmpTileCoord),
+ return tileUrlFunction(
+ transformFn(tileCoord, projection, tmpTileCoord),
pixelRatio,
projection);
}