diff --git a/examples/overlay-and-popup.html b/examples/anchored-elements.html similarity index 86% rename from examples/overlay-and-popup.html rename to examples/anchored-elements.html index 067adbf8b4..5d7b25b747 100644 --- a/examples/overlay-and-popup.html +++ b/examples/anchored-elements.html @@ -80,7 +80,7 @@ margin-left: -13px; } - Overlay example + Anchored elements example
@@ -89,16 +89,16 @@
-

Overlay example

-
Demonstrates Overlays.
+

Anchored elements example

+
Demonstrates anchored elements.

See the - overlay-and-popup.js source + anchored-elements.js source to see how this is done.

-
overlay, popup, mapquest, openaerial
- +
anchored elements, overlay, popup, mapquest, openaerial
+ diff --git a/examples/overlay-and-popup.js b/examples/anchored-elements.js similarity index 86% rename from examples/overlay-and-popup.js rename to examples/anchored-elements.js index 1e19bb425b..80fac6d6bf 100644 --- a/examples/overlay-and-popup.js +++ b/examples/anchored-elements.js @@ -1,12 +1,12 @@ goog.require('goog.debug.Console'); goog.require('goog.debug.Logger'); goog.require('goog.debug.Logger.Level'); +goog.require('ol.AnchoredElement'); goog.require('ol.Collection'); goog.require('ol.Coordinate'); goog.require('ol.Map'); goog.require('ol.RendererHints'); goog.require('ol.View2D'); -goog.require('ol.overlay.Overlay'); goog.require('ol.source.MapQuestOpenAerial'); @@ -31,15 +31,15 @@ var map = new ol.Map({ }); // Vienna label -var vienna = new ol.overlay.Overlay({ +var vienna = new ol.AnchoredElement({ map: map, - coordinate: ol.Projection.transformWithCodes( + position: ol.Projection.transformWithCodes( new ol.Coordinate(16.3725, 48.208889), 'EPSG:4326', 'EPSG:3857'), element: document.getElementById('vienna') }); // Popup showing the position the user clicked -var popup = new ol.overlay.Overlay({ +var popup = new ol.AnchoredElement({ map: map, element: document.getElementById('popup') }); @@ -49,5 +49,5 @@ map.addEventListener('click', function(evt) { 'Welcome to ol3. The location you clicked was
' + ol.Coordinate.toStringHDMS(ol.Projection.transformWithCodes( coordinate, 'EPSG:3857', 'EPSG:4326')); - popup.setCoordinate(coordinate); + popup.setPosition(coordinate); }); diff --git a/src/objectliterals.exports b/src/objectliterals.exports index f22ef20690..4c16ae6b90 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -51,11 +51,11 @@ @exportObjectLiteralProperty ol.layer.LayerOptions.source ol.source.Source @exportObjectLiteralProperty ol.layer.LayerOptions.visible boolean|undefined -@exportObjectLiteral ol.overlay.OverlayOptions -@exportObjectLiteralProperty ol.overlay.OverlayOptions.coordinate ol.Coordinate|undefined -@exportObjectLiteralProperty ol.overlay.OverlayOptions.element Element|undefined -@exportObjectLiteralProperty ol.overlay.OverlayOptions.map ol.Map|undefined -@exportObjectLiteralProperty ol.overlay.OverlayOptions.positioning Array.|undefined +@exportObjectLiteral ol.AnchoredElementOptions +@exportObjectLiteralProperty ol.AnchoredElementOptions.element Element|undefined +@exportObjectLiteralProperty ol.AnchoredElementOptions.map ol.Map|undefined +@exportObjectLiteralProperty ol.AnchoredElementOptions.position ol.Coordinate|undefined +@exportObjectLiteralProperty ol.AnchoredElementOptions.positioning ol.AnchoredElementPositioning|undefined @exportObjectLiteral ol.source.BingMapsOptions @exportObjectLiteralProperty ol.source.BingMapsOptions.culture string|undefined diff --git a/src/ol/anchoredelement.exports b/src/ol/anchoredelement.exports new file mode 100644 index 0000000000..29fbb3bfc8 --- /dev/null +++ b/src/ol/anchoredelement.exports @@ -0,0 +1,7 @@ +@exportClass ol.AnchoredElement ol.AnchoredElementOptions + +@exportSymbol ol.AnchoredElementPositioning +@exportProperty ol.AnchoredElementPositioning.BOTTOM_LEFT +@exportProperty ol.AnchoredElementPositioning.BOTTOM_RIGHT +@exportProperty ol.AnchoredElementPositioning.TOP_LEFT +@exportProperty ol.AnchoredElementPositioning.TOP_RIGHT diff --git a/src/ol/anchoredelement.js b/src/ol/anchoredelement.js new file mode 100644 index 0000000000..14c60cd0fe --- /dev/null +++ b/src/ol/anchoredelement.js @@ -0,0 +1,318 @@ +goog.provide('ol.AnchoredElement'); +goog.provide('ol.AnchoredElementPositioning'); +goog.provide('ol.AnchoredElementProperty'); + +goog.require('goog.dom'); +goog.require('goog.events'); +goog.require('goog.style'); +goog.require('ol.Coordinate'); +goog.require('ol.Map'); +goog.require('ol.MapEventType'); +goog.require('ol.Object'); + + +/** + * @enum {string} + */ +ol.AnchoredElementProperty = { + ELEMENT: 'element', + MAP: 'map', + POSITION: 'position', + POSITIONING: 'positioning' +}; + + +/** + * @enum {string} + */ +ol.AnchoredElementPositioning = { + BOTTOM_LEFT: 'bottom-left', + BOTTOM_RIGHT: 'bottom-right', + TOP_LEFT: 'top-left', + TOP_RIGHT: 'top-right' +}; + + + +/** + * @constructor + * @extends {ol.Object} + * @param {ol.AnchoredElementOptions} anchoredElementOptions Anchored element + * options. + */ +ol.AnchoredElement = function(anchoredElementOptions) { + + goog.base(this); + + /** + * @private + * @type {Element} + */ + this.element_ = goog.dom.createElement(goog.dom.TagName.DIV); + this.element_.style.position = 'absolute'; + + /** + * @private + * @type {{bottom_: string, + * left_: string, + * right_: string, + * top_: string, + * visible: boolean}} + */ + this.rendered_ = { + bottom_: '', + left_: '', + right_: '', + top_: '', + visible: true + }; + + goog.events.listen( + this, ol.Object.getChangedEventType(ol.AnchoredElementProperty.ELEMENT), + this.handleElementChanged, false, this); + + goog.events.listen( + this, ol.Object.getChangedEventType(ol.AnchoredElementProperty.MAP), + this.handleMapChanged, false, this); + + goog.events.listen( + this, ol.Object.getChangedEventType(ol.AnchoredElementProperty.POSITION), + this.handlePositionChanged, false, this); + + goog.events.listen( + this, + ol.Object.getChangedEventType(ol.AnchoredElementProperty.POSITIONING), + this.handlePositioningChanged, false, this); + + if (goog.isDef(anchoredElementOptions.element)) { + this.setElement(anchoredElementOptions.element); + } + if (goog.isDef(anchoredElementOptions.position)) { + this.setPosition(anchoredElementOptions.position); + } + if (goog.isDef(anchoredElementOptions.positioning)) { + this.setPositioning(anchoredElementOptions.positioning); + } + if (goog.isDef(anchoredElementOptions.map)) { + this.setMap(anchoredElementOptions.map); + } + +}; +goog.inherits(ol.AnchoredElement, ol.Object); + + +/** + * @return {Element|undefined} Element. + */ +ol.AnchoredElement.prototype.getElement = function() { + return /** @type {Element|undefined} */ ( + this.get(ol.AnchoredElementProperty.ELEMENT)); +}; +goog.exportProperty( + ol.AnchoredElement.prototype, + 'getElement', + ol.AnchoredElement.prototype.getElement); + + +/** + * @return {ol.Map|undefined} Map. + */ +ol.AnchoredElement.prototype.getMap = function() { + return /** @type {ol.Map|undefined} */ ( + this.get(ol.AnchoredElementProperty.MAP)); +}; +goog.exportProperty( + ol.AnchoredElement.prototype, + 'getMap', + ol.AnchoredElement.prototype.getMap); + + +/** + * @return {ol.Coordinate|undefined} Position. + */ +ol.AnchoredElement.prototype.getPosition = function() { + return /** @type {ol.Coordinate|undefined} */ ( + this.get(ol.AnchoredElementProperty.POSITION)); +}; +goog.exportProperty( + ol.AnchoredElement.prototype, + 'getPosition', + ol.AnchoredElement.prototype.getPosition); + + +/** + * @return {ol.AnchoredElementPositioning|undefined} Positioning. + */ +ol.AnchoredElement.prototype.getPositioning = function() { + return /** @type {ol.AnchoredElementPositioning|undefined} */ ( + this.get(ol.AnchoredElementProperty.POSITIONING)); +}; +goog.exportProperty( + ol.AnchoredElement.prototype, + 'getPositioning', + ol.AnchoredElement.prototype.getPositioning); + + +/** + * @protected + */ +ol.AnchoredElement.prototype.handleElementChanged = function() { + goog.dom.removeChildren(this.element_); + var element = this.getElement(); + if (goog.isDefAndNotNull(element)) { + goog.dom.append(/** @type {!Node} */ (this.element_), element); + } +}; + + +/** + * @protected + */ +ol.AnchoredElement.prototype.handleMapChanged = function() { + if (!goog.isNull(this.mapPostrenderListenerKey_)) { + goog.dom.removeNode(this.element_); + goog.events.unlistenByKey(this.mapPostrenderListenerKey_); + this.mapPostrenderListenerKey_ = null; + } + var map = this.getMap(); + if (goog.isDefAndNotNull(map)) { + this.mapPostrenderListenerKey_ = goog.events.listen(map, + ol.MapEventType.POSTRENDER, this.handleMapPostrender, false, this); + this.updatePixelPosition_(); + goog.dom.append( + /** @type {!Node} */ (map.getOverlayContainer()), this.element_); + } +}; + + +/** + * @protected + */ +ol.AnchoredElement.prototype.handleMapPostrender = function() { + this.updatePixelPosition_(); +}; + + +/** + * @protected + */ +ol.AnchoredElement.prototype.handlePositionChanged = function() { + this.updatePixelPosition_(); +}; + + +/** + * @protected + */ +ol.AnchoredElement.prototype.handlePositioningChanged = function() { + this.updatePixelPosition_(); +}; + + +/** + * @param {Element|undefined} element Element. + */ +ol.AnchoredElement.prototype.setElement = function(element) { + this.set(ol.AnchoredElementProperty.ELEMENT, element); +}; +goog.exportProperty( + ol.AnchoredElement.prototype, + 'setElement', + ol.AnchoredElement.prototype.setElement); + + +/** + * @param {ol.Map|undefined} map Map. + */ +ol.AnchoredElement.prototype.setMap = function(map) { + this.set(ol.AnchoredElementProperty.MAP, map); +}; +goog.exportProperty( + ol.AnchoredElement.prototype, + 'setMap', + ol.AnchoredElement.prototype.setMap); + + +/** + * @param {ol.Coordinate|undefined} position Position. + */ +ol.AnchoredElement.prototype.setPosition = function(position) { + this.set(ol.AnchoredElementProperty.POSITION, position); +}; +goog.exportProperty( + ol.AnchoredElement.prototype, + 'setPosition', + ol.AnchoredElement.prototype.setPosition); + + +/** + * @param {ol.AnchoredElementPositioning|undefined} positioning Positioning. + */ +ol.AnchoredElement.prototype.setPositioning = function(positioning) { + this.set(ol.AnchoredElementProperty.POSITIONING, positioning); +}; + + +/** + * @private + */ +ol.AnchoredElement.prototype.updatePixelPosition_ = function() { + + var map = this.getMap(); + var position = this.getPosition(); + if (!goog.isDef(map) || !map.isDef() || !goog.isDef(position)) { + if (this.rendered_.visible) { + goog.style.showElement(this.element_, false); + this.rendered_.visible = false; + } + return; + } + + var pixel = map.getPixelFromCoordinate(position); + var mapSize = map.getSize(); + goog.asserts.assert(goog.isDef(mapSize)); + var style = this.element_.style; + var positioning = this.getPositioning(); + if (positioning == ol.AnchoredElementPositioning.BOTTOM_RIGHT || + positioning == ol.AnchoredElementPositioning.TOP_RIGHT) { + if (this.rendered_.left_ !== '') { + this.rendered_.left_ = style.left = ''; + } + var right = Math.round(mapSize.width - pixel.x) + 'px'; + if (this.rendered_.right_ != right) { + this.rendered_.right_ = style.right = right; + } + } else { + if (this.rendered_.right_ !== '') { + this.rendered_.right_ = style.right = ''; + } + var left = Math.round(pixel.x) + 'px'; + if (this.rendered_.left_ != left) { + this.rendered_.left_ = style.left = left; + } + } + if (positioning == ol.AnchoredElementPositioning.TOP_LEFT || + positioning == ol.AnchoredElementPositioning.TOP_RIGHT) { + if (this.rendered_.bottom_ !== '') { + this.rendered_.bottom_ = style.bottom = ''; + } + var top = Math.round(pixel.y) + 'px'; + if (this.rendered_.top_ != top) { + this.rendered_.top_ = style.top = top; + } + } else { + if (this.rendered_.top_ !== '') { + this.rendered_.top_ = style.top = ''; + } + var bottom = Math.round(mapSize.height - pixel.y) + 'px'; + if (this.rendered_.bottom_ != bottom) { + this.rendered_.bottom_ = style.bottom = bottom; + } + } + + if (!this.rendered_.visible) { + goog.style.showElement(this.element_, true); + this.rendered_.visible = true; + } + +}; diff --git a/src/ol/overlay/overlay.exports b/src/ol/overlay/overlay.exports deleted file mode 100644 index b60b8a16a0..0000000000 --- a/src/ol/overlay/overlay.exports +++ /dev/null @@ -1,5 +0,0 @@ -@exportClass ol.overlay.Overlay ol.overlay.OverlayOptions -@exportProperty ol.overlay.Overlay.prototype.getElement -@exportProperty ol.overlay.Overlay.prototype.setCoordinate -@exportProperty ol.overlay.Overlay.prototype.setMap - diff --git a/src/ol/overlay/overlay.js b/src/ol/overlay/overlay.js deleted file mode 100644 index 8c878fb3e2..0000000000 --- a/src/ol/overlay/overlay.js +++ /dev/null @@ -1,211 +0,0 @@ -goog.provide('ol.overlay.Overlay'); -goog.provide('ol.overlay.OverlayPositioning'); - -goog.require('goog.array'); -goog.require('goog.events'); -goog.require('goog.style'); - - - -/** - * @constructor - * @param {ol.overlay.OverlayOptions} overlayOptions Overlay options. - */ -ol.overlay.Overlay = function(overlayOptions) { - - /** - * @type {ol.Coordinate} - * @private - */ - this.coordinate_ = null; - - /** - * @type {Element} - * @private - */ - this.element_ = null; - - /** - * @private - * @type {ol.Map} - */ - this.map_ = null; - - /** - * @type {Array.} - * @private - */ - this.positioning_ = [ - ol.overlay.OverlayPositioning.LEFT, - ol.overlay.OverlayPositioning.BOTTOM - ]; - - /** - * @private - * @type {Array.} - */ - this.mapListenerKeys_ = null; - - /** - * @private - * @type {Array.} - */ - this.viewListenerKeys_ = null; - - if (goog.isDef(overlayOptions.coordinate)) { - this.setCoordinate(overlayOptions.coordinate); - } - if (goog.isDef(overlayOptions.element)) { - this.setElement(overlayOptions.element); - } - if (goog.isDef(overlayOptions.map)) { - this.setMap(overlayOptions.map); - } - if (goog.isDef(overlayOptions.positioning)) { - this.setPositioning(overlayOptions.positioning); - } -}; - - -/** - * @private - */ -ol.overlay.Overlay.prototype.handleViewChanged_ = function() { - goog.asserts.assert(!goog.isNull(this.map_)); - if (!goog.isNull(this.viewListenerKeys_)) { - goog.array.forEach(this.viewListenerKeys_, goog.events.unlistenByKey); - this.viewListenerKeys_ = null; - } - var view = this.map_.getView(); - if (goog.isDefAndNotNull(view)) { - // FIXME works for View2D only - goog.asserts.assert(view instanceof ol.View2D); - this.viewListenerKeys_ = [ - goog.events.listen( - view, ol.Object.getChangedEventType(ol.View2DProperty.CENTER), - this.updatePixelPosition_, false, this), - - goog.events.listen( - view, ol.Object.getChangedEventType(ol.View2DProperty.RESOLUTION), - this.updatePixelPosition_, false, this), - - goog.events.listen( - view, ol.Object.getChangedEventType(ol.View2DProperty.ROTATION), - this.updatePixelPosition_, false, this) - ]; - this.updatePixelPosition_(); - } -}; - - -/** - * @param {ol.Coordinate} coordinate Coordinate for the overlay's position on - * the map. - */ -ol.overlay.Overlay.prototype.setCoordinate = function(coordinate) { - this.coordinate_ = coordinate; - this.updatePixelPosition_(); -}; - - -/** - * @param {Element} element The DOM element for the overlay. - */ -ol.overlay.Overlay.prototype.setElement = function(element) { - if (this.element_) { - goog.dom.removeNode(this.element_); - } - this.element_ = element; - if (this.map_) { - goog.style.setStyle(this.element_, 'position', 'absolute'); - goog.dom.append(/** @type {!Node} */ (this.map_.getOverlayContainer()), - this.element_); - } - this.updatePixelPosition_(); -}; - - -/** - * @return {Element} The DOM element for the overlay. - */ -ol.overlay.Overlay.prototype.getElement = function() { - return this.element_; -}; - - -/** - * @param {ol.Map} map Map. - */ -ol.overlay.Overlay.prototype.setMap = function(map) { - this.map_ = map; - if (!goog.isNull(this.mapListenerKeys_)) { - goog.array.forEach(this.mapListenerKeys_, goog.events.unlistenByKey); - this.mapListenerKeys_ = null; - } - if (this.element_) { - this.setElement(this.element_); - } - if (goog.isDefAndNotNull(map)) { - this.mapListenerKeys_ = [ - goog.events.listen( - map, ol.Object.getChangedEventType(ol.MapProperty.SIZE), - this.updatePixelPosition_, false, this), - goog.events.listen( - map, ol.Object.getChangedEventType(ol.MapProperty.VIEW), - this.handleViewChanged_, false, this) - ]; - this.handleViewChanged_(); - } -}; - - -/** - * @return {ol.Map} Map. - */ -ol.overlay.Overlay.prototype.getMap = function() { - return this.map_; -}; - - -/** - * Set the CSS properties to use for x- and y-positioning of the element. If - * not set, the default is {@code ['left', 'bottom']}. - * @param {Array.} positioning The positioning. - */ -ol.overlay.Overlay.prototype.setPositioning = function(positioning) { - this.positioning_ = positioning; - this.updatePixelPosition_(); -}; - - -/** - * @private - */ -ol.overlay.Overlay.prototype.updatePixelPosition_ = function() { - if (!goog.isNull(this.map_) && !goog.isNull(this.coordinate_) && - !goog.isNull(this.element_)) { - var pixel = this.map_.getPixelFromCoordinate(this.coordinate_); - var mapSize = this.map_.get(ol.MapProperty.SIZE); - var x = Math.round(pixel.x); - if (this.positioning_[0] === ol.overlay.OverlayPositioning.RIGHT) { - x = mapSize.width - x; - } - var y = Math.round(pixel.y); - if (this.positioning_[1] === ol.overlay.OverlayPositioning.BOTTOM) { - y = mapSize.height - y; - } - goog.style.setStyle(this.element_, this.positioning_[0], x + 'px'); - goog.style.setStyle(this.element_, this.positioning_[1], y + 'px'); - } -}; - - -/** - * @enum {string} - */ -ol.overlay.OverlayPositioning = { - LEFT: 'left', - RIGHT: 'right', - TOP: 'top', - BOTTOM: 'bottom' -};