diff --git a/examples/icon-negative.html b/examples/icon-negative.html new file mode 100644 index 0000000000..9fd760e860 --- /dev/null +++ b/examples/icon-negative.html @@ -0,0 +1,9 @@ +--- +layout: example.html +title: Icon Pixel Operations +shortdesc: Canvas pixel operations on a point icon. +docs: > + Example using an icon to symbolize a point. Click on the icon to select it, and it will be rendered using its negative image. +tags: "vector, style, icon, marker, canvas, select" +--- +
diff --git a/examples/icon-negative.js b/examples/icon-negative.js new file mode 100644 index 0000000000..86f6839889 --- /dev/null +++ b/examples/icon-negative.js @@ -0,0 +1,71 @@ +goog.require('ol.Feature'); +goog.require('ol.Map'); +goog.require('ol.View'); +goog.require('ol.geom.Point'); +goog.require('ol.interaction.Select'); +goog.require('ol.layer.Tile'); +goog.require('ol.layer.Vector'); +goog.require('ol.source.Stamen'); +goog.require('ol.source.Vector'); +goog.require('ol.style.Icon'); +goog.require('ol.style.Style'); + + +function createStyle(src, img) { + return new ol.style.Style({ + image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({ + anchor: [0.5, 0.96], + src: src, + img: img, + imgSize: img ? [img.width, img.height] : undefined + })) + }); +} + +var iconFeature = new ol.Feature(new ol.geom.Point([0, 0])); +iconFeature.set('style', createStyle('data/icon.png', undefined)); + +var map = new ol.Map({ + layers: [ + new ol.layer.Tile({ + source: new ol.source.Stamen({ layer: 'watercolor' }) + }), + new ol.layer.Vector({ + style: function(feature) { return feature.get('style'); }, + source: new ol.source.Vector({ features: [iconFeature] }) + }) + ], + target: document.getElementById('map'), + view: new ol.View({ + center: [0, 0], + zoom: 3 + }) +}); + +var selectStyle = {}; +var select = new ol.interaction.Select({ + style: function(feature, resolution) { + var image = feature.get('style').getImage().getImage(); + if (!selectStyle[image.src]) { + var canvas = document.createElement('canvas'); + var context = canvas.getContext('2d'); + canvas.width = image.width; + canvas.height = image.height; + context.drawImage(image, 0, 0, image.width, image.height); + var imageData = context.getImageData(0, 0, canvas.width, canvas.height); + var data = imageData.data; + for (var i = 0, ii = data.length; i < ii; i = i + (i % 4 == 2 ? 2 : 1)) { + data[i] = 255 - data[i]; + } + context.putImageData(imageData, 0, 0); + selectStyle[image.src] = createStyle(undefined, canvas); + } + return selectStyle[image.src]; + } +}); +map.addInteraction(select); + +map.on('pointermove', function(evt) { + map.getTargetElement().style.cursor = + map.hasFeatureAtPixel(evt.pixel) ? 'pointer' : ''; +}); diff --git a/externs/olx.js b/externs/olx.js index 71deb6a622..5ae51dc0f7 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -6005,7 +6005,7 @@ olx.style.FillOptions.prototype.color; * anchorXUnits: (ol.style.IconAnchorUnits|undefined), * anchorYUnits: (ol.style.IconAnchorUnits|undefined), * crossOrigin: (null|string|undefined), - * img: (Image|undefined), + * img: (Image|HTMLCanvasElement|undefined), * offset: (Array.|undefined), * offsetOrigin: (ol.style.IconOrigin|undefined), * opacity: (number|undefined), @@ -6074,7 +6074,7 @@ olx.style.IconOptions.prototype.crossOrigin; * Image object for the icon. If the `src` option is not provided then the * provided image must already be loaded. And in that case, it is required * to provide the size of the image, with the `imgSize` option. - * @type {Image|undefined} + * @type {Image|HTMLCanvasElement|undefined} * @api */ olx.style.IconOptions.prototype.img; diff --git a/src/ol/style/iconstyle.js b/src/ol/style/iconstyle.js index 8417b08a9d..1f841ee774 100644 --- a/src/ol/style/iconstyle.js +++ b/src/ol/style/iconstyle.js @@ -90,7 +90,7 @@ ol.style.Icon = function(opt_options) { options.crossOrigin !== undefined ? options.crossOrigin : null; /** - * @type {Image} + * @type {Image|HTMLCanvasElement} */ var image = options.img !== undefined ? options.img : null; @@ -114,7 +114,7 @@ ol.style.Icon = function(opt_options) { 'imgSize must be set when image is provided'); if ((src === undefined || src.length === 0) && image) { - src = image.src; + src = image.src || goog.getUid(image).toString(); } goog.asserts.assert(src !== undefined && src.length > 0, 'must provide a defined and non-empty src or image'); @@ -244,7 +244,7 @@ ol.style.Icon.prototype.getAnchor = function() { /** * Get the image icon. * @param {number} pixelRatio Pixel ratio. - * @return {Image} Image element. + * @return {Image|HTMLCanvasElement} Image or Canvas element. * @api */ ol.style.Icon.prototype.getImage = function(pixelRatio) { @@ -368,7 +368,7 @@ ol.style.Icon.prototype.unlistenImageChange = function(listener, thisArg) { /** * @constructor - * @param {Image} image Image. + * @param {Image|HTMLCanvasElement} image Image. * @param {string|undefined} src Src. * @param {ol.Size} size Size. * @param {?string} crossOrigin Cross origin. @@ -388,7 +388,7 @@ ol.style.IconImage_ = function(image, src, size, crossOrigin, imageState) { /** * @private - * @type {Image} + * @type {Image|HTMLCanvasElement} */ this.image_ = !image ? new Image() : image; @@ -434,7 +434,7 @@ goog.inherits(ol.style.IconImage_, goog.events.EventTarget); /** - * @param {Image} image Image. + * @param {Image|HTMLCanvasElement} image Image. * @param {string} src Src. * @param {ol.Size} size Size. * @param {?string} crossOrigin Cross origin. @@ -499,7 +499,7 @@ ol.style.IconImage_.prototype.handleImageLoad_ = function() { /** * @param {number} pixelRatio Pixel ratio. - * @return {Image} Image element. + * @return {Image|HTMLCanvasElement} Image or Canvas element. */ ol.style.IconImage_.prototype.getImage = function(pixelRatio) { return this.image_; diff --git a/test/spec/ol/style/iconstyle.test.js b/test/spec/ol/style/iconstyle.test.js index ff99a50fb0..6efb6c57a8 100644 --- a/test/spec/ol/style/iconstyle.test.js +++ b/test/spec/ol/style/iconstyle.test.js @@ -9,6 +9,20 @@ goog.require('ol.style.IconOrigin'); describe('ol.style.Icon', function() { var size = [36, 48]; + describe('constructor', function() { + + it('caches canvas images with a uid as src', function() { + var canvas = document.createElement('canvas'); + new ol.style.Icon({ + img: canvas, + imgSize: size + }); + expect(ol.style.IconImage_.get( + canvas, goog.getUid(canvas), size, '').getImage()).to.eql(canvas); + }); + + }); + describe('#getAnchor', function() { var fractionAnchor = [0.25, 0.25];