diff --git a/examples/kml.html b/examples/kml.html index 4c243a0950..ee44518836 100644 --- a/examples/kml.html +++ b/examples/kml.html @@ -43,6 +43,11 @@
KML
+
+
+   +
+
diff --git a/examples/kml.js b/examples/kml.js index dbbeb5da6e..2282e1504d 100644 --- a/examples/kml.js +++ b/examples/kml.js @@ -43,6 +43,20 @@ var map = new ol.Map({ var kml = new ol.parser.KML({ maxDepth: 1, dimension: 2, extractStyles: true, extractAttributes: true}); +map.on('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(', ') || ' '; + } + }); +}); + var url = 'data/kml/lines.kml'; var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); diff --git a/src/ol/extent.js b/src/ol/extent.js index d37dc87794..dee6c57968 100644 --- a/src/ol/extent.js +++ b/src/ol/extent.js @@ -57,8 +57,7 @@ ol.extent.boundingExtentXYs_ = function(xs, ys, opt_extent) { /** - * Checks if the passed coordinate is contained or on the edge - * of the extent. + * Checks if the passed coordinate is contained or on the edge of the extent. * * @param {ol.Extent} extent Extent. * @param {ol.Coordinate} coordinate Coordinate. diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index c941a10898..7a076406d1 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -70,6 +70,10 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { this.sketchTransform_ = goog.vec.Mat4.createNumber(); /** + * Tile cache entries are arrays. The first item in each array is the tile + * itself, the second are the symbol sizes, and the third is the maximum + * symbol size. + * * @private * @type {ol.TileCache} */ @@ -198,40 +202,64 @@ ol.renderer.canvas.VectorLayer.prototype.getTransform = function() { */ ol.renderer.canvas.VectorLayer.prototype.getFeatureInfoForPixel = function(pixel, success) { - // TODO adjust pixel tolerance for applied styles - var minPixel = new ol.Pixel(pixel.x - 1, pixel.y - 1); - var maxPixel = new ol.Pixel(pixel.x + 1, pixel.y + 1); var map = this.getMap(); - - var locationMin = map.getCoordinateFromPixel(minPixel); - var locationMax = map.getCoordinateFromPixel(maxPixel); - var locationBbox = ol.extent.boundingExtent([locationMin, locationMax]); - var filter = new ol.filter.Extent(locationBbox); - // TODO do a real intersect against the filtered result for exact matches - var candidates = this.getLayer().getFeatures(filter); + var result = []; var location = map.getCoordinateFromPixel(pixel); - // TODO adjust tolerance for stroke width or use configurable tolerance - var tolerance = map.getView().getView2D().getResolution() * 3; - var result = []; - var candidate, geom; - for (var i = 0, ii = candidates.length; i < ii; ++i) { - candidate = candidates[i]; - geom = candidate.getGeometry(); - if (goog.isFunction(geom.containsCoordinate)) { - // For polygons, check if the pixel location is inside the polygon - if (geom.containsCoordinate(location)) { - result.push(candidate); + var tileCoord = this.tileGrid_.getTileCoordForCoordAndResolution( + location, this.getMap().getView().getView2D().getResolution()); + var key = tileCoord.toString(); + if (this.tileCache_.containsKey(key)) { + var cachedTile = this.tileCache_.get(key); + var symbolSizes = cachedTile[1]; + var maxSymbolSize = cachedTile[2]; + var halfMaxWidth = maxSymbolSize[0] / 2; + var halfMaxHeight = maxSymbolSize[1] / 2; + var locationMin = [location[0] - halfMaxWidth, location[1] - halfMaxHeight]; + 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 candidate, geom, type, symbolBounds, symbolSize, halfWidth, halfHeight, + coordinates, j; + for (var i = 0, ii = candidates.length; i < ii; ++i) { + candidate = candidates[i]; + geom = candidate.getGeometry(); + type = geom.getType(); + if (type === ol.geom.GeometryType.POINT || + type === ol.geom.GeometryType.MULTIPOINT) { + // For points, check if the pixel coordinate is inside the candidate's + // symbol + symbolSize = symbolSizes[goog.getUid(candidate)]; + halfWidth = symbolSize[0] / 2; + halfHeight = symbolSize[1] / 2; + symbolBounds = ol.extent.boundingExtent( + [[location[0] - halfWidth, location[1] - halfHeight], + [location[0] + halfWidth, location[1] + halfHeight]]); + coordinates = geom.getCoordinates(); + if (!goog.isArray(coordinates[0])) { + coordinates = [coordinates]; + } + for (j = coordinates.length - 1; j >= 0; --j) { + if (ol.extent.containsCoordinate(symbolBounds, coordinates[j])) { + result.push(candidate); + break; + } + } + } else if (goog.isFunction(geom.containsCoordinate)) { + // For polygons, check if the pixel location is inside the polygon + if (geom.containsCoordinate(location)) { + result.push(candidate); + } + } else if (goog.isFunction(geom.distanceFromCoordinate)) { + // For lines, check if the distance to the pixel location is + // within the rendered line width + if (2 * geom.distanceFromCoordinate(location) <= + symbolSizes[goog.getUid(candidate)][0]) { + result.push(candidate); + } } - } else if (goog.isFunction(geom.distanceFromCoordinate)) { - // For lines, check if the ditance to the pixel location is within the - // tolerance threshold - if (geom.distanceFromCoordinate(location) < tolerance) { - result.push(candidate); - } - } else { - // For points, the bbox filter is all we need - result.push(candidate); } } goog.global.setTimeout(function() { success(result); }, 0); @@ -416,17 +444,19 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = goog.object.extend(tilesToRender, tilesOnSketchCanvas); } + var symbolSizes = sketchCanvasRenderer.getSymbolSizes(), + maxSymbolSize = sketchCanvasRenderer.getMaxSymbolSize(); for (key in tilesToRender) { tileCoord = tilesToRender[key]; if (this.tileCache_.containsKey(key)) { - tile = /** @type {HTMLCanvasElement} */ (this.tileCache_.get(key)); + tile = /** @type {HTMLCanvasElement} */ (this.tileCache_.get(key)[0]); } else { tile = /** @type {HTMLCanvasElement} */ (this.tileArchetype_.cloneNode(false)); tile.getContext('2d').drawImage(sketchCanvas, (tileRange.minX - tileCoord.x) * tileSize.width, (tileCoord.y - tileRange.maxY) * tileSize.height); - this.tileCache_.set(key, tile); + this.tileCache_.set(key, [tile, symbolSizes, maxSymbolSize]); } finalContext.drawImage(tile, tileSize.width * (tileCoord.x - tileRange.minX), diff --git a/src/ol/renderer/canvas/canvasvectorrenderer.js b/src/ol/renderer/canvas/canvasvectorrenderer.js index 4e4696d221..a5ac95d229 100644 --- a/src/ol/renderer/canvas/canvasvectorrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorrenderer.js @@ -79,6 +79,34 @@ ol.renderer.canvas.VectorRenderer = */ this.iconLoadedCallback_ = opt_iconLoadedCallback; + /** + * @type {Object.>} + * @private + */ + this.symbolSizes_ = {}; + + /** + * @type {Array.} + * @private + */ + this.maxSymbolSize_ = [0, 0]; + +}; + + +/** + * @return {Object.>} Symbolizer sizes. + */ +ol.renderer.canvas.VectorRenderer.prototype.getSymbolSizes = function() { + return this.symbolSizes_; +}; + + +/** + * @return {Array.} Maximum symbolizer size. + */ +ol.renderer.canvas.VectorRenderer.prototype.getMaxSymbolSize = function() { + return this.maxSymbolSize_; }; @@ -129,7 +157,8 @@ ol.renderer.canvas.VectorRenderer.prototype.renderLineStringFeatures_ = function(features, symbolizer) { var context = this.context_, - i, ii, geometry, components, j, jj, line, dim, k, kk, x, y; + i, ii, feature, id, currentSize, geometry, components, j, jj, line, dim, + k, kk, x, y; context.globalAlpha = symbolizer.opacity; context.strokeStyle = symbolizer.strokeColor; @@ -138,7 +167,15 @@ ol.renderer.canvas.VectorRenderer.prototype.renderLineStringFeatures_ = context.lineJoin = 'round'; // TODO: accept this as a symbolizer property context.beginPath(); for (i = 0, ii = features.length; i < ii; ++i) { - geometry = features[i].getGeometry(); + feature = features[i]; + id = goog.getUid(feature); + currentSize = goog.isDef(this.symbolSizes_[id]) ? + this.symbolSizes_[id] : [0]; + currentSize[0] = Math.max(currentSize[0], context.lineWidth); + this.symbolSizes_[id] = currentSize; + this.maxSymbolSize_ = [Math.max(currentSize[0], this.maxSymbolSize_[0]), + Math.max(currentSize[0], this.maxSymbolSize_[1])]; + geometry = feature.getGeometry(); if (geometry instanceof ol.geom.LineString) { components = [geometry]; } else { @@ -175,7 +212,8 @@ ol.renderer.canvas.VectorRenderer.prototype.renderPointFeatures_ = function(features, symbolizer) { var context = this.context_, - content, alpha, i, ii, geometry, components, j, jj, point, vec; + content, alpha, i, ii, feature, id, size, geometry, components, j, jj, + point, vec; if (symbolizer instanceof ol.style.ShapeLiteral) { content = ol.renderer.canvas.VectorRenderer.renderShape(symbolizer); @@ -198,7 +236,18 @@ ol.renderer.canvas.VectorRenderer.prototype.renderPointFeatures_ = context.setTransform(1, 0, 0, 1, -midWidth, -midHeight); context.globalAlpha = alpha; for (i = 0, ii = features.length; i < ii; ++i) { - geometry = features[i].getGeometry(); + feature = features[i]; + id = goog.getUid(feature); + size = this.symbolSizes_[id]; + this.symbolSizes_[id] = goog.isDef(size) ? + [Math.max(size[0], content.width * this.inverseScale_), + Math.max(size[1], content.height * this.inverseScale_)] : + [content.width * this.inverseScale_, + content.height * this.inverseScale_]; + this.maxSymbolSize_ = + [Math.max(this.maxSymbolSize_[0], this.symbolSizes_[id][0]), + Math.max(this.maxSymbolSize_[1], this.symbolSizes_[id][1])]; + geometry = feature.getGeometry(); if (geometry instanceof ol.geom.Point) { components = [geometry]; } else {