diff --git a/src/ol/style/circlestyle.js b/src/ol/style/circlestyle.js index 28ed96a17a..eb7d53cc13 100644 --- a/src/ol/style/circlestyle.js +++ b/src/ol/style/circlestyle.js @@ -56,12 +56,21 @@ ol.style.Circle = function(opt_options) { var size = this.render_(); + /** + * @private + * @type {Array.} + */ + this.anchor_ = [size / 2, size / 2]; + + /** + * @private + * @type {ol.Size} + */ + this.size_ = [size, size]; + goog.base(this, { - anchor: [size / 2, size / 2], - imageState: ol.style.ImageState.LOADED, rotation: 0, scale: 1, - size: [size, size], snapToPixel: undefined, subtractViewRotation: false }); @@ -70,6 +79,14 @@ ol.style.Circle = function(opt_options) { goog.inherits(ol.style.Circle, ol.style.Image); +/** + * @inheritDoc + */ +ol.style.Circle.prototype.getAnchor = function() { + return this.anchor_; +}; + + /** * @return {ol.style.Fill} Fill style. */ @@ -94,6 +111,14 @@ ol.style.Circle.prototype.getImage = function(pixelRatio) { }; +/** + * @inheritDoc + */ +ol.style.Circle.prototype.getImageState = function() { + return ol.style.ImageState.LOADED; +}; + + /** * @return {number} Radius. */ @@ -102,6 +127,14 @@ ol.style.Circle.prototype.getRadius = function() { }; +/** + * @inheritDoc + */ +ol.style.Circle.prototype.getSize = function() { + return this.size_; +}; + + /** * @return {ol.style.Stroke} Stroke style. */ @@ -110,12 +143,24 @@ ol.style.Circle.prototype.getStroke = function() { }; +/** + * @inheritDoc + */ +ol.style.Circle.prototype.listenImageChange = goog.nullFunction; + + /** * @inheritDoc */ ol.style.Circle.prototype.load = goog.nullFunction; +/** + * @inheritDoc + */ +ol.style.Circle.prototype.unlistenImageChange = goog.nullFunction; + + /** * @private * @return {number} Size. diff --git a/src/ol/style/iconstyle.js b/src/ol/style/iconstyle.js index 5cb250f9ef..827ed4bd39 100644 --- a/src/ol/style/iconstyle.js +++ b/src/ol/style/iconstyle.js @@ -8,6 +8,7 @@ goog.require('goog.asserts'); goog.require('goog.dom'); goog.require('goog.dom.TagName'); goog.require('goog.events'); +goog.require('goog.events.EventTarget'); goog.require('goog.events.EventType'); goog.require('ol.style.Image'); goog.require('ol.style.ImageState'); @@ -32,49 +33,11 @@ ol.style.Icon = function(opt_options) { var options = goog.isDef(opt_options) ? opt_options : {}; - /** - * @private - * @type {Image|HTMLCanvasElement} - */ - this.hitDetectionImage_ = null; - - /** - * @private - * @type {Image} - */ - this.image_ = new Image(); - - /** - * @type {?string} - */ - var crossOrigin = - goog.isDef(options.crossOrigin) ? options.crossOrigin : null; - if (!goog.isNull(crossOrigin)) { - this.image_.crossOrigin = crossOrigin; - } - /** * @private * @type {Array.} */ - this.imageListenerKeys_ = null; - - /** - * @private - * @type {string|undefined} - */ - this.src_ = options.src; - - /** - * @private - * @type {boolean} - */ - this.tainting_ = false; - - /** - * @type {ol.Size} - */ - var size = goog.isDef(options.size) ? options.size : null; + this.anchor_ = goog.isDef(options.anchor) ? options.anchor : [0.5, 0.5]; /** * @private @@ -91,9 +54,22 @@ ol.style.Icon = function(opt_options) { options.anchorYUnits : ol.style.IconAnchorUnits.FRACTION; /** - * @type {Array.} + * @type {?string} */ - var anchor = goog.isDef(options.anchor) ? options.anchor : [0.5, 0.5]; + var crossOrigin = + goog.isDef(options.crossOrigin) ? options.crossOrigin : null; + + /** + * @private + * @type {ol.style.IconImage_} + */ + this.iconImage_ = new ol.style.IconImage_(options.src, crossOrigin); + + /** + * @private + * @type {ol.Size} + */ + this.size_ = goog.isDef(options.size) ? options.size : null; /** * @type {number} @@ -106,11 +82,8 @@ ol.style.Icon = function(opt_options) { var scale = goog.isDef(options.scale) ? options.scale : 1; goog.base(this, { - anchor: anchor, - imageState: ol.style.ImageState.IDLE, rotation: rotation, scale: scale, - size: size, snapToPixel: undefined, subtractViewRotation: false }); @@ -120,9 +93,176 @@ goog.inherits(ol.style.Icon, ol.style.Image); /** + * @inheritDoc + */ +ol.style.Icon.prototype.getAnchor = function() { + var anchor = this.anchor_; + if (this.anchorXUnits_ == ol.style.IconAnchorUnits.FRACTION || + this.anchorYUnits_ == ol.style.IconAnchorUnits.FRACTION) { + var size = this.getSize(); + if (goog.isNull(size)) { + return null; + } + anchor = [this.anchor_[0], this.anchor_[1]]; + if (this.anchorXUnits_ == ol.style.IconAnchorUnits.FRACTION) { + anchor[0] *= size[0]; + } + if (this.anchorYUnits_ == ol.style.IconAnchorUnits.FRACTION) { + anchor[1] *= size[1]; + } + } + return anchor; +}; + + +/** + * @inheritDoc + */ +ol.style.Icon.prototype.getImage = function(pixelRatio) { + return this.iconImage_.getImage(pixelRatio); +}; + + +/** + * @inheritDoc + */ +ol.style.Icon.prototype.getImageState = function() { + return this.iconImage_.getImageState(); +}; + + +/** + * @inheritDoc + */ +ol.style.Icon.prototype.getHitDetectionImage = function(pixelRatio) { + return this.iconImage_.getHitDetectionImage(pixelRatio); +}; + + +/** + * @return {string|undefined} Image src. + */ +ol.style.Icon.prototype.getSrc = function() { + return this.iconImage_.getSrc(); +}; + + +/** + * @inheritDoc + */ +ol.style.Icon.prototype.getSize = function() { + return goog.isNull(this.size_) ? this.iconImage_.getSize() : this.size_; +}; + + +/** + * @inheritDoc + */ +ol.style.Icon.prototype.listenImageChange = function(listener, thisArg) { + return goog.events.listen(this.iconImage_, goog.events.EventType.CHANGE, + listener, false, thisArg); +}; + + +/** + * Load not yet loaded URI. + */ +ol.style.Icon.prototype.load = function() { + this.iconImage_.load(); +}; + + +/** + * @inheritDoc + */ +ol.style.Icon.prototype.unlistenImageChange = function(listener, thisArg) { + goog.events.unlisten(this.iconImage_, goog.events.EventType.CHANGE, + listener, false, thisArg); +}; + + + +/** + * @constructor + * @param {string} src Src. + * @param {?string} crossOrigin Cross origin. + * @extends {goog.events.EventTarget} * @private */ -ol.style.Icon.prototype.determineTainting_ = function() { +ol.style.IconImage_ = function(src, crossOrigin) { + + goog.base(this); + + /** + * @private + * @type {Image|HTMLCanvasElement} + */ + this.hitDetectionImage_ = null; + + /** + * @private + * @type {Image} + */ + this.image_ = new Image(); + + if (!goog.isNull(crossOrigin)) { + this.image_.crossOrigin = crossOrigin; + } + + /** + * @private + * @type {Array.} + */ + this.imageListenerKeys_ = null; + + /** + * @private + * @type {ol.style.ImageState} + */ + this.imageState_ = ol.style.ImageState.IDLE; + + /** + * @private + * @type {ol.Size} + */ + this.size_ = null; + + /** + * @private + * @type {string} + */ + this.src_ = src; + + /** + * @private + * @type {boolean} + */ + this.tainting_ = false; + +}; +goog.inherits(ol.style.IconImage_, goog.events.EventTarget); + + +/** + * @param {string} src Src. + * @param {?string} crossOrigin Cross origin. + * @return {ol.style.IconImage_} Icon image. + */ +ol.style.IconImage_.get = function(src, crossOrigin) { + var iconImageCache = ol.style.IconImageCache.getInstance(); + var iconImage = iconImageCache.get(src, crossOrigin); + if (goog.isNull(iconImage)) { + iconImage = new ol.style.IconImage_(src, crossOrigin); + iconImageCache.set(src, crossOrigin, iconImage); + } + return iconImage; +}; + + +/** + * @private + */ +ol.style.IconImage_.prototype.determineTainting_ = function() { var canvas = /** @type {HTMLCanvasElement} */ (goog.dom.createElement(goog.dom.TagName.CANVAS)); canvas.width = 1; @@ -141,51 +281,61 @@ ol.style.Icon.prototype.determineTainting_ = function() { /** * @private */ -ol.style.Icon.prototype.handleImageError_ = function() { - this.imageState = ol.style.ImageState.ERROR; - this.unlistenImage_(); - this.dispatchChangeEvent(); +ol.style.IconImage_.prototype.dispatchChangeEvent_ = function() { + this.dispatchEvent(goog.events.EventType.CHANGE); }; /** * @private */ -ol.style.Icon.prototype.handleImageLoad_ = function() { - this.imageState = ol.style.ImageState.LOADED; - if (goog.isNull(this.size)) { - this.size = [this.image_.width, this.image_.height]; - } - if (this.anchorXUnits_ == ol.style.IconAnchorUnits.FRACTION) { - this.anchor[0] = this.size[0] * this.anchor[0]; - } - if (this.anchorYUnits_ == ol.style.IconAnchorUnits.FRACTION) { - this.anchor[1] = this.size[1] * this.anchor[1]; - } +ol.style.IconImage_.prototype.handleImageError_ = function() { + this.imageState_ = ol.style.ImageState.ERROR; this.unlistenImage_(); - this.determineTainting_(); - this.dispatchChangeEvent(); + this.dispatchChangeEvent_(); }; /** - * @inheritDoc + * @private */ -ol.style.Icon.prototype.getImage = function(pixelRatio) { +ol.style.IconImage_.prototype.handleImageLoad_ = function() { + this.imageState_ = ol.style.ImageState.LOADED; + this.size_ = [this.image_.width, this.image_.height]; + this.unlistenImage_(); + this.determineTainting_(); + this.dispatchChangeEvent_(); +}; + + +/** + * @param {number} pixelRatio Pixel ratio. + * @return {Image} Image element. + */ +ol.style.IconImage_.prototype.getImage = function(pixelRatio) { return this.image_; }; /** - * @inheritDoc + * @return {ol.style.ImageState} Image state. */ -ol.style.Icon.prototype.getHitDetectionImage = function(pixelRatio) { +ol.style.IconImage_.prototype.getImageState = function() { + return this.imageState_; +}; + + +/** + * @param {number} pixelRatio Pixel ratio. + * @return {Image|HTMLCanvasElement} Image element. + */ +ol.style.IconImage_.prototype.getHitDetectionImage = function(pixelRatio) { if (goog.isNull(this.hitDetectionImage_)) { if (this.tainting_) { var canvas = /** @type {HTMLCanvasElement} */ (goog.dom.createElement(goog.dom.TagName.CANVAS)); - var width = this.size[0]; - var height = this.size[1]; + var width = this.size_[0]; + var height = this.size_[1]; canvas.width = width; canvas.height = height; var context = /** @type {CanvasRenderingContext2D} */ @@ -200,10 +350,18 @@ ol.style.Icon.prototype.getHitDetectionImage = function(pixelRatio) { }; +/** + * @return {ol.Size} Image size. + */ +ol.style.IconImage_.prototype.getSize = function() { + return this.size_; +}; + + /** * @return {string|undefined} Image src. */ -ol.style.Icon.prototype.getSrc = function() { +ol.style.IconImage_.prototype.getSrc = function() { return this.src_; }; @@ -211,11 +369,11 @@ ol.style.Icon.prototype.getSrc = function() { /** * Load not yet loaded URI. */ -ol.style.Icon.prototype.load = function() { - if (this.imageState == ol.style.ImageState.IDLE) { +ol.style.IconImage_.prototype.load = function() { + if (this.imageState_ == ol.style.ImageState.IDLE) { goog.asserts.assert(goog.isDef(this.src_)); goog.asserts.assert(goog.isNull(this.imageListenerKeys_)); - this.imageState = ol.style.ImageState.LOADING; + this.imageState_ = ol.style.ImageState.LOADING; this.imageListenerKeys_ = [ goog.events.listenOnce(this.image_, goog.events.EventType.ERROR, this.handleImageError_, false, this), @@ -232,7 +390,7 @@ ol.style.Icon.prototype.load = function() { * * @private */ -ol.style.Icon.prototype.unlistenImage_ = function() { +ol.style.IconImage_.prototype.unlistenImage_ = function() { goog.asserts.assert(!goog.isNull(this.imageListenerKeys_)); goog.array.forEach(this.imageListenerKeys_, goog.events.unlistenByKey); this.imageListenerKeys_ = null; diff --git a/src/ol/style/imagestyle.js b/src/ol/style/imagestyle.js index 6592f81313..9063d65f99 100644 --- a/src/ol/style/imagestyle.js +++ b/src/ol/style/imagestyle.js @@ -2,9 +2,6 @@ goog.provide('ol.style.Image'); goog.provide('ol.style.ImageState'); goog.require('goog.array'); -goog.require('goog.events'); -goog.require('goog.events.EventTarget'); -goog.require('goog.events.EventType'); /** @@ -19,11 +16,8 @@ ol.style.ImageState = { /** - * @typedef {{anchor: Array., - * imageState: ol.style.ImageState, - * rotation: number, + * @typedef {{rotation: number, * scale: number, - * size: ol.Size, * snapToPixel: (boolean|undefined), * subtractViewRotation: boolean}} */ @@ -34,24 +28,9 @@ ol.style.ImageOptions; /** * @constructor * @param {ol.style.ImageOptions} options Options. - * @extends {goog.events.EventTarget} */ ol.style.Image = function(options) { - goog.base(this); - - /** - * @protected - * @type {Array.} - */ - this.anchor = options.anchor; - - /** - * @protected - * @type {ol.style.ImageState} - */ - this.imageState = options.imageState; - /** * @private * @type {number} @@ -64,12 +43,6 @@ ol.style.Image = function(options) { */ this.scale_ = options.scale; - /** - * @protected - * @type {ol.Size} - */ - this.size = options.size; - /** * @private * @type {boolean|undefined} @@ -83,31 +56,6 @@ ol.style.Image = function(options) { this.subtractViewRotation_ = options.subtractViewRotation; }; -goog.inherits(ol.style.Image, goog.events.EventTarget); - - -/** - * @protected - */ -ol.style.Image.prototype.dispatchChangeEvent = function() { - this.dispatchEvent(goog.events.EventType.CHANGE); -}; - - -/** - * @return {Array.} Anchor. - */ -ol.style.Image.prototype.getAnchor = function() { - return this.anchor; -}; - - -/** - * @return {ol.style.ImageState} Image state. - */ -ol.style.Image.prototype.getImageState = function() { - return this.imageState; -}; /** @@ -126,14 +74,6 @@ ol.style.Image.prototype.getScale = function() { }; -/** - * @return {ol.Size} Size. - */ -ol.style.Image.prototype.getSize = function() { - return this.size; -}; - - /** * @return {boolean|undefined} Snap to pixel? */ @@ -150,6 +90,12 @@ ol.style.Image.prototype.getSubtractViewRotation = function() { }; +/** + * @return {Array.} Anchor. + */ +ol.style.Image.prototype.getAnchor = goog.abstractMethod; + + /** * @param {number} pixelRatio Pixel ratio. * @return {HTMLCanvasElement|HTMLVideoElement|Image} Image element. @@ -157,6 +103,12 @@ ol.style.Image.prototype.getSubtractViewRotation = function() { ol.style.Image.prototype.getImage = goog.abstractMethod; +/** + * @return {ol.style.ImageState} Image state. + */ +ol.style.Image.prototype.getImageState = goog.abstractMethod; + + /** * @param {number} pixelRatio Pixel ratio. * @return {HTMLCanvasElement|HTMLVideoElement|Image} Image element. @@ -164,7 +116,30 @@ ol.style.Image.prototype.getImage = goog.abstractMethod; ol.style.Image.prototype.getHitDetectionImage = goog.abstractMethod; +/** + * @return {ol.Size} Size. + */ +ol.style.Image.prototype.getSize = goog.abstractMethod; + + +/** + * @param {function(this: T, goog.events.Event)} listener Listener function. + * @param {T} thisArg Value to use as `this` when executing `listener`. + * @return {goog.events.Key|undefined} Listener key. + * @template T + */ +ol.style.Image.prototype.listenImageChange = goog.abstractMethod; + + /** * Load not yet loaded URI. */ ol.style.Image.prototype.load = goog.abstractMethod; + + +/** + * @param {function(this: T, goog.events.Event)} listener Listener function. + * @param {T} thisArg Value to use as `this` when executing `listener`. + * @template T + */ +ol.style.Image.prototype.unlistenImageChange = goog.abstractMethod;