diff --git a/examples/gpx.js b/examples/gpx.js
index 15e23a9d69..9416eded87 100644
--- a/examples/gpx.js
+++ b/examples/gpx.js
@@ -15,7 +15,14 @@ var raster = new ol.layer.TileLayer({
var vector = new ol.layer.Vector({
source: new ol.source.Vector({
projection: ol.proj.get('EPSG:4326')
- })
+ }),
+ featureInfoFunction: function(features) {
+ var info = [];
+ for (var i = 0, ii = features.length; i < ii; ++i) {
+ info.push(features[i].get('name') + ': ' + features[i].get('type'));
+ }
+ return info.join(', ');
+ }
});
var map = new ol.Map({
@@ -32,12 +39,8 @@ map.on(['click', 'mousemove'], function(evt) {
map.getFeatureInfo({
pixel: evt.getPixel(),
layers: [vector],
- success: function(features) {
- var info = [];
- for (var i = 0, ii = features.length; i < ii; ++i) {
- info.push(features[i].get('name') + ': ' + features[i].get('type'));
- }
- document.getElementById('info').innerHTML = info.join(', ') || ' ';
+ success: function(featureInfo) {
+ document.getElementById('info').innerHTML = featureInfo[0] || ' ';
}
});
});
diff --git a/examples/kml.js b/examples/kml.js
index 0f7f48aa39..154fba8f52 100644
--- a/examples/kml.js
+++ b/examples/kml.js
@@ -25,7 +25,14 @@ var epsg4326 = ol.proj.get('EPSG:4326');
var vector = new ol.layer.Vector({
source: new ol.source.Vector({
projection: epsg4326
- })
+ }),
+ featureInfoFunction: function(features) {
+ var info = [];
+ for (var i = 0, ii = features.length; i < ii; ++i) {
+ info.push(features[i].get('name'));
+ }
+ return info.join(', ');
+ }
});
var map = new ol.Map({
@@ -46,12 +53,8 @@ map.on(['click', 'mousemove'], function(evt) {
map.getFeatureInfo({
pixel: evt.getPixel(),
layers: [vector],
- success: function(features) {
- var info = [];
- for (var i = 0, ii = features.length; i < ii; ++i) {
- info.push(features[i].get('name'));
- }
- document.getElementById('info').innerHTML = info.join(', ') || ' ';
+ success: function(featureInfo) {
+ document.getElementById('info').innerHTML = featureInfo[0] || ' ';
}
});
});
diff --git a/examples/vector-layer.js b/examples/vector-layer.js
index 4ece93ef11..6b5602e98e 100644
--- a/examples/vector-layer.js
+++ b/examples/vector-layer.js
@@ -28,7 +28,11 @@ var vector = new ol.layer.Vector({
})
]
})
- ]})
+ ]}),
+ featureInfoFunction: function(features) {
+ return features.length > 0 ?
+ features[0].getFeatureId() + ': ' + features[0].get('name') : ' ';
+ }
});
var map = new ol.Map({
@@ -45,12 +49,8 @@ map.on(['click', 'mousemove'], function(evt) {
map.getFeatureInfo({
pixel: evt.getPixel(),
layers: [vector],
- success: function(features) {
- var info = [];
- for (var i = 0, ii = features.length; i < ii; ++i) {
- info.push(features[i].getFeatureId() + ': ' + features[i].get('name'));
- }
- document.getElementById('info').innerHTML = info.join(', ') || ' ';
+ success: function(featureInfo) {
+ document.getElementById('info').innerHTML = featureInfo[0];
}
});
});
diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc
index 02269b07e0..353613f91c 100644
--- a/src/objectliterals.jsdoc
+++ b/src/objectliterals.jsdoc
@@ -9,12 +9,26 @@
* @typedef {Object} ol.GetFeatureInfoOptions
* @property {ol.Pixel} pixel Pixel coordinate relative to the map viewport.
* @property {Array.
|undefined} layers Layers to restrict the
- * query to. All layers will be queried if not provided.
- * @property {function(Array.)} success Callback for
+ * query to. All map layers will be queried if not provided.
+ * @property {function(Array.)} success Callback for
* successful queries. The passed argument is the resulting feature
- * information. Layers that are able to provide attribute data will put
- * ol.Feature instances, other layers will put a string which can either
- * be plain text or markup.
+ * information for each layer, with array indices being the same as in the
+ * passed `layers` array or in the layer collection as returned from
+ * `ol.Map#getLayers()` if no `layers` were provided.
+ * @property {function(Object)|undefined} error Callback for unsuccessful
+ * queries.
+ */
+
+/**
+ * @typedef {Object} ol.GetFeaturesOptions
+ * @property {ol.Pixel} pixel Pixel coordinate relative to the map viewport.
+ * @property {Array.|undefined} layers Layers to restrict the
+ * query to. All layers will be queried if not provided.
+ * @property {function(Array.>)} success Callback
+ * for successful queries. The passed argument is the resulting features for
+ * each layer, with array indices being the same as in the passed `layers`
+ * array or in the layer collection as returned from `ol.Map#getLayers()` if
+ * no layers were provided.
* @property {function(Object)|undefined} error Callback for unsuccessful
* queries.
*/
@@ -287,6 +301,10 @@
/**
* @typedef {Object} ol.layer.VectorLayerOptions
+ * @property {function(Array.):string|
+ * undefined} featureInfoFunction Function to render an array of features
+ * into feature info markup. If not provided, a comma separated list the
+ * unique ids of the resulting features will be returned.
* @property {number|undefined} opacity Opacity. 0-1. Default is 1.
* @property {ol.source.Source} source Source for this layer.
* @property {ol.style.Style|undefined} style Style.
diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js
index bbbdd4e6cc..82af46a521 100644
--- a/src/ol/layer/vectorlayer.js
+++ b/src/ol/layer/vectorlayer.js
@@ -1,5 +1,6 @@
goog.provide('ol.layer.Vector');
+goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.events.EventType');
goog.require('goog.object');
@@ -178,6 +179,13 @@ ol.layer.Vector = function(options) {
*/
this.featureCache_ = new ol.layer.FeatureCache();
+ /**
+ * @type {function(Array.):string}
+ * @private
+ */
+ this.featureInfoFunction_ = goog.isDef(options.featureInfoFunction) ?
+ options.featureInfoFunction : ol.layer.Vector.uidFeatureInfoFunction;
+
/**
* TODO: this means we need to know dimension at construction
* @type {ol.geom.SharedVertices}
@@ -378,6 +386,25 @@ ol.layer.Vector.prototype.parseFeatures = function(data, parser, projection) {
};
+/**
+ * @return {function(Array.):string} Feature info function.
+ */
+ol.layer.Vector.prototype.getFeatureInfoFunction = function() {
+ return this.featureInfoFunction_;
+};
+
+
+/**
+ * @param {Array.} features Features.
+ * @return {string} Feature info.
+ */
+ol.layer.Vector.uidFeatureInfoFunction = function(features) {
+ var featureIds = goog.array.map(features,
+ function(feature) { return goog.getUid(feature); });
+ return featureIds.join(', ');
+};
+
+
goog.require('ol.filter.Extent');
goog.require('ol.filter.Geometry');
goog.require('ol.filter.Logical');
diff --git a/src/ol/map.exports b/src/ol/map.exports
index 4bca2e1995..8523ff1658 100644
--- a/src/ol/map.exports
+++ b/src/ol/map.exports
@@ -3,6 +3,7 @@
@exportProperty ol.Map.prototype.addPreRenderFunction
@exportProperty ol.Map.prototype.addPreRenderFunctions
@exportProperty ol.Map.prototype.getFeatureInfo
+@exportProperty ol.Map.prototype.getFeatures
@exportProperty ol.Map.prototype.getInteractions
@exportProperty ol.Map.prototype.getRenderer
@exportProperty ol.Map.prototype.removeLayer
diff --git a/src/ol/map.js b/src/ol/map.js
index 79a67077fb..2f8966853f 100644
--- a/src/ol/map.js
+++ b/src/ol/map.js
@@ -440,6 +440,19 @@ ol.Map.prototype.getFeatureInfo = function(options) {
};
+/**
+ * Get features for a pixel on the map.
+ *
+ * @param {ol.GetFeaturesOptions} options Options.
+ */
+ol.Map.prototype.getFeatures = function(options) {
+ var layers = goog.isDefAndNotNull(options.layers) ?
+ options.layers : this.getLayers().getArray();
+ this.getRenderer().getFeaturesForPixel(
+ options.pixel, layers, options.success, options.error);
+};
+
+
/**
* @return {ol.Collection} Interactions.
*/
diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js
index 2207b02d2b..7c56d15d19 100644
--- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js
+++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js
@@ -193,17 +193,31 @@ ol.renderer.canvas.VectorLayer.prototype.getTransform = function() {
/**
* @param {ol.Pixel} pixel Pixel coordinate relative to the map viewport.
- * @param {function(Array.)} success Callback for
- * successful queries. The passed argument is the resulting feature
- * information. Layers that are able to provide attribute data will put
- * ol.Feature instances, other layers will put a string which can either
- * be plain text or markup.
+ * @param {function(string, ol.layer.Layer)} success Callback for
+ * successful queries. The passed arguments are the resulting feature
+ * information and the layer.
*/
ol.renderer.canvas.VectorLayer.prototype.getFeatureInfoForPixel =
function(pixel, success) {
+ var callback = function(features, layer) {
+ success(layer.getFeatureInfoFunction()(features), layer);
+ };
+ this.getFeaturesForPixel(pixel, callback);
+};
+
+
+/**
+ * @param {ol.Pixel} pixel Pixel coordinate relative to the map viewport.
+ * @param {function(Array., ol.layer.Layer)} success Callback for
+ * successful queries. The passed arguments are the resulting features
+ * and the layer.
+ */
+ol.renderer.canvas.VectorLayer.prototype.getFeaturesForPixel =
+ function(pixel, success) {
var map = this.getMap();
var result = [];
+ var layer = this.getLayer();
var location = map.getCoordinateFromPixel(pixel);
var tileCoord = this.tileGrid_.getTileCoordForCoordAndResolution(
location, this.getMap().getView().getView2D().getResolution());
@@ -218,7 +232,7 @@ ol.renderer.canvas.VectorLayer.prototype.getFeatureInfoForPixel =
var locationMax = [location[0] + halfMaxWidth, location[1] + halfMaxHeight];
var locationBbox = ol.extent.boundingExtent([locationMin, locationMax]);
var filter = new ol.filter.Extent(locationBbox);
- var candidates = this.getLayer().getFeatures(filter);
+ var candidates = layer.getFeatures(filter);
var candidate, geom, type, symbolBounds, symbolSize, halfWidth, halfHeight,
coordinates, j;
@@ -261,7 +275,7 @@ ol.renderer.canvas.VectorLayer.prototype.getFeatureInfoForPixel =
}
}
}
- goog.global.setTimeout(function() { success(result); }, 0);
+ goog.global.setTimeout(function() { success(result, layer); }, 0);
};
diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js
index cfc2fad47e..2623110027 100644
--- a/src/ol/renderer/maprenderer.js
+++ b/src/ol/renderer/maprenderer.js
@@ -1,6 +1,7 @@
goog.provide('ol.renderer.Map');
goog.require('goog.Disposable');
+goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.dispose');
goog.require('goog.functions');
@@ -110,22 +111,60 @@ ol.renderer.Map.prototype.getCanvas = goog.functions.NULL;
*/
ol.renderer.Map.prototype.getFeatureInfoForPixel =
function(pixel, layers, success, opt_error) {
- var layer, layerRenderer;
- var featureInfo = [];
var numLayers = layers.length;
- var callback = function(layerFeatureInfo) {
- featureInfo.push.apply(featureInfo, layerFeatureInfo);
+ var featureInfo = new Array(numLayers);
+ var callback = function(layerFeatureInfo, layer) {
+ featureInfo[goog.array.indexOf(layers, layer)] = layerFeatureInfo;
--numLayers;
if (!numLayers) {
success(featureInfo);
}
};
+ var layer, layerRenderer;
for (var i = 0; i < numLayers; ++i) {
layer = layers[i];
layerRenderer = this.getLayerRenderer(layer);
if (goog.isFunction(layerRenderer.getFeatureInfoForPixel)) {
layerRenderer.getFeatureInfoForPixel(pixel, callback, opt_error);
+ } else {
+ --numLayers;
+ }
+ }
+};
+
+
+/**
+ * @param {ol.Pixel} pixel Pixel coordinate relative to the map viewport.
+ * @param {Array.} layers Layers to query.
+ * @param {function(Array.)} success Callback for
+ * successful queries. The passed argument is the resulting feature
+ * information. Layers that are able to provide attribute data will put
+ * ol.Feature instances, other layers will put a string which can either
+ * be plain text or markup.
+ * @param {function(Object)=} opt_error Callback for unsuccessful
+ * queries.
+ */
+ol.renderer.Map.prototype.getFeaturesForPixel =
+ function(pixel, layers, success, opt_error) {
+ var numLayers = layers.length;
+ var features = new Array(numLayers);
+ var callback = function(layerFeatures, layer) {
+ features[goog.array.indexOf(layers, layer)] = layerFeatures;
+ --numLayers;
+ if (!numLayers) {
+ success(features);
+ }
+ };
+
+ var layer, layerRenderer;
+ for (var i = 0; i < numLayers; ++i) {
+ layer = layers[i];
+ layerRenderer = this.getLayerRenderer(layer);
+ if (goog.isFunction(layerRenderer.getFeaturesForPixel)) {
+ layerRenderer.getFeaturesForPixel(pixel, callback, opt_error);
+ } else {
+ --numLayers;
}
}
};