/**
* @name BoundsBox
* @author Esa
* @copyright (c) 2009 Esa I Ojala
* @fileoverview BoundsBox is an extension to Gmaps api v3 for creating rectangle overlays.
* Facilities to create and change image overlays and control opacity and zIndex are provided.
* Events: click, imageloaded and imageloaderror
*/
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @version 1.1b
* 1.1b getPosition() added, get_position() deprecated
* 1.1 KmBox constructor added
* 1.0 image handling, opacity and zIndex were added
* 0.1 first release
*/
var BOUNDS_BOX_VERSION = "1.1b";
/**
* @constructor BoundsBox()
* @extends OverlayView
* @param {Map} map The map where the BoundsBox shall be overlayed
* @param {LatLngBounds} bounds Bounds of the overlay
* @param {Object} opt_options Optional object. Supported properties: html, className, fit, imageSrc, pane, zIndex
*/
function BoundsBox(map, bounds, opt_options) {
this.bounds_ = bounds;
this.setMap(map);
this.opts = opt_options || {};
if(this.opts.fit) map.fitBounds(bounds);
this.map = map;
};
BoundsBox.prototype = new google.maps.OverlayView();
/**
* @private Map calls draw() internally when needed
*/
BoundsBox.prototype.draw = function() {
var me = this;
var div = this.div_;
var image = this.image_;
if (!div) { // sets the div only when draw() is called first time
div = this.div_ = document.createElement('div');
div.style.position = "absolute";
div.style.overflow = "hidden";
this.zIndex = this.opts.zIndex || 0;
div.style.zIndex = this.zIndex;
div.className = this.opts.cssClass || this.opts.className || 'bounds-box';
div.innerHTML = this.opts.html || "";
google.maps.event.addDomListener(div, "click", function(event) {
google.maps.event.trigger(me, "click", event);
});
var panes = this.getPanes();
var paneId = this.opts.pane || "overlayLayer";
panes[paneId].appendChild(div);
if(this.opts.imageSrc){
image = this.image_ = new Image();
image.src = this.opts.imageSrc;
image.alt = "";
image.onload = function(){
google.maps.event.trigger(me, "imageloaded");
}
image.onerror = function(){
google.maps.event.trigger(me, "imageloaderror");
}
div.appendChild(image);
}
this.opacity = this.opts.opacity * 1 || 1;
this.div_.style.filter = 'alpha(opacity:' + this.opacity*100 + ')';
this.div_.style.opacity = this.opacity;
} // Positions and dimensions the overlay every time draw() is called
var pixSW = this.getProjection().fromLatLngToDivPixel(this.bounds_.getSouthWest());
var pixNE = this.getProjection().fromLatLngToDivPixel(this.bounds_.getNorthEast());
this.divSize = new google.maps.Size((pixNE.x-pixSW.x), (pixSW.y-pixNE.y));
div.style.left = pixSW.x + 'px';
div.style.top = pixNE.y + 'px';
div.style.width = this.divSize.width + "px";
div.style.height = this.divSize.height + "px";
div.style.zIndex = this.zIndex;
if(!!image){
image.width = this.divSize.width;
image.height = this.divSize.height;
}
this.image_ = image;
};
/**
* Removes the div from DOM
* @returns true if success, false if the div was not found
*/
BoundsBox.prototype.remove = function() {
if(!this.div_) return false;
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
return true;
};
/**
* Overrides previous CSS className
* @param {String}
* @returns true if success, false if the div was not found
*/
BoundsBox.prototype.setClassName = function(cssClass) {
if(!this.div_) return false;
this.div_.className = cssClass;
return true;
};
/**
* Sets innerHTML of the div
* @param {String}
* @returns true if success, false if the div was not found
*/
BoundsBox.prototype.setContent = function(html) {
if(!this.div_) return false;
this.div_.innerHTML = html;
return true;
};
/**
* Sets src attribute of the optional image object
* @param {String} URL of an image file
* @returns true if success, false if the image was not found
*/
BoundsBox.prototype.setImageSrc = function(src) {
if(!this.image_) return false;
this.image_.src = src;
return true;
};
/**
* Sets a new opacity value for divs style object
* @param number between 0...1
* @returns true if success, false if the div was not found
*/
BoundsBox.prototype.setOpacity = function(opacity) {
if(!this.div_) return false;
this.div_.style.filter = 'alpha(opacity:' + opacity*100 + ')';
this.div_.style.opacity = opacity * 1;
this.opacity = opacity * 1;
return true;
};
/**
* Gives a new zIndex value for divs style object
* @param number
* @returns true if success, false if the div was not found
*/
BoundsBox.prototype.setZIndex = function(zIndex) {
if(!this.div_) return false;
this.div_.style.zIndex = zIndex;
this.zIndex = zIndex;
return true;
};
/**
* @returns The center point of the div
* @type LatLng object (null if the div was not found)
*/
BoundsBox.prototype.getPosition = function() {
if(!this.div_) return null;
return this.bounds_.getCenter();
};
BoundsBox.prototype.get_position = function() { // deprecated
if(!this.div_) return null;
return this.bounds_.getCenter();
};
/**
* @returns innerHTML of the div (null if the div was not found)
* @type String
*/
BoundsBox.prototype.getContent = function() {
if(!this.div_) return null;
return this.div_.innerHTML;
};
/**
* @returns Reference to the div node (null if the div was not found)
* @type html object
*/
BoundsBox.prototype.getDiv = function() {
if(!this.div_) return null;
return this.div_;
};
/**
* @returns Pixel dimensions of of the div (null if the div was not found)
* @type Size object
*/
BoundsBox.prototype.getSize = function() {
if(!this.div_) return null;
return this.divSize;
};
/**
* @returns Reference to optional image node (null if the image was not found)
* @type Image object
*/
BoundsBox.prototype.getImage = function() {
if(!this.image_) return null;
return this.image_;
};
/**
* @returns The current CSS className of the div (null if the div was not found)
* @type String
*/
BoundsBox.prototype.getClassName = function() {
if(!this.div_) return null;
return this.div_.className;
};
/**
* @returns The current zIndex value (null if the div was not found)
* @type number
*/
BoundsBox.prototype.getZIndex = function() {
if(!this.div_) return null;
return this.zIndex;
};
/**
* @returns Bounds of the overlay
* @type LatLngBounds object
*/
BoundsBox.prototype.getBounds = function() {
return this.bounds_;
};
/////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* destinationLatLng() method for LatLng
* Calculates destination point from this point towards bearing (deg) in distance (km)
* @param {number} bearing The direction in degrees
* @param {number} dist Distance from this point in kilometers
* @returns destination point
* @type LatLng object
*/
google.maps.LatLng.prototype.destinationLatLng = function(bearing, dist) {
var R = 6371; // earth's mean radius in km
var toRad = Math.PI/180;
var lat1 = this.lat() * toRad;
var lng1 = this.lng() * toRad;
bearing = bearing * toRad;
var lat2 = Math.asin( Math.sin(lat1)*Math.cos(dist/R) +
Math.cos(lat1)*Math.sin(dist/R)*Math.cos(bearing) );
var lng2 = lng1 + Math.atan2(Math.sin(bearing)*Math.sin(dist/R)*Math.cos(lat1),
Math.cos(dist/R)-Math.sin(lat1)*Math.sin(lat2));
lng2 = (lng2+Math.PI)%(2*Math.PI) - Math.PI; // normalise to -180...+180
var pint = new google.maps.LatLng(lat2 / toRad, lng2 / toRad);
return pint;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
/**
* @constructor KmBox()
* @extends OverlayView
* @param {Map} map The map where the KmBox shall be overlayed
* @param {number} kmX Width of the overlay in kilometers
* @param {number} kmY Optional height of the overlay in kilometers. Default = kmX
* @param {Object} opt_options Optional object. Supported properties: html, className, fit, imageSrc, pane, zIndex, kmX, kmY, miX, miY
*/
function KmBox(map, point, opt_options) {
this.opts = opt_options || {};
var kmX = this.opts.kmX || this.opts.miX * 1.609344 || 1;
var kmY = this.opts.kmY || this.opts.miY * 1.609344 || kmX;
var west = point.destinationLatLng(270, kmX / 2).lng();
var east = point.destinationLatLng(90, kmX / 2).lng();
var north = point.destinationLatLng(0, kmY / 2).lat();
var south = point.destinationLatLng(180, kmY / 2).lat();
this.bounds_ = new google.maps.LatLngBounds();
this.bounds_.extend(new google.maps.LatLng(south, west));
this.bounds_.extend(new google.maps.LatLng(north, east));
this.setMap(map);
if(this.opts.fit) map.fitBounds(this.bounds_);
this.map = map;
};
/**
* Clone all the methods of BoundsBox()
*/
KmBox.prototype = BoundsBox.prototype;