getFeatures method and featureInfo templates
To avoid surprises for application developers, this change creates a new getFeatures method. So it is clear now beforehand whether features or feature info markup is returned. The result is now also grouped by layer, so application developers always have a link between a layer and the feature info it returns. To make getFeatureInfo return markup for vector layers, this change also adds a featureInfoFunction property to the vector layer, which gives developers full control over how features are rendered to feature info markup.
This commit is contained in:
@@ -15,7 +15,14 @@ var raster = new ol.layer.TileLayer({
|
|||||||
var vector = new ol.layer.Vector({
|
var vector = new ol.layer.Vector({
|
||||||
source: new ol.source.Vector({
|
source: new ol.source.Vector({
|
||||||
projection: ol.proj.get('EPSG:4326')
|
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({
|
var map = new ol.Map({
|
||||||
@@ -32,12 +39,8 @@ map.on(['click', 'mousemove'], function(evt) {
|
|||||||
map.getFeatureInfo({
|
map.getFeatureInfo({
|
||||||
pixel: evt.getPixel(),
|
pixel: evt.getPixel(),
|
||||||
layers: [vector],
|
layers: [vector],
|
||||||
success: function(features) {
|
success: function(featureInfo) {
|
||||||
var info = [];
|
document.getElementById('info').innerHTML = featureInfo[0] || ' ';
|
||||||
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(', ') || ' ';
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,7 +25,14 @@ var epsg4326 = ol.proj.get('EPSG:4326');
|
|||||||
var vector = new ol.layer.Vector({
|
var vector = new ol.layer.Vector({
|
||||||
source: new ol.source.Vector({
|
source: new ol.source.Vector({
|
||||||
projection: epsg4326
|
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({
|
var map = new ol.Map({
|
||||||
@@ -46,12 +53,8 @@ map.on(['click', 'mousemove'], function(evt) {
|
|||||||
map.getFeatureInfo({
|
map.getFeatureInfo({
|
||||||
pixel: evt.getPixel(),
|
pixel: evt.getPixel(),
|
||||||
layers: [vector],
|
layers: [vector],
|
||||||
success: function(features) {
|
success: function(featureInfo) {
|
||||||
var info = [];
|
document.getElementById('info').innerHTML = featureInfo[0] || ' ';
|
||||||
for (var i = 0, ii = features.length; i < ii; ++i) {
|
|
||||||
info.push(features[i].get('name'));
|
|
||||||
}
|
|
||||||
document.getElementById('info').innerHTML = info.join(', ') || ' ';
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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({
|
var map = new ol.Map({
|
||||||
@@ -45,12 +49,8 @@ map.on(['click', 'mousemove'], function(evt) {
|
|||||||
map.getFeatureInfo({
|
map.getFeatureInfo({
|
||||||
pixel: evt.getPixel(),
|
pixel: evt.getPixel(),
|
||||||
layers: [vector],
|
layers: [vector],
|
||||||
success: function(features) {
|
success: function(featureInfo) {
|
||||||
var info = [];
|
document.getElementById('info').innerHTML = featureInfo[0];
|
||||||
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(', ') || ' ';
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,12 +9,26 @@
|
|||||||
* @typedef {Object} ol.GetFeatureInfoOptions
|
* @typedef {Object} ol.GetFeatureInfoOptions
|
||||||
* @property {ol.Pixel} pixel Pixel coordinate relative to the map viewport.
|
* @property {ol.Pixel} pixel Pixel coordinate relative to the map viewport.
|
||||||
* @property {Array.<ol.layer.Layer>|undefined} layers Layers to restrict the
|
* @property {Array.<ol.layer.Layer>|undefined} layers Layers to restrict the
|
||||||
* query to. All layers will be queried if not provided.
|
* query to. All map layers will be queried if not provided.
|
||||||
* @property {function(Array.<ol.Feature|string>)} success Callback for
|
* @property {function(Array.<string|undefined>)} success Callback for
|
||||||
* successful queries. The passed argument is the resulting feature
|
* successful queries. The passed argument is the resulting feature
|
||||||
* information. Layers that are able to provide attribute data will put
|
* information for each layer, with array indices being the same as in the
|
||||||
* ol.Feature instances, other layers will put a string which can either
|
* passed `layers` array or in the layer collection as returned from
|
||||||
* be plain text or markup.
|
* `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.<ol.layer.Layer>|undefined} layers Layers to restrict the
|
||||||
|
* query to. All layers will be queried if not provided.
|
||||||
|
* @property {function(Array.<Array.<ol.Feature|undefined>>)} 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
|
* @property {function(Object)|undefined} error Callback for unsuccessful
|
||||||
* queries.
|
* queries.
|
||||||
*/
|
*/
|
||||||
@@ -287,6 +301,10 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} ol.layer.VectorLayerOptions
|
* @typedef {Object} ol.layer.VectorLayerOptions
|
||||||
|
* @property {function(Array.<ol.Feature>):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 {number|undefined} opacity Opacity. 0-1. Default is 1.
|
||||||
* @property {ol.source.Source} source Source for this layer.
|
* @property {ol.source.Source} source Source for this layer.
|
||||||
* @property {ol.style.Style|undefined} style Style.
|
* @property {ol.style.Style|undefined} style Style.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
goog.provide('ol.layer.Vector');
|
goog.provide('ol.layer.Vector');
|
||||||
|
|
||||||
|
goog.require('goog.array');
|
||||||
goog.require('goog.asserts');
|
goog.require('goog.asserts');
|
||||||
goog.require('goog.events.EventType');
|
goog.require('goog.events.EventType');
|
||||||
goog.require('goog.object');
|
goog.require('goog.object');
|
||||||
@@ -178,6 +179,13 @@ ol.layer.Vector = function(options) {
|
|||||||
*/
|
*/
|
||||||
this.featureCache_ = new ol.layer.FeatureCache();
|
this.featureCache_ = new ol.layer.FeatureCache();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {function(Array.<ol.Feature>):string}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.featureInfoFunction_ = goog.isDef(options.featureInfoFunction) ?
|
||||||
|
options.featureInfoFunction : ol.layer.Vector.uidFeatureInfoFunction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: this means we need to know dimension at construction
|
* TODO: this means we need to know dimension at construction
|
||||||
* @type {ol.geom.SharedVertices}
|
* @type {ol.geom.SharedVertices}
|
||||||
@@ -378,6 +386,25 @@ ol.layer.Vector.prototype.parseFeatures = function(data, parser, projection) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {function(Array.<ol.Feature>):string} Feature info function.
|
||||||
|
*/
|
||||||
|
ol.layer.Vector.prototype.getFeatureInfoFunction = function() {
|
||||||
|
return this.featureInfoFunction_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array.<ol.Feature>} 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.Extent');
|
||||||
goog.require('ol.filter.Geometry');
|
goog.require('ol.filter.Geometry');
|
||||||
goog.require('ol.filter.Logical');
|
goog.require('ol.filter.Logical');
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
@exportProperty ol.Map.prototype.addPreRenderFunction
|
@exportProperty ol.Map.prototype.addPreRenderFunction
|
||||||
@exportProperty ol.Map.prototype.addPreRenderFunctions
|
@exportProperty ol.Map.prototype.addPreRenderFunctions
|
||||||
@exportProperty ol.Map.prototype.getFeatureInfo
|
@exportProperty ol.Map.prototype.getFeatureInfo
|
||||||
|
@exportProperty ol.Map.prototype.getFeatures
|
||||||
@exportProperty ol.Map.prototype.getInteractions
|
@exportProperty ol.Map.prototype.getInteractions
|
||||||
@exportProperty ol.Map.prototype.getRenderer
|
@exportProperty ol.Map.prototype.getRenderer
|
||||||
@exportProperty ol.Map.prototype.removeLayer
|
@exportProperty ol.Map.prototype.removeLayer
|
||||||
|
|||||||
@@ -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.
|
* @return {ol.Collection} Interactions.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -193,17 +193,31 @@ ol.renderer.canvas.VectorLayer.prototype.getTransform = function() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {ol.Pixel} pixel Pixel coordinate relative to the map viewport.
|
* @param {ol.Pixel} pixel Pixel coordinate relative to the map viewport.
|
||||||
* @param {function(Array.<ol.Feature|string>)} success Callback for
|
* @param {function(string, ol.layer.Layer)} success Callback for
|
||||||
* successful queries. The passed argument is the resulting feature
|
* successful queries. The passed arguments are the resulting feature
|
||||||
* information. Layers that are able to provide attribute data will put
|
* information and the layer.
|
||||||
* ol.Feature instances, other layers will put a string which can either
|
|
||||||
* be plain text or markup.
|
|
||||||
*/
|
*/
|
||||||
ol.renderer.canvas.VectorLayer.prototype.getFeatureInfoForPixel =
|
ol.renderer.canvas.VectorLayer.prototype.getFeatureInfoForPixel =
|
||||||
function(pixel, success) {
|
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.Feature>, 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 map = this.getMap();
|
||||||
var result = [];
|
var result = [];
|
||||||
|
|
||||||
|
var layer = this.getLayer();
|
||||||
var location = map.getCoordinateFromPixel(pixel);
|
var location = map.getCoordinateFromPixel(pixel);
|
||||||
var tileCoord = this.tileGrid_.getTileCoordForCoordAndResolution(
|
var tileCoord = this.tileGrid_.getTileCoordForCoordAndResolution(
|
||||||
location, this.getMap().getView().getView2D().getResolution());
|
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 locationMax = [location[0] + halfMaxWidth, location[1] + halfMaxHeight];
|
||||||
var locationBbox = ol.extent.boundingExtent([locationMin, locationMax]);
|
var locationBbox = ol.extent.boundingExtent([locationMin, locationMax]);
|
||||||
var filter = new ol.filter.Extent(locationBbox);
|
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,
|
var candidate, geom, type, symbolBounds, symbolSize, halfWidth, halfHeight,
|
||||||
coordinates, j;
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
goog.provide('ol.renderer.Map');
|
goog.provide('ol.renderer.Map');
|
||||||
|
|
||||||
goog.require('goog.Disposable');
|
goog.require('goog.Disposable');
|
||||||
|
goog.require('goog.array');
|
||||||
goog.require('goog.asserts');
|
goog.require('goog.asserts');
|
||||||
goog.require('goog.dispose');
|
goog.require('goog.dispose');
|
||||||
goog.require('goog.functions');
|
goog.require('goog.functions');
|
||||||
@@ -110,22 +111,60 @@ ol.renderer.Map.prototype.getCanvas = goog.functions.NULL;
|
|||||||
*/
|
*/
|
||||||
ol.renderer.Map.prototype.getFeatureInfoForPixel =
|
ol.renderer.Map.prototype.getFeatureInfoForPixel =
|
||||||
function(pixel, layers, success, opt_error) {
|
function(pixel, layers, success, opt_error) {
|
||||||
var layer, layerRenderer;
|
|
||||||
var featureInfo = [];
|
|
||||||
var numLayers = layers.length;
|
var numLayers = layers.length;
|
||||||
var callback = function(layerFeatureInfo) {
|
var featureInfo = new Array(numLayers);
|
||||||
featureInfo.push.apply(featureInfo, layerFeatureInfo);
|
var callback = function(layerFeatureInfo, layer) {
|
||||||
|
featureInfo[goog.array.indexOf(layers, layer)] = layerFeatureInfo;
|
||||||
--numLayers;
|
--numLayers;
|
||||||
if (!numLayers) {
|
if (!numLayers) {
|
||||||
success(featureInfo);
|
success(featureInfo);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var layer, layerRenderer;
|
||||||
for (var i = 0; i < numLayers; ++i) {
|
for (var i = 0; i < numLayers; ++i) {
|
||||||
layer = layers[i];
|
layer = layers[i];
|
||||||
layerRenderer = this.getLayerRenderer(layer);
|
layerRenderer = this.getLayerRenderer(layer);
|
||||||
if (goog.isFunction(layerRenderer.getFeatureInfoForPixel)) {
|
if (goog.isFunction(layerRenderer.getFeatureInfoForPixel)) {
|
||||||
layerRenderer.getFeatureInfoForPixel(pixel, callback, opt_error);
|
layerRenderer.getFeatureInfoForPixel(pixel, callback, opt_error);
|
||||||
|
} else {
|
||||||
|
--numLayers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.Pixel} pixel Pixel coordinate relative to the map viewport.
|
||||||
|
* @param {Array.<ol.layer.Layer>} layers Layers to query.
|
||||||
|
* @param {function(Array.<ol.Feature|string>)} 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user