/**
* @requires OpenLayers/BaseTypes/Class.js
* @requires OpenLayers/Util.js
* @requires OpenLayers/Control.js
* @requires OpenLayers/Format.js
* @requires OpenLayers/Request.js
* @requires OpenLayers/Layer/WMS.js
* @requires OpenLayers/Layer/MapServer.js
* @requires OpenLayers/Tile.js
* @requires OpenLayers/Request/XMLHttpRequest.js
* @requires OpenLayers/Layer/Vector.js
* @requires OpenLayers/Layer/Markers.js
* @requires OpenLayers/Console.js
* @requires OpenLayers/Lang.js
* @requires OpenLayers/Feature.js
* @requires OpenLayers/Layer/EventPane.js
* @requires OpenLayers/Layer/FixedZoomLevels.js
* @requires OpenLayers/Layer/SphericalMercator.js
* @requires OpenLayers/Protocol.js
* @requires OpenLayers/Format/JSON.js
* @requires OpenLayers/Format/WKT.js
* @requires OpenLayers/Format/XML.js
* @requires OpenLayers/Geometry.js
* @requires OpenLayers/Renderer/Elements.js
* @requires OpenLayers/Popup/Anchored.js
* @requires Rico/Corner.js
*/
/**
* About: Deprecated
* The deprecated.js script includes all methods, properties, and constructors
* that are not supported as part of the long-term API. If you use any of
* these, you have to explicitly include this script in your application.
*
* For example:
* (code)
* <script src="deprecated.js" type="text/javascript"></script>
* (end)
*
* You are strongly encouraged to avoid using deprecated functionality. The
* documentation here should point you to the supported alternatives.
*/
/**
* Namespace: OpenLayers.Class
*/
/**
* Property: isPrototype
* *Deprecated*. This is no longer needed and will be removed at 3.0.
*/
OpenLayers.Class.isPrototype = function () {};
/**
* APIFunction: OpenLayers.create
* *Deprecated*. Old method to create an OpenLayers style class. Use the
* <OpenLayers.Class> constructor instead.
*
* Returns:
* An OpenLayers class
*/
OpenLayers.Class.create = function() {
return function() {
if (arguments && arguments[0] != OpenLayers.Class.isPrototype) {
this.initialize.apply(this, arguments);
}
};
};
/**
* APIFunction: inherit
* *Deprecated*. Old method to inherit from one or more OpenLayers style
* classes. Use the <OpenLayers.Class> constructor instead.
*
* Parameters:
* class - One or more classes can be provided as arguments
*
* Returns:
* An object prototype
*/
OpenLayers.Class.inherit = function (P) {
var C = function() {
P.call(this);
};
var newArgs = [C].concat(Array.prototype.slice.call(arguments));
OpenLayers.inherit.apply(null, newArgs);
return C.prototype;
};
/**
* Namespace: OpenLayers.Util
*/
/**
* Function: clearArray
* *Deprecated*. This function will disappear in 3.0.
* Please use "array.length = 0" instead.
*
* Parameters:
* array - {Array}
*/
OpenLayers.Util.clearArray = function(array) {
OpenLayers.Console.warn(
OpenLayers.i18n(
"methodDeprecated", {'newMethod': 'array = []'}
)
);
array.length = 0;
};
/**
* Function: setOpacity
* *Deprecated*. This function has been deprecated. Instead, please use
* <OpenLayers.Util.modifyDOMElement>
* or
* <OpenLayers.Util.modifyAlphaImageDiv>
*
* Set the opacity of a DOM Element
* Note that for this function to work in IE, elements must "have layout"
* according to:
* http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/haslayout.asp
*
* Parameters:
* element - {DOMElement} Set the opacity on this DOM element
* opacity - {Float} Opacity value (0.0 - 1.0)
*/
OpenLayers.Util.setOpacity = function(element, opacity) {
OpenLayers.Util.modifyDOMElement(element, null, null, null,
null, null, null, opacity);
};
/**
* Function: safeStopPropagation
* *Deprecated*. This function has been deprecated. Please use directly
* <OpenLayers.Event.stop> passing 'true' as the 2nd
* argument (preventDefault)
*
* Safely stop the propagation of an event *without* preventing
* the default browser action from occurring.
*
* Parameters:
* evt - {Event}
*/
OpenLayers.Util.safeStopPropagation = function(evt) {
OpenLayers.Event.stop(evt, true);
};
/**
* Function: getArgs
* *Deprecated*. Will be removed in 3.0. Please use instead
* <OpenLayers.Util.getParameters>
*
* Parameters:
* url - {String} Optional url used to extract the query string.
* If null, query string is taken from page location.
*
* Returns:
* {Object} An object of key/value pairs from the query string.
*/
OpenLayers.Util.getArgs = function(url) {
OpenLayers.Console.warn(
OpenLayers.i18n(
"methodDeprecated", {'newMethod': 'OpenLayers.Util.getParameters'}
)
);
return OpenLayers.Util.getParameters(url);
};
/**
* Maintain existing definition of $.
*
* The use of our $-method is deprecated and the mapping of
* OpenLayers.Util.getElement will eventually be removed. Do not depend on
* window.$ being defined by OpenLayers.
*/
if(typeof window.$ === "undefined") {
window.$ = OpenLayers.Util.getElement;
}
/**
* Namespace: OpenLayers.Ajax
*/
/**
* Function: OpenLayers.nullHandler
* @param {} request
*/
OpenLayers.nullHandler = function(request) {
OpenLayers.Console.userError(OpenLayers.i18n("unhandledRequest", {'statusText':request.statusText}));
};
/**
* APIFunction: OpenLayers.loadURL
* Background load a document.
* *Deprecated*. Use <OpenLayers.Request.GET> method instead.
*
* Parameters:
* uri - {String} URI of source doc
* params - {String} or {Object} GET params. Either a string in the form
* "?hello=world&foo=bar" (do not forget the leading question mark)
* or an object in the form {'hello': 'world', 'foo': 'bar}
* caller - {Object} object which gets callbacks
* onComplete - {Function} Optional callback for success. The callback
* will be called with this set to caller and will receive the request
* object as an argument. Note that if you do not specify an onComplete
* function, <OpenLayers.nullHandler> will be called (which pops up a
* user friendly error message dialog).
* onFailure - {Function} Optional callback for failure. In the event of
* a failure, the callback will be called with this set to caller and will
* receive the request object as an argument. Note that if you do not
* specify an onComplete function, <OpenLayers.nullHandler> will be called
* (which pops up a user friendly error message dialog).
*
* Returns:
* {<OpenLayers.Request.XMLHttpRequest>} The request object. To abort loading,
* call request.abort().
*/
OpenLayers.loadURL = function(uri, params, caller,
onComplete, onFailure) {
if(typeof params == 'string') {
params = OpenLayers.Util.getParameters(params);
}
var success = (onComplete) ? onComplete : OpenLayers.nullHandler;
var failure = (onFailure) ? onFailure : OpenLayers.nullHandler;
return OpenLayers.Request.GET({
url: uri, params: params,
success: success, failure: failure, scope: caller
});
};
/**
* Function: OpenLayers.parseXMLString
* Parse XML into a doc structure
*
* Parameters:
* text - {String}
*
* Returns:
* {?} Parsed AJAX Responsev
*/
OpenLayers.parseXMLString = function(text) {
//MS sucks, if the server is bad it dies
var index = text.indexOf('<');
if (index > 0) {
text = text.substring(index);
}
var ajaxResponse = OpenLayers.Util.Try(
function() {
var xmldom = new ActiveXObject('Microsoft.XMLDOM');
xmldom.loadXML(text);
return xmldom;
},
function() {
return new DOMParser().parseFromString(text, 'text/xml');
},
function() {
var req = new XMLHttpRequest();
req.open("GET", "data:" + "text/xml" +
";charset=utf-8," + encodeURIComponent(text), false);
if (req.overrideMimeType) {
req.overrideMimeType("text/xml");
}
req.send(null);
return req.responseXML;
}
);
return ajaxResponse;
};
OpenLayers.Ajax = {
/**
* Method: emptyFunction
*/
emptyFunction: function () {},
/**
* Method: getTransport
*
* Returns:
* {Object} Transport mechanism for whichever browser we're in, or false if
* none available.
*/
getTransport: function() {
return OpenLayers.Util.Try(
function() {return new XMLHttpRequest();},
function() {return new ActiveXObject('Msxml2.XMLHTTP');},
function() {return new ActiveXObject('Microsoft.XMLHTTP');}
) || false;
},
/**
* Property: activeRequestCount
* {Integer}
*/
activeRequestCount: 0
};
/**
* Namespace: OpenLayers.Ajax.Responders
* {Object}
*/
OpenLayers.Ajax.Responders = {
/**
* Property: responders
* {Array}
*/
responders: [],
/**
* Method: register
*
* Parameters:
* responderToAdd - {?}
*/
register: function(responderToAdd) {
for (var i = 0; i < this.responders.length; i++){
if (responderToAdd == this.responders[i]){
return;
}
}
this.responders.push(responderToAdd);
},
/**
* Method: unregister
*
* Parameters:
* responderToRemove - {?}
*/
unregister: function(responderToRemove) {
OpenLayers.Util.removeItem(this.reponders, responderToRemove);
},
/**
* Method: dispatch
*
* Parameters:
* callback - {?}
* request - {?}
* transport - {?}
*/
dispatch: function(callback, request, transport) {
var responder;
for (var i = 0; i < this.responders.length; i++) {
responder = this.responders[i];
if (responder[callback] &&
typeof responder[callback] == 'function') {
try {
responder[callback].apply(responder,
[request, transport]);
} catch (e) {}
}
}
}
};
OpenLayers.Ajax.Responders.register({
/**
* Function: onCreate
*/
onCreate: function() {
OpenLayers.Ajax.activeRequestCount++;
},
/**
* Function: onComplete
*/
onComplete: function() {
OpenLayers.Ajax.activeRequestCount--;
}
});
/**
* Class: OpenLayers.Ajax.Base
*/
OpenLayers.Ajax.Base = OpenLayers.Class({
/**
* Constructor: OpenLayers.Ajax.Base
*
* Parameters:
* options - {Object}
*/
initialize: function(options) {
this.options = {
method: 'post',
asynchronous: true,
contentType: 'application/xml',
parameters: ''
};
OpenLayers.Util.extend(this.options, options || {});
this.options.method = this.options.method.toLowerCase();
if (typeof this.options.parameters == 'string') {
this.options.parameters =
OpenLayers.Util.getParameters(this.options.parameters);
}
}
});
/**
* Class: OpenLayers.Ajax.Request
* *Deprecated*. Use <OpenLayers.Request> method instead.
*
* Inherit:
* - <OpenLayers.Ajax.Base>
*/
OpenLayers.Ajax.Request = OpenLayers.Class(OpenLayers.Ajax.Base, {
/**
* Property: _complete
*
* {Boolean}
*/
_complete: false,
/**
* Constructor: OpenLayers.Ajax.Request
*
* Parameters:
* url - {String}
* options - {Object}
*/
initialize: function(url, options) {
OpenLayers.Ajax.Base.prototype.initialize.apply(this, [options]);
if (OpenLayers.ProxyHost && OpenLayers.String.startsWith(url, "http")) {
url = OpenLayers.ProxyHost + encodeURIComponent(url);
}
this.transport = OpenLayers.Ajax.getTransport();
this.request(url);
},
/**
* Method: request
*
* Parameters:
* url - {String}
*/
request: function(url) {
this.url = url;
this.method = this.options.method;
var params = OpenLayers.Util.extend({}, this.options.parameters);
if (this.method != 'get' && this.method != 'post') {
// simulate other verbs over post
params['_method'] = this.method;
this.method = 'post';
}
this.parameters = params;
if (params = OpenLayers.Util.getParameterString(params)) {
// when GET, append parameters to URL
if (this.method == 'get') {
this.url += ((this.url.indexOf('?') > -1) ? '&' : '?') + params;
} else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
params += '&_=';
}
}
try {
var response = new OpenLayers.Ajax.Response(this);
if (this.options.onCreate) {
this.options.onCreate(response);
}
OpenLayers.Ajax.Responders.dispatch('onCreate',
this,
response);
this.transport.open(this.method.toUpperCase(),
this.url,
this.options.asynchronous);
if (this.options.asynchronous) {
window.setTimeout(
OpenLayers.Function.bind(this.respondToReadyState, this, 1),
10);
}
this.transport.onreadystatechange =
OpenLayers.Function.bind(this.onStateChange, this);
this.setRequestHeaders();
this.body = this.method == 'post' ?
(this.options.postBody || params) : null;
this.transport.send(this.body);
// Force Firefox to handle ready state 4 for synchronous requests
if (!this.options.asynchronous &&
this.transport.overrideMimeType) {
this.onStateChange();
}
} catch (e) {
this.dispatchException(e);
}
},
/**
* Method: onStateChange
*/
onStateChange: function() {
var readyState = this.transport.readyState;
if (readyState > 1 && !((readyState == 4) && this._complete)) {
this.respondToReadyState(this.transport.readyState);
}
},
/**
* Method: setRequestHeaders
*/
setRequestHeaders: function() {
var headers = {
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'text/javascript, text/html, application/xml, text/xml, */*',
'OpenLayers': true
};
if (this.method == 'post') {
headers['Content-type'] = this.options.contentType +
(this.options.encoding ? '; charset=' + this.options.encoding : '');
/* Force "Connection: close" for older Mozilla browsers to work
* around a bug where XMLHttpRequest sends an incorrect
* Content-length header. See Mozilla Bugzilla #246651.
*/
if (this.transport.overrideMimeType &&
(navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) {
headers['Connection'] = 'close';
}
}
// user-defined headers
if (typeof this.options.requestHeaders == 'object') {
var extras = this.options.requestHeaders;
if (typeof extras.push == 'function') {
for (var i = 0, length = extras.length; i < length; i += 2) {
headers[extras[i]] = extras[i+1];
}
} else {
for (var i in extras) {
headers[i] = extras[i];
}
}
}
for (var name in headers) {
this.transport.setRequestHeader(name, headers[name]);
}
},
/**
* Method: success
*
* Returns:
* {Boolean} -
*/
success: function() {
var status = this.getStatus();
return !status || (status >=200 && status < 300);
},
/**
* Method: getStatus
*
* Returns:
* {Integer} - Status
*/
getStatus: function() {
try {
return this.transport.status || 0;
} catch (e) {
return 0;
}
},
/**
* Method: respondToReadyState
*
* Parameters:
* readyState - {?}
*/
respondToReadyState: function(readyState) {
var state = OpenLayers.Ajax.Request.Events[readyState];
var response = new OpenLayers.Ajax.Response(this);
if (state == 'Complete') {
try {
this._complete = true;
(this.options['on' + response.status] ||
this.options['on' + (this.success() ? 'Success' : 'Failure')] ||
OpenLayers.Ajax.emptyFunction)(response);
} catch (e) {
this.dispatchException(e);
}
var contentType = response.getHeader('Content-type');
}
try {
(this.options['on' + state] ||
OpenLayers.Ajax.emptyFunction)(response);
OpenLayers.Ajax.Responders.dispatch('on' + state,
this,
response);
} catch (e) {
this.dispatchException(e);
}
if (state == 'Complete') {
// avoid memory leak in MSIE: clean up
this.transport.onreadystatechange = OpenLayers.Ajax.emptyFunction;
}
},
/**
* Method: getHeader
*
* Parameters:
* name - {String} Header name
*
* Returns:
* {?} - response header for the given name
*/
getHeader: function(name) {
try {
return this.transport.getResponseHeader(name);
} catch (e) {
return null;
}
},
/**
* Method: dispatchException
* If the optional onException function is set, execute it
* and then dispatch the call to any other listener registered
* for onException.
*
* If no optional onException function is set, we suspect that
* the user may have also not used
* OpenLayers.Ajax.Responders.register to register a listener
* for the onException call. To make sure that something
* gets done with this exception, only dispatch the call if there
* are listeners.
*
* If you explicitly want to swallow exceptions, set
* request.options.onException to an empty function (function(){})
* or register an empty function with <OpenLayers.Ajax.Responders>
* for onException.
*
* Parameters:
* exception - {?}
*/
dispatchException: function(exception) {
var handler = this.options.onException;
if(handler) {
// call options.onException and alert any other listeners
handler(this, exception);
OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
} else {
// check if there are any other listeners
var listener = false;
var responders = OpenLayers.Ajax.Responders.responders;
for (var i = 0; i < responders.length; i++) {
if(responders[i].onException) {
listener = true;
break;
}
}
if(listener) {
// call all listeners
OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
} else {
// let the exception through
throw exception;
}
}
}
});
/**
* Property: Events
* {Array(String)}
*/
OpenLayers.Ajax.Request.Events =
['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
/**
* Class: OpenLayers.Ajax.Response
*/
OpenLayers.Ajax.Response = OpenLayers.Class({
/**
* Property: status
*
* {Integer}
*/
status: 0,
/**
* Property: statusText
*
* {String}
*/
statusText: '',
/**
* Constructor: OpenLayers.Ajax.Response
*
* Parameters:
* request - {Object}
*/
initialize: function(request) {
this.request = request;
var transport = this.transport = request.transport,
readyState = this.readyState = transport.readyState;
if ((readyState > 2 &&
!(!!(window.attachEvent && !window.opera))) ||
readyState == 4) {
this.status = this.getStatus();
this.statusText = this.getStatusText();
this.responseText = transport.responseText == null ?
'' : String(transport.responseText);
}
if(readyState == 4) {
var xml = transport.responseXML;
this.responseXML = xml === undefined ? null : xml;
}
},
/**
* Method: getStatus
*/
getStatus: OpenLayers.Ajax.Request.prototype.getStatus,
/**
* Method: getStatustext
*
* Returns:
* {String} - statusText
*/
getStatusText: function() {
try {
return this.transport.statusText || '';
} catch (e) {
return '';
}
},
/**
* Method: getHeader
*/
getHeader: OpenLayers.Ajax.Request.prototype.getHeader,
/**
* Method: getResponseHeader
*
* Returns:
* {?} - response header for given name
*/
getResponseHeader: function(name) {
return this.transport.getResponseHeader(name);
}
});
/**
* Function: getElementsByTagNameNS
*
* Parameters:
* parentnode - {?}
* nsuri - {?}
* nsprefix - {?}
* tagname - {?}
*
* Returns:
* {?}
*/
OpenLayers.Ajax.getElementsByTagNameNS = function(parentnode, nsuri,
nsprefix, tagname) {
var elem = null;
if (parentnode.getElementsByTagNameNS) {
elem = parentnode.getElementsByTagNameNS(nsuri, tagname);
} else {
elem = parentnode.getElementsByTagName(nsprefix + ':' + tagname);
}
return elem;
};
/**
* Function: serializeXMLToString
* Wrapper function around XMLSerializer, which doesn't exist/work in
* IE/Safari. We need to come up with a way to serialize in those browser:
* for now, these browsers will just fail. #535, #536
*
* Parameters:
* xmldom {XMLNode} xml dom to serialize
*
* Returns:
* {?}
*/
OpenLayers.Ajax.serializeXMLToString = function(xmldom) {
var serializer = new XMLSerializer();
var data = serializer.serializeToString(xmldom);
return data;
};
/**
* Namespace: OpenLayers.Element
*/
OpenLayers.Util.extend(OpenLayers.Element, {
/**
* APIFunction: hide
* *Deprecated*. Hide element(s) passed in
*
* Parameters:
* element - {DOMElement} Actually user can pass any number of elements
*/
hide: function() {
OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
newMethod: "element.style.display = 'none';"
}));
for (var i=0, len=arguments.length; i<len; i++) {
var element = OpenLayers.Util.getElement(arguments[i]);
if (element) {
element.style.display = 'none';
}
}
},
/**
* APIFunction: show
* *Deprecated*. Show element(s) passed in
*
* Parameters:
* element - {DOMElement} Actually user can pass any number of elements
*/
show: function() {
OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
newMethod: "element.style.display = '';"
}));
for (var i=0, len=arguments.length; i<len; i++) {
var element = OpenLayers.Util.getElement(arguments[i]);
if (element) {
element.style.display = '';
}
}
},
/**
* APIFunction: getDimensions
* *Deprecated*. Returns dimensions of the element passed in.
*
* Parameters:
* element - {DOMElement}
*
* Returns:
* {Object} Object with 'width' and 'height' properties which are the
* dimensions of the element passed in.
*/
getDimensions: function(element) {
element = OpenLayers.Util.getElement(element);
if (OpenLayers.Element.getStyle(element, 'display') != 'none') {
return {width: element.offsetWidth, height: element.offsetHeight};
}
// All *Width and *Height properties give 0 on elements with display none,
// so enable the element temporarily
var els = element.style;
var originalVisibility = els.visibility;
var originalPosition = els.position;
var originalDisplay = els.display;
els.visibility = 'hidden';
els.position = 'absolute';
els.display = '';
var originalWidth = element.clientWidth;
var originalHeight = element.clientHeight;
els.display = originalDisplay;
els.position = originalPosition;
els.visibility = originalVisibility;
return {width: originalWidth, height: originalHeight};
}
});
if (!String.prototype.startsWith) {
/**
* APIMethod: String.startsWith
* *Deprecated*. Whether or not a string starts with another string.
*
* Parameters:
* sStart - {String} The string we're testing for.
*
* Returns:
* {Boolean} Whether or not this string starts with the string passed in.
*/
String.prototype.startsWith = function(sStart) {
OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
{'newMethod':'OpenLayers.String.startsWith'}));
return OpenLayers.String.startsWith(this, sStart);
};
}
if (!String.prototype.contains) {
/**
* APIMethod: String.contains
* *Deprecated*. Whether or not a string contains another string.
*
* Parameters:
* str - {String} The string that we're testing for.
*
* Returns:
* {Boolean} Whether or not this string contains with the string passed in.
*/
String.prototype.contains = function(str) {
OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
{'newMethod':'OpenLayers.String.contains'}));
return OpenLayers.String.contains(this, str);
};
}
if (!String.prototype.trim) {
/**
* APIMethod: String.trim
* *Deprecated*. Removes leading and trailing whitespace characters from a string.
*
* Returns:
* {String} A trimmed version of the string - all leading and
* trailing spaces removed
*/
String.prototype.trim = function() {
OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
{'newMethod':'OpenLayers.String.trim'}));
return OpenLayers.String.trim(this);
};
}
if (!String.prototype.camelize) {
/**
* APIMethod: String.camelize
* *Deprecated*. Camel-case a hyphenated string.
* Ex. "chicken-head" becomes "chickenHead", and
* "-chicken-head" becomes "ChickenHead".
*
* Returns:
* {String} The string, camelized
*/
String.prototype.camelize = function() {
OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
{'newMethod':'OpenLayers.String.camelize'}));
return OpenLayers.String.camelize(this);
};
}
if (!Function.prototype.bind) {
/**
* APIMethod: Function.bind
* *Deprecated*. Bind a function to an object.
* Method to easily create closures with 'this' altered.
*
* Parameters:
* object - {Object} the this parameter
*
* Returns:
* {Function} A closure with 'this' altered to the first
* argument.
*/
Function.prototype.bind = function() {
OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
{'newMethod':'OpenLayers.Function.bind'}));
// new function takes the same arguments with this function up front
Array.prototype.unshift.apply(arguments, [this]);
return OpenLayers.Function.bind.apply(null, arguments);
};
}
if (!Function.prototype.bindAsEventListener) {
/**
* APIMethod: Function.bindAsEventListener
* *Deprecated*. Bind a function to an object, and configure it to receive the
* event object as first parameter when called.
*
* Parameters:
* object - {Object} A reference to this.
*
* Returns:
* {Function}
*/
Function.prototype.bindAsEventListener = function(object) {
OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
{'newMethod':'OpenLayers.Function.bindAsEventListener'}));
return OpenLayers.Function.bindAsEventListener(this, object);
};
}
// FIXME: Remove this in 3.0. In 3.0, Event.stop will no longer be provided
// by OpenLayers.
if (window.Event) {
OpenLayers.Util.applyDefaults(window.Event, OpenLayers.Event);
} else {
var Event = OpenLayers.Event;
}
/**
* Namespace: OpenLayers.Tile
*/
OpenLayers.Util.extend(OpenLayers.Tile.prototype, {
/**
* Method: getBoundsFromBaseLayer
* Take the pixel locations of the corner of the tile, and pass them to
* the base layer and ask for the location of those pixels, so that
* displaying tiles over Google works fine.
*
* Parameters:
* position - {<OpenLayers.Pixel>}
*
* Returns:
* bounds - {<OpenLayers.Bounds>}
*/
getBoundsFromBaseLayer: function(position) {
var msg = OpenLayers.i18n('reprojectDeprecated',
{'layerName':this.layer.name});
OpenLayers.Console.warn(msg);
var topLeft = this.layer.map.getLonLatFromLayerPx(position);
var bottomRightPx = position.clone();
bottomRightPx.x += this.size.w;
bottomRightPx.y += this.size.h;
var bottomRight = this.layer.map.getLonLatFromLayerPx(bottomRightPx);
// Handle the case where the base layer wraps around the date line.
// Google does this, and it breaks WMS servers to request bounds in
// that fashion.
if (topLeft.lon > bottomRight.lon) {
if (topLeft.lon < 0) {
topLeft.lon = -180 - (topLeft.lon+180);
} else {
bottomRight.lon = 180+bottomRight.lon+180;
}
}
var bounds = new OpenLayers.Bounds(topLeft.lon,
bottomRight.lat,
bottomRight.lon,
topLeft.lat);
return bounds;
}
});
/**
* Class: OpenLayers.Control.MouseDefaults
* This class is DEPRECATED in 2.4 and will be removed by 3.0.
* If you need this functionality, use <OpenLayers.Control.Navigation>
* instead!!!
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.MouseDefaults = OpenLayers.Class(OpenLayers.Control, {
/** WARNING WARNING WARNING!!!
This class is DEPRECATED in 2.4 and will be removed by 3.0.
If you need this functionality, use Control.Navigation instead!!! */
/**
* Property: performedDrag
* {Boolean}
*/
performedDrag: false,
/**
* Property: wheelObserver
* {Function}
*/
wheelObserver: null,
/**
* Constructor: OpenLayers.Control.MouseDefaults
*/
initialize: function() {
OpenLayers.Control.prototype.initialize.apply(this, arguments);
},
/**
* APIMethod: destroy
*/
destroy: function() {
if (this.handler) {
this.handler.destroy();
}
this.handler = null;
this.map.events.un({
"click": this.defaultClick,
"dblclick": this.defaultDblClick,
"mousedown": this.defaultMouseDown,
"mouseup": this.defaultMouseUp,
"mousemove": this.defaultMouseMove,
"mouseout": this.defaultMouseOut,
scope: this
});
//unregister mousewheel events specifically on the window and document
OpenLayers.Event.stopObserving(window, "DOMMouseScroll",
this.wheelObserver);
OpenLayers.Event.stopObserving(window, "mousewheel",
this.wheelObserver);
OpenLayers.Event.stopObserving(document, "mousewheel",
this.wheelObserver);
this.wheelObserver = null;
OpenLayers.Control.prototype.destroy.apply(this, arguments);
},
/**
* Method: draw
*/
draw: function() {
this.map.events.on({
"click": this.defaultClick,
"dblclick": this.defaultDblClick,
"mousedown": this.defaultMouseDown,
"mouseup": this.defaultMouseUp,
"mousemove": this.defaultMouseMove,
"mouseout": this.defaultMouseOut,
scope: this
});
this.registerWheelEvents();
},
/**
* Method: registerWheelEvents
*/
registerWheelEvents: function() {
this.wheelObserver = OpenLayers.Function.bindAsEventListener(
this.onWheelEvent, this
);
//register mousewheel events specifically on the window and document
OpenLayers.Event.observe(window, "DOMMouseScroll", this.wheelObserver);
OpenLayers.Event.observe(window, "mousewheel", this.wheelObserver);
OpenLayers.Event.observe(document, "mousewheel", this.wheelObserver);
},
/**
* Method: defaultClick
*
* Parameters:
* evt - {Event}
*
* Returns:
* {Boolean}
*/
defaultClick: function (evt) {
if (!OpenLayers.Event.isLeftClick(evt)) {
return;
}
var notAfterDrag = !this.performedDrag;
this.performedDrag = false;
return notAfterDrag;
},
/**
* Method: defaultDblClick
*
* Parameters:
* evt - {Event}
*/
defaultDblClick: function (evt) {
var newCenter = this.map.getLonLatFromViewPortPx( evt.xy );
this.map.setCenter(newCenter, this.map.zoom + 1);
OpenLayers.Event.stop(evt);
return false;
},
/**
* Method: defaultMouseDown
*
* Parameters:
* evt - {Event}
*/
defaultMouseDown: function (evt) {
if (!OpenLayers.Event.isLeftClick(evt)) {
return;
}
this.mouseDragStart = evt.xy.clone();
this.performedDrag = false;
if (evt.shiftKey) {
this.map.div.style.cursor = "crosshair";
this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
this.mouseDragStart,
null,
null,
"absolute",
"2px solid red");
this.zoomBox.style.backgroundColor = "white";
this.zoomBox.style.filter = "alpha(opacity=50)"; // IE
this.zoomBox.style.opacity = "0.50";
this.zoomBox.style.fontSize = "1px";
this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
this.map.viewPortDiv.appendChild(this.zoomBox);
}
document.onselectstart = OpenLayers.Function.False;
OpenLayers.Event.stop(evt);
},
/**
* Method: defaultMouseMove
*
* Parameters:
* evt - {Event}
*/
defaultMouseMove: function (evt) {
// record the mouse position, used in onWheelEvent
this.mousePosition = evt.xy.clone();
if (this.mouseDragStart != null) {
if (this.zoomBox) {
var deltaX = Math.abs(this.mouseDragStart.x - evt.xy.x);
var deltaY = Math.abs(this.mouseDragStart.y - evt.xy.y);
this.zoomBox.style.width = Math.max(1, deltaX) + "px";
this.zoomBox.style.height = Math.max(1, deltaY) + "px";
if (evt.xy.x < this.mouseDragStart.x) {
this.zoomBox.style.left = evt.xy.x+"px";
}
if (evt.xy.y < this.mouseDragStart.y) {
this.zoomBox.style.top = evt.xy.y+"px";
}
} else {
var deltaX = this.mouseDragStart.x - evt.xy.x;
var deltaY = this.mouseDragStart.y - evt.xy.y;
var size = this.map.getSize();
var newXY = new OpenLayers.Pixel(size.w / 2 + deltaX,
size.h / 2 + deltaY);
var newCenter = this.map.getLonLatFromViewPortPx( newXY );
this.map.setCenter(newCenter, null, true);
this.mouseDragStart = evt.xy.clone();
this.map.div.style.cursor = "move";
}
this.performedDrag = true;
}
},
/**
* Method: defaultMouseUp
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
defaultMouseUp: function (evt) {
if (!OpenLayers.Event.isLeftClick(evt)) {
return;
}
if (this.zoomBox) {
this.zoomBoxEnd(evt);
} else {
if (this.performedDrag) {
this.map.setCenter(this.map.center);
}
}
document.onselectstart=null;
this.mouseDragStart = null;
this.map.div.style.cursor = "";
},
/**
* Method: defaultMouseOut
*
* Parameters:
* evt - {Event}
*/
defaultMouseOut: function (evt) {
if (this.mouseDragStart != null &&
OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {
if (this.zoomBox) {
this.removeZoomBox();
}
this.mouseDragStart = null;
}
},
/**
* Method: defaultWheelUp
* User spun scroll wheel up
*
*/
defaultWheelUp: function(evt) {
if (this.map.getZoom() <= this.map.getNumZoomLevels()) {
this.map.setCenter(this.map.getLonLatFromPixel(evt.xy),
this.map.getZoom() + 1);
}
},
/**
* Method: defaultWheelDown
* User spun scroll wheel down
*/
defaultWheelDown: function(evt) {
if (this.map.getZoom() > 0) {
this.map.setCenter(this.map.getLonLatFromPixel(evt.xy),
this.map.getZoom() - 1);
}
},
/**
* Method: zoomBoxEnd
* Zoombox function.
*/
zoomBoxEnd: function(evt) {
if (this.mouseDragStart != null) {
if (Math.abs(this.mouseDragStart.x - evt.xy.x) > 5 ||
Math.abs(this.mouseDragStart.y - evt.xy.y) > 5) {
var start = this.map.getLonLatFromViewPortPx( this.mouseDragStart );
var end = this.map.getLonLatFromViewPortPx( evt.xy );
var top = Math.max(start.lat, end.lat);
var bottom = Math.min(start.lat, end.lat);
var left = Math.min(start.lon, end.lon);
var right = Math.max(start.lon, end.lon);
var bounds = new OpenLayers.Bounds(left, bottom, right, top);
this.map.zoomToExtent(bounds);
} else {
var end = this.map.getLonLatFromViewPortPx( evt.xy );
this.map.setCenter(new OpenLayers.LonLat(
(end.lon),
(end.lat)
), this.map.getZoom() + 1);
}
this.removeZoomBox();
}
},
/**
* Method: removeZoomBox
* Remove the zoombox from the screen and nullify our reference to it.
*/
removeZoomBox: function() {
this.map.viewPortDiv.removeChild(this.zoomBox);
this.zoomBox = null;
},
/**
* Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
*/
/**
* Method: onWheelEvent
* Catch the wheel event and handle it xbrowserly
*
* Parameters:
* e - {Event}
*/
onWheelEvent: function(e){
// first determine whether or not the wheeling was inside the map
var inMap = false;
var elem = OpenLayers.Event.element(e);
while(elem != null) {
if (this.map && elem == this.map.div) {
inMap = true;
break;
}
elem = elem.parentNode;
}
if (inMap) {
var delta = 0;
if (!e) {
e = window.event;
}
if (e.wheelDelta) {
delta = e.wheelDelta/120;
if (window.opera && window.opera.version() < 9.2) {
delta = -delta;
}
} else if (e.detail) {
delta = -e.detail / 3;
}
if (delta) {
// add the mouse position to the event because mozilla has a bug
// with clientX and clientY (see https://bugzilla.mozilla.org/show_bug.cgi?id=352179)
// getLonLatFromViewPortPx(e) returns wrong values
e.xy = this.mousePosition;
if (delta < 0) {
this.defaultWheelDown(e);
} else {
this.defaultWheelUp(e);
}
}
//only wheel the map, not the window
OpenLayers.Event.stop(e);
}
},
CLASS_NAME: "OpenLayers.Control.MouseDefaults"
});
/**
* Class: OpenLayers.Control.MouseToolbar
* This class is DEPRECATED in 2.4 and will be removed by 3.0.
* If you need this functionality, use <OpenLayers.Control.NavToolbar>
* instead!!!
*/
OpenLayers.Control.MouseToolbar = OpenLayers.Class(
OpenLayers.Control.MouseDefaults, {
/**
* Property: mode
*/
mode: null,
/**
* Property: buttons
*/
buttons: null,
/**
* APIProperty: direction
* {String} 'vertical' or 'horizontal'
*/
direction: "vertical",
/**
* Property: buttonClicked
* {String}
*/
buttonClicked: null,
/**
* Constructor: OpenLayers.Control.MouseToolbar
*
* Parameters:
* position - {<OpenLayers.Pixel>}
* direction - {String}
*/
initialize: function(position, direction) {
OpenLayers.Control.prototype.initialize.apply(this, arguments);
this.position = new OpenLayers.Pixel(OpenLayers.Control.MouseToolbar.X,
OpenLayers.Control.MouseToolbar.Y);
if (position) {
this.position = position;
}
if (direction) {
this.direction = direction;
}
this.measureDivs = [];
},
/**
* APIMethod: destroy
*/
destroy: function() {
for( var btnId in this.buttons) {
var btn = this.buttons[btnId];
btn.map = null;
btn.events.destroy();
}
OpenLayers.Control.MouseDefaults.prototype.destroy.apply(this,
arguments);
},
/**
* Method: draw
*/
draw: function() {
OpenLayers.Control.prototype.draw.apply(this, arguments);
OpenLayers.Control.MouseDefaults.prototype.draw.apply(this, arguments);
this.buttons = {};
var sz = new OpenLayers.Size(28,28);
var centered = new OpenLayers.Pixel(OpenLayers.Control.MouseToolbar.X,0);
this._addButton("zoombox", "drag-rectangle-off.png", "drag-rectangle-on.png", centered, sz, "Shift->Drag to zoom to area");
centered = centered.add((this.direction == "vertical" ? 0 : sz.w), (this.direction == "vertical" ? sz.h : 0));
this._addButton("pan", "panning-hand-off.png", "panning-hand-on.png", centered, sz, "Drag the map to pan.");
centered = centered.add((this.direction == "vertical" ? 0 : sz.w), (this.direction == "vertical" ? sz.h : 0));
this.switchModeTo("pan");
return this.div;
},
/**
* Method: _addButton
*/
_addButton:function(id, img, activeImg, xy, sz, title) {
var imgLocation = OpenLayers.Util.getImageLocation(img);
var activeImgLocation = OpenLayers.Util.getImageLocation(activeImg);
// var btn = new ol.AlphaImage("_"+id, imgLocation, xy, sz);
var btn = OpenLayers.Util.createAlphaImageDiv(
"OpenLayers_Control_MouseToolbar_" + id,
xy, sz, imgLocation, "absolute");
//we want to add the outer div
this.div.appendChild(btn);
btn.imgLocation = imgLocation;
btn.activeImgLocation = activeImgLocation;
btn.events = new OpenLayers.Events(this, btn, null, true);
btn.events.on({
"mousedown": this.buttonDown,
"mouseup": this.buttonUp,
"dblclick": OpenLayers.Event.stop,
scope: this
});
btn.action = id;
btn.title = title;
btn.alt = title;
btn.map = this.map;
//we want to remember/reference the outer div
this.buttons[id] = btn;
return btn;
},
/**
* Method: buttonDown
*
* Parameters:
* evt - {Event}
*/
buttonDown: function(evt) {
if (!OpenLayers.Event.isLeftClick(evt)) {
return;
}
this.buttonClicked = evt.element.action;
OpenLayers.Event.stop(evt);
},
/**
* Method: buttonUp
*
* Parameters:
* evt - {Event}
*/
buttonUp: function(evt) {
if (!OpenLayers.Event.isLeftClick(evt)) {
return;
}
if (this.buttonClicked != null) {
if (this.buttonClicked == evt.element.action) {
this.switchModeTo(evt.element.action);
}
OpenLayers.Event.stop(evt);
this.buttonClicked = null;
}
},
/**
* Method: defaultDblClick
*
* Parameters:
* evt - {Event}
*/
defaultDblClick: function (evt) {
this.switchModeTo("pan");
this.performedDrag = false;
var newCenter = this.map.getLonLatFromViewPortPx( evt.xy );
this.map.setCenter(newCenter, this.map.zoom + 1);
OpenLayers.Event.stop(evt);
return false;
},
/**
* Method: defaultMouseDown
*
* Parameters:
* evt - {Event}
*/
defaultMouseDown: function (evt) {
if (!OpenLayers.Event.isLeftClick(evt)) {
return;
}
this.mouseDragStart = evt.xy.clone();
this.performedDrag = false;
this.startViaKeyboard = false;
if (evt.shiftKey && this.mode !="zoombox") {
this.switchModeTo("zoombox");
this.startViaKeyboard = true;
} else if (evt.altKey && this.mode !="measure") {
this.switchModeTo("measure");
} else if (!this.mode) {
this.switchModeTo("pan");
}
switch (this.mode) {
case "zoombox":
this.map.div.style.cursor = "crosshair";
this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
this.mouseDragStart,
null,
null,
"absolute",
"2px solid red");
this.zoomBox.style.backgroundColor = "white";
this.zoomBox.style.filter = "alpha(opacity=50)"; // IE
this.zoomBox.style.opacity = "0.50";
this.zoomBox.style.fontSize = "1px";
this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
this.map.viewPortDiv.appendChild(this.zoomBox);
this.performedDrag = true;
break;
case "measure":
var distance = "";
if (this.measureStart) {
var measureEnd = this.map.getLonLatFromViewPortPx(this.mouseDragStart);
distance = OpenLayers.Util.distVincenty(this.measureStart, measureEnd);
distance = Math.round(distance * 100) / 100;
distance = distance + "km";
this.measureStartBox = this.measureBox;
}
this.measureStart = this.map.getLonLatFromViewPortPx(this.mouseDragStart);;
this.measureBox = OpenLayers.Util.createDiv(null,
this.mouseDragStart.add(
-2-parseInt(this.map.layerContainerDiv.style.left),
-2-parseInt(this.map.layerContainerDiv.style.top)),
null,
null,
"absolute");
this.measureBox.style.width="4px";
this.measureBox.style.height="4px";
this.measureBox.style.fontSize = "1px";
this.measureBox.style.backgroundColor="red";
this.measureBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
this.map.layerContainerDiv.appendChild(this.measureBox);
if (distance) {
this.measureBoxDistance = OpenLayers.Util.createDiv(null,
this.mouseDragStart.add(
-2-parseInt(this.map.layerContainerDiv.style.left),
2-parseInt(this.map.layerContainerDiv.style.top)),
null,
null,
"absolute");
this.measureBoxDistance.innerHTML = distance;
this.measureBoxDistance.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
this.map.layerContainerDiv.appendChild(this.measureBoxDistance);
this.measureDivs.push(this.measureBoxDistance);
}
this.measureBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
this.map.layerContainerDiv.appendChild(this.measureBox);
this.measureDivs.push(this.measureBox);
break;
default:
this.map.div.style.cursor = "move";
break;
}
document.onselectstart = OpenLayers.Function.False;
OpenLayers.Event.stop(evt);
},
/**
* Method: switchModeTo
*
* Parameters:
* mode - {String}
*/
switchModeTo: function(mode) {
if (mode != this.mode) {
if (this.mode && this.buttons[this.mode]) {
OpenLayers.Util.modifyAlphaImageDiv(this.buttons[this.mode], null, null, null, this.buttons[this.mode].imgLocation);
}
if (this.mode == "measure" && mode != "measure") {
for(var i=0, len=this.measureDivs.length; i<len; i++) {
if (this.measureDivs[i]) {
this.map.layerContainerDiv.removeChild(this.measureDivs[i]);
}
}
this.measureDivs = [];
this.measureStart = null;
}
this.mode = mode;
if (this.buttons[mode]) {
OpenLayers.Util.modifyAlphaImageDiv(this.buttons[mode], null, null, null, this.buttons[mode].activeImgLocation);
}
switch (this.mode) {
case "zoombox":
this.map.div.style.cursor = "crosshair";
break;
default:
this.map.div.style.cursor = "";
break;
}
}
},
/**
* Method: leaveMode
*/
leaveMode: function() {
this.switchModeTo("pan");
},
/**
* Method: defaultMouseMove
*
* Parameters:
* evt - {Event}
*/
defaultMouseMove: function (evt) {
if (this.mouseDragStart != null) {
switch (this.mode) {
case "zoombox":
var deltaX = Math.abs(this.mouseDragStart.x - evt.xy.x);
var deltaY = Math.abs(this.mouseDragStart.y - evt.xy.y);
this.zoomBox.style.width = Math.max(1, deltaX) + "px";
this.zoomBox.style.height = Math.max(1, deltaY) + "px";
if (evt.xy.x < this.mouseDragStart.x) {
this.zoomBox.style.left = evt.xy.x+"px";
}
if (evt.xy.y < this.mouseDragStart.y) {
this.zoomBox.style.top = evt.xy.y+"px";
}
break;
default:
var deltaX = this.mouseDragStart.x - evt.xy.x;
var deltaY = this.mouseDragStart.y - evt.xy.y;
var size = this.map.getSize();
var newXY = new OpenLayers.Pixel(size.w / 2 + deltaX,
size.h / 2 + deltaY);
var newCenter = this.map.getLonLatFromViewPortPx( newXY );
this.map.setCenter(newCenter, null, true);
this.mouseDragStart = evt.xy.clone();
}
this.performedDrag = true;
}
},
/**
* Method: defaultMouseUp
*
* Parameters:
* evt - {Event}
*/
defaultMouseUp: function (evt) {
if (!OpenLayers.Event.isLeftClick(evt)) {
return;
}
switch (this.mode) {
case "zoombox":
this.zoomBoxEnd(evt);
if (this.startViaKeyboard) {
this.leaveMode();
}
break;
case "pan":
if (this.performedDrag) {
this.map.setCenter(this.map.center);
}
}
document.onselectstart = null;
this.mouseDragStart = null;
this.map.div.style.cursor = "default";
},
/**
* Method: defaultMouseOut
*
* Parameters:
* evt - {Event}
*/
defaultMouseOut: function (evt) {
if (this.mouseDragStart != null
&& OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {
if (this.zoomBox) {
this.removeZoomBox();
if (this.startViaKeyboard) {
this.leaveMode();
}
}
this.mouseDragStart = null;
this.map.div.style.cursor = "default";
}
},
/**
* Method: defaultClick
*
* Parameters:
* evt - {Event}
*/
defaultClick: function (evt) {
if (this.performedDrag) {
this.performedDrag = false;
return false;
}
},
CLASS_NAME: "OpenLayers.Control.MouseToolbar"
});
OpenLayers.Control.MouseToolbar.X = 6;
OpenLayers.Control.MouseToolbar.Y = 300;
/**
* Class: OpenLayers.Layer.Grid
*/
OpenLayers.Util.extend(OpenLayers.Layer.Grid.prototype, {
/**
* Method: getGridBounds
* Deprecated. This function will be removed in 3.0. Please use
* getTilesBounds() instead.
*
* Returns:
* {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the
* currently loaded tiles (including those partially or not at all seen
* onscreen)
*/
getGridBounds: function() {
var msg = "The getGridBounds() function is deprecated. It will be " +
"removed in 3.0. Please use getTilesBounds() instead.";
OpenLayers.Console.warn(msg);
return this.getTilesBounds();
}
});
/**
* Class: OpenLayers.Format.XML
*/
OpenLayers.Util.extend(OpenLayers.Format.XML.prototype, {
/**
* APIMethod: concatChildValues
* *Deprecated*. Use <getChildValue> instead.
*
* Concatenate the value of all child nodes if any exist, or return an
* optional default string. Returns an empty string if no children
* exist and no default value is supplied. Not optimized for large
* numbers of child nodes.
*
* Parameters:
* node - {DOMElement} The element used to look for child values.
* def - {String} Optional string to return in the event that no
* child exist.
*
* Returns:
* {String} The concatenated value of all child nodes of the given node.
*/
concatChildValues: function(node, def) {
var value = "";
var child = node.firstChild;
var childValue;
while(child) {
childValue = child.nodeValue;
if(childValue) {
value += childValue;
}
child = child.nextSibling;
}
if(value == "" && def != undefined) {
value = def;
}
return value;
}
});
/**
* Class: OpenLayers.Layer.WMS.Post
* Instances of OpenLayers.Layer.WMS.Post are used to retrieve data from OGC
* Web Mapping Services via HTTP-POST (application/x-www-form-urlencoded).
* Create a new WMS layer with the <OpenLayers.Layer.WMS.Post> constructor.
*
* *Deprecated*. Instead of this layer, use <OpenLayers.Layer.WMS> with
* <OpenLayers.Tile.Image.maxGetUrlLength> configured in the layer's
* <OpenLayers.Layer.WMS.tileOptions>.
*
* Inherits from:
* - <OpenLayers.Layer.WMS>
*/
OpenLayers.Layer.WMS.Post = OpenLayers.Class(OpenLayers.Layer.WMS, {
/**
* APIProperty: unsupportedBrowsers
* {Array} Array with browsers, which should use the HTTP-GET protocol
* instead of HTTP-POST for fetching tiles from a WMS .
* Defaults to ["mozilla", "firefox", "opera"], because Opera is not able
* to show transparent images in IFrames and Firefox/Mozilla has some ugly
* effects of viewport-shaking when panning the map. Both browsers, Opera
* and Firefox/Mozilla, have no problem with long urls, which is the reason
* for using POST instead of GET. The strings to pass to this array are
* the ones returned by <OpenLayers.BROWSER_NAME>.
*/
unsupportedBrowsers: ["mozilla", "firefox", "opera"],
/**
* Property: SUPPORTED_TRANSITIONS
* {Array}
* no supported transitions for this type of layer, because it is not
* possible to modify the initialized tiles (iframes)
*/
SUPPORTED_TRANSITIONS: [],
/**
* Property: usePost
* {Boolean}
*/
usePost: null,
/**
* Constructor: OpenLayers.Layer.WMS.Post
* Creates a new WMS layer object.
*
* Example:
* (code)
* var wms = new OpenLayers.Layer.WMS.Post(
* "NASA Global Mosaic",
* "http://wms.jpl.nasa.gov/wms.cgi",
* {layers: "modis, global_mosaic"});
* (end)
*
* Parameters:
* name - {String} A name for the layer
* url - {String} Base url for the WMS
* (e.g. http://wms.jpl.nasa.gov/wms.cgi)
* params - {Object} An object with key/value pairs representing the
* GetMap query string parameters and parameter values.
* options - {Object} Hashtable of extra options to tag onto the layer.
*/
initialize: function(name, url, params, options) {
var newArguments = [];
newArguments.push(name, url, params, options);
OpenLayers.Layer.WMS.prototype.initialize.apply(this, newArguments);
this.usePost = OpenLayers.Util.indexOf(
this.unsupportedBrowsers, OpenLayers.BROWSER_NAME) == -1;
},
/**
* Method: addTile
* addTile creates a tile, initializes it and adds it as iframe to the
* layer div.
*
* Parameters:
* bounds - {<OpenLayers.Bounds>}
* position - {<OpenLayers.Pixel>}
*
* Returns:
* {<OpenLayers.Tile.Image.IFrame>} The added OpenLayers.Tile.Image.IFrame
*/
addTile: function(bounds,position) {
return new OpenLayers.Tile.Image(
this, position, bounds, null, this.tileSize, {
maxGetUrlLength: this.usePost ? 0 : null
});
},
CLASS_NAME: 'OpenLayers.Layer.WMS.Post'
});
/**
* Class: OpenLayers.Layer.WMS.Untiled
* *Deprecated*. To be removed in 3.0. Instead use OpenLayers.Layer.WMS and
* pass the option 'singleTile' as true.
*
* Inherits from:
* - <OpenLayers.Layer.WMS>
*/
OpenLayers.Layer.WMS.Untiled = OpenLayers.Class(OpenLayers.Layer.WMS, {
/**
* APIProperty: singleTile
* {singleTile} Always true for untiled.
*/
singleTile: true,
/**
* Constructor: OpenLayers.Layer.WMS.Untiled
*
* Parameters:
* name - {String}
* url - {String}
* params - {Object}
* options - {Object}
*/
initialize: function(name, url, params, options) {
OpenLayers.Layer.WMS.prototype.initialize.apply(this, arguments);
var msg = "The OpenLayers.Layer.WMS.Untiled class is deprecated and " +
"will be removed in 3.0. Instead, you should use the " +
"normal OpenLayers.Layer.WMS class, passing it the option " +
"'singleTile' as true.";
OpenLayers.Console.warn(msg);
},
/**
* Method: clone
* Create a clone of this layer
*
* Returns:
* {<OpenLayers.Layer.WMS.Untiled>} An exact clone of this layer
*/
clone: function (obj) {
if (obj == null) {
obj = new OpenLayers.Layer.WMS.Untiled(this.name,
this.url,
this.params,
this.getOptions());
}
//get all additions from superclasses
obj = OpenLayers.Layer.WMS.prototype.clone.apply(this, [obj]);
// copy/set any non-init, non-simple values here
return obj;
},
CLASS_NAME: "OpenLayers.Layer.WMS.Untiled"
});
/**
* Class: OpenLayers.Layer.MapServer.Untiled
* *Deprecated*. To be removed in 3.0. Instead use OpenLayers.Layer.MapServer
* and pass the option 'singleTile' as true.
*
* Inherits from:
* - <OpenLayers.Layer.MapServer>
*/
OpenLayers.Layer.MapServer.Untiled = OpenLayers.Class(OpenLayers.Layer.MapServer, {
/**
* APIProperty: singleTile
* {singleTile} Always true for untiled.
*/
singleTile: true,
/**
* Constructor: OpenLayers.Layer.MapServer.Untiled
*
* Parameters:
* name - {String}
* url - {String}
* params - {Object}
* options - {Object}
*/
initialize: function(name, url, params, options) {
OpenLayers.Layer.MapServer.prototype.initialize.apply(this, arguments);
var msg = "The OpenLayers.Layer.MapServer.Untiled class is deprecated and " +
"will be removed in 3.0. Instead, you should use the " +
"normal OpenLayers.Layer.MapServer class, passing it the option " +
"'singleTile' as true.";
OpenLayers.Console.warn(msg);
},
/**
* Method: clone
* Create a clone of this layer
*
* Returns:
* {<OpenLayers.Layer.MapServer.Untiled>} An exact clone of this layer
*/
clone: function (obj) {
if (obj == null) {
obj = new OpenLayers.Layer.MapServer.Untiled(this.name,
this.url,
this.params,
this.getOptions());
}
//get all additions from superclasses
obj = OpenLayers.Layer.MapServer.prototype.clone.apply(this, [obj]);
// copy/set any non-init, non-simple values here
return obj;
},
CLASS_NAME: "OpenLayers.Layer.MapServer.Untiled"
});
/**
* Class: OpenLayers.Tile.WFS
* Instances of OpenLayers.Tile.WFS are used to manage the image tiles
* used by various layers. Create a new image tile with the
* <OpenLayers.Tile.WFS> constructor.
*
* Inherits from:
* - <OpenLayers.Tile>
*/
OpenLayers.Tile.WFS = OpenLayers.Class(OpenLayers.Tile, {
/**
* Property: features
* {Array(<OpenLayers.Feature>)} list of features in this tile
*/
features: null,
/**
* Property: url
* {String}
*/
url: null,
/**
* Property: request
* {<OpenLayers.Request.XMLHttpRequest>}
*/
request: null,
/** TBD 3.0 - reorder the parameters to the init function to put URL
* as last, so we can continue to call tile.initialize()
* without changing the arguments.
*
* Constructor: OpenLayers.Tile.WFS
* Constructor for a new <OpenLayers.Tile.WFS> instance.
*
* Parameters:
* layer - {<OpenLayers.Layer>} layer that the tile will go in.
* position - {<OpenLayers.Pixel>}
* bounds - {<OpenLayers.Bounds>}
* url - {<String>}
* size - {<OpenLayers.Size>}
*/
initialize: function(layer, position, bounds, url, size) {
OpenLayers.Tile.prototype.initialize.apply(this, arguments);
this.url = url;
this.features = [];
},
/**
* APIMethod: destroy
* nullify references to prevent circular references and memory leaks
*/
destroy: function() {
OpenLayers.Tile.prototype.destroy.apply(this, arguments);
this.destroyAllFeatures();
this.features = null;
this.url = null;
if(this.request) {
this.request.abort();
//this.request.destroy();
this.request = null;
}
},
/**
* Method: clear
* Clear the tile of any bounds/position-related data so that it can
* be reused in a new location.
*/
clear: function() {
this.destroyAllFeatures();
},
/**
* Method: draw
* Check that a tile should be drawn, and load features for it.
*/
draw:function() {
if (OpenLayers.Tile.prototype.draw.apply(this, arguments)) {
if (this.isLoading) {
//if already loading, send 'reload' instead of 'loadstart'.
this.events.triggerEvent("reload");
} else {
this.isLoading = true;
this.events.triggerEvent("loadstart");
}
this.loadFeaturesForRegion(this.requestSuccess);
}
},
/**
* Method: loadFeaturesForRegion
* Abort any pending requests and issue another request for data.
*
* Input are function pointers for what to do on success and failure.
*
* Parameters:
* success - {function}
* failure - {function}
*/
loadFeaturesForRegion:function(success, failure) {
if(this.request) {
this.request.abort();
}
this.request = OpenLayers.Request.GET({
url: this.url,
success: success,
failure: failure,
scope: this
});
},
/**
* Method: requestSuccess
* Called on return from request succcess. Adds results via
* layer.addFeatures in vector mode, addResults otherwise.
*
* Parameters:
* request - {<OpenLayers.Request.XMLHttpRequest>}
*/
requestSuccess:function(request) {
if (this.features) {
var doc = request.responseXML;
if (!doc || !doc.documentElement) {
doc = request.responseText;
}
if (this.layer.vectorMode) {
this.layer.addFeatures(this.layer.formatObject.read(doc));
} else {
var xml = new OpenLayers.Format.XML();
if (typeof doc == "string") {
doc = xml.read(doc);
}
var resultFeatures = xml.getElementsByTagNameNS(
doc, "http://www.opengis.net/gml", "featureMember"
);
this.addResults(resultFeatures);
}
}
if (this.events) {
this.events.triggerEvent("loadend");
}
//request produced with success, we can delete the request object.
//this.request.destroy();
this.request = null;
},
/**
* Method: addResults
* Construct new feature via layer featureClass constructor, and add to
* this.features.
*
* Parameters:
* results - {Object}
*/
addResults: function(results) {
for (var i=0; i < results.length; i++) {
var feature = new this.layer.featureClass(this.layer,
results[i]);
this.features.push(feature);
}
},
/**
* Method: destroyAllFeatures
* Iterate through and call destroy() on each feature, removing it from
* the local array
*/
destroyAllFeatures: function() {
while(this.features.length > 0) {
var feature = this.features.shift();
feature.destroy();
}
},
CLASS_NAME: "OpenLayers.Tile.WFS"
}
);
/**
* Class: OpenLayers.Feature.WFS
* WFS handling class, for use as a featureClass on the WFS layer for handling
* 'point' WFS types. Good for subclassing when creating a custom WFS like
* XML application.
*
* Inherits from:
* - <OpenLayers.Feature>
*/
OpenLayers.Feature.WFS = OpenLayers.Class(OpenLayers.Feature, {
/**
* Constructor: OpenLayers.Feature.WFS
* Create a WFS feature.
*
* Parameters:
* layer - {<OpenLayers.Layer>}
* xmlNode - {XMLNode}
*/
initialize: function(layer, xmlNode) {
var newArguments = arguments;
var data = this.processXMLNode(xmlNode);
newArguments = new Array(layer, data.lonlat, data);
OpenLayers.Feature.prototype.initialize.apply(this, newArguments);
this.createMarker();
this.layer.addMarker(this.marker);
},
/**
* Method: destroy
* nullify references to prevent circular references and memory leaks
*/
destroy: function() {
if (this.marker != null) {
this.layer.removeMarker(this.marker);
}
OpenLayers.Feature.prototype.destroy.apply(this, arguments);
},
/**
* Method: processXMLNode
* When passed an xmlNode, parses it for a GML point, and passes
* back an object describing that point.
*
* For subclasses of Feature.WFS, this is the feature to change.
*
* Parameters:
* xmlNode - {XMLNode}
*
* Returns:
* {Object} Data Object with 'id', 'lonlat', and private properties set
*/
processXMLNode: function(xmlNode) {
//this should be overridden by subclasses
// must return an Object with 'id' and 'lonlat' values set
var point = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, "http://www.opengis.net/gml", "gml", "Point");
var text = OpenLayers.Util.getXmlNodeValue(OpenLayers.Ajax.getElementsByTagNameNS(point[0], "http://www.opengis.net/gml","gml", "coordinates")[0]);
var floats = text.split(",");
return {lonlat: new OpenLayers.LonLat(parseFloat(floats[0]),
parseFloat(floats[1])),
id: null};
},
CLASS_NAME: "OpenLayers.Feature.WFS"
});
/**
* Class: OpenLayers.Layer.WFS
* *Deprecated*. To be removed in 3.0. Instead use OpenLayers.Layer.Vector
* with a Protocol.WFS and one or more Strategies.
*
* Inherits from:
* - <OpenLayers.Layer.Vector>
* - <OpenLayers.Layer.Markers>
*/
OpenLayers.Layer.WFS = OpenLayers.Class(
OpenLayers.Layer.Vector, OpenLayers.Layer.Markers, {
/**
* APIProperty: isBaseLayer
* {Boolean} WFS layer is not a base layer by default.
*/
isBaseLayer: false,
/**
* Property: tile
* {<OpenLayers.Tile.WFS>}
*/
tile: null,
/**
* APIProperty: ratio
* {Float} The ratio property determines the size of the serverside query
* relative to the map viewport size. By default, we load an area twice
* as big as the map, to allow for panning without immediately reload.
* Setting this to 1 will cause the area of the WFS request to match
* the map area exactly. It is recommended to set this to some number
* at least slightly larger than 1, otherwise accidental clicks can
* cause a data reload, by moving the map only 1 pixel.
*/
ratio: 2,
/**
* Property: DEFAULT_PARAMS
* {Object} Hashtable of default key/value parameters
*/
DEFAULT_PARAMS: { service: "WFS",
version: "1.0.0",
request: "GetFeature"
},
/**
* APIProperty: featureClass
* {<OpenLayers.Feature>} If featureClass is defined, an old-style markers
* based WFS layer is created instead of a new-style vector layer. If
* sent, this should be a subclass of OpenLayers.Feature
*/
featureClass: null,
/**
* APIProperty: format
* {<OpenLayers.Format>} The format you want the data to be parsed with.
* Must be passed in the constructor. Should be a class, not an instance.
* This option can only be used if no featureClass is passed / vectorMode
* is false: if a featureClass is passed, then this parameter is ignored.
*/
format: null,
/**
* Property: formatObject
* {<OpenLayers.Format>} Internally created/managed format object, used by
* the Tile to parse data.
*/
formatObject: null,
/**
* APIProperty: formatOptions
* {Object} Hash of options which should be passed to the format when it is
* created. Must be passed in the constructor.
*/
formatOptions: null,
/**
* Property: vectorMode
* {Boolean} Should be calculated automatically. Determines whether the
* layer is in vector mode or marker mode.
*/
vectorMode: true,
/**
* APIProperty: encodeBBOX
* {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no',
* but some services want it that way. Default false.
*/
encodeBBOX: false,
/**
* APIProperty: extractAttributes
* {Boolean} Should the WFS layer parse attributes from the retrieved
* GML? Defaults to false. If enabled, parsing is slower, but
* attributes are available in the attributes property of
* layer features.
*/
extractAttributes: false,
/**
* Constructor: OpenLayers.Layer.WFS
*
* Parameters:
* name - {String}
* url - {String}
* params - {Object}
* options - {Object} Hashtable of extra options to tag onto the layer
*/
initialize: function(name, url, params, options) {
if (options == undefined) { options = {}; }
if (options.featureClass ||
!OpenLayers.Layer.Vector ||
!OpenLayers.Feature.Vector) {
this.vectorMode = false;
}
// Uppercase params
params = OpenLayers.Util.upperCaseObject(params);
// Turn off error reporting, browsers like Safari may work
// depending on the setup, and we don't want an unneccesary alert.
OpenLayers.Util.extend(options, {'reportError': false});
var newArguments = [];
newArguments.push(name, options);
OpenLayers.Layer.Vector.prototype.initialize.apply(this, newArguments);
if (!this.renderer || !this.vectorMode) {
this.vectorMode = false;
if (!options.featureClass) {
options.featureClass = OpenLayers.Feature.WFS;
}
OpenLayers.Layer.Markers.prototype.initialize.apply(this,
newArguments);
}
if (this.params && this.params.typename && !this.options.typename) {
this.options.typename = this.params.typename;
}
if (!this.options.geometry_column) {
this.options.geometry_column = "the_geom";
}
this.params = OpenLayers.Util.applyDefaults(
params,
OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)
);
this.url = url;
},
/**
* APIMethod: destroy
*/
destroy: function() {
if (this.vectorMode) {
OpenLayers.Layer.Vector.prototype.destroy.apply(this, arguments);
} else {
OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
}
if (this.tile) {
this.tile.destroy();
}
this.tile = null;
this.ratio = null;
this.featureClass = null;
this.format = null;
if (this.formatObject && this.formatObject.destroy) {
this.formatObject.destroy();
}
this.formatObject = null;
this.formatOptions = null;
this.vectorMode = null;
this.encodeBBOX = null;
this.extractAttributes = null;
},
/**
* Method: setMap
*
* Parameters:
* map - {<OpenLayers.Map>}
*/
setMap: function(map) {
if (this.vectorMode) {
OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
var options = {
'extractAttributes': this.extractAttributes
};
OpenLayers.Util.extend(options, this.formatOptions);
if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
options.externalProjection = this.projection;
options.internalProjection = this.map.getProjectionObject();
}
this.formatObject = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
} else {
OpenLayers.Layer.Markers.prototype.setMap.apply(this, arguments);
}
},
/**
* Method: moveTo
*
* Parameters:
* bounds - {<OpenLayers.Bounds>}
* zoomChanged - {Boolean}
* dragging - {Boolean}
*/
moveTo:function(bounds, zoomChanged, dragging) {
if (this.vectorMode) {
OpenLayers.Layer.Vector.prototype.moveTo.apply(this, arguments);
} else {
OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);
}
// don't load wfs features while dragging, wait for drag end
if (dragging) {
// TBD try to hide the vector layer while dragging
// this.setVisibility(false);
// this will probably help for panning performances
return false;
}
if ( zoomChanged ) {
if (this.vectorMode) {
this.renderer.clear();
}
}
//DEPRECATED - REMOVE IN 3.0
// don't load data if current zoom level doesn't match
if (this.options.minZoomLevel) {
OpenLayers.Console.warn(OpenLayers.i18n('minZoomLevelError'));
if (this.map.getZoom() < this.options.minZoomLevel) {
return null;
}
}
if (bounds == null) {
bounds = this.map.getExtent();
}
var firstRendering = (this.tile == null);
//does the new bounds to which we need to move fall outside of the
// current tile's bounds?
var outOfBounds = (!firstRendering &&
!this.tile.bounds.containsBounds(bounds));
if (zoomChanged || firstRendering || (!dragging && outOfBounds)) {
//determine new tile bounds
var center = bounds.getCenterLonLat();
var tileWidth = bounds.getWidth() * this.ratio;
var tileHeight = bounds.getHeight() * this.ratio;
var tileBounds =
new OpenLayers.Bounds(center.lon - (tileWidth / 2),
center.lat - (tileHeight / 2),
center.lon + (tileWidth / 2),
center.lat + (tileHeight / 2));
//determine new tile size
var tileSize = this.map.getSize();
tileSize.w = tileSize.w * this.ratio;
tileSize.h = tileSize.h * this.ratio;
//determine new position (upper left corner of new bounds)
var ul = new OpenLayers.LonLat(tileBounds.left, tileBounds.top);
var pos = this.map.getLayerPxFromLonLat(ul);
//formulate request url string
var url = this.getFullRequestString();
var params = null;
// Cant combine "filter" and "BBOX". This is a cheap hack to help
// people out who can't migrate to the WFS protocol immediately.
var filter = this.params.filter || this.params.FILTER;
if (filter) {
params = {FILTER: filter};
}
else {
params = {BBOX: this.encodeBBOX ? tileBounds.toBBOX()
: tileBounds.toArray()};
}
if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
var projectedBounds = tileBounds.clone();
projectedBounds.transform(this.map.getProjectionObject(),
this.projection);
if (!filter){
params.BBOX = this.encodeBBOX ? projectedBounds.toBBOX()
: projectedBounds.toArray();
}
}
url += "&" + OpenLayers.Util.getParameterString(params);
if (!this.tile) {
this.tile = new OpenLayers.Tile.WFS(this, pos, tileBounds,
url, tileSize);
this.addTileMonitoringHooks(this.tile);
this.tile.draw();
} else {
if (this.vectorMode) {
this.destroyFeatures();
this.renderer.clear();
} else {
this.clearMarkers();
}
this.removeTileMonitoringHooks(this.tile);
this.tile.destroy();
this.tile = null;
this.tile = new OpenLayers.Tile.WFS(this, pos, tileBounds,
url, tileSize);
this.addTileMonitoringHooks(this.tile);
this.tile.draw();
}
}
},
/**
* Method: addTileMonitoringHooks
* This function takes a tile as input and adds the appropriate hooks to
* the tile so that the layer can keep track of the loading tile
* (making sure to check that the tile is always the layer's current
* tile before taking any action).
*
* Parameters:
* tile - {<OpenLayers.Tile>}
*/
addTileMonitoringHooks: function(tile) {
tile.onLoadStart = function() {
//if this is the the layer's current tile, then trigger
// a 'loadstart'
if (this == this.layer.tile) {
this.layer.events.triggerEvent("loadstart");
}
};
tile.events.register("loadstart", tile, tile.onLoadStart);
tile.onLoadEnd = function() {
//if this is the the layer's current tile, then trigger
// a 'tileloaded' and 'loadend'
if (this == this.layer.tile) {
this.layer.events.triggerEvent("tileloaded");
this.layer.events.triggerEvent("loadend");
}
};
tile.events.register("loadend", tile, tile.onLoadEnd);
tile.events.register("unload", tile, tile.onLoadEnd);
},
/**
* Method: removeTileMonitoringHooks
* This function takes a tile as input and removes the tile hooks
* that were added in addTileMonitoringHooks()
*
* Parameters:
* tile - {<OpenLayers.Tile>}
*/
removeTileMonitoringHooks: function(tile) {
tile.unload();
tile.events.un({
"loadstart": tile.onLoadStart,
"loadend": tile.onLoadEnd,
"unload": tile.onLoadEnd,
scope: tile
});
},
/**
* Method: onMapResize
* Call the onMapResize method of the appropriate parent class.
*/
onMapResize: function() {
if(this.vectorMode) {
OpenLayers.Layer.Vector.prototype.onMapResize.apply(this,
arguments);
} else {
OpenLayers.Layer.Markers.prototype.onMapResize.apply(this,
arguments);
}
},
/**
* Method: display
* Call the display method of the appropriate parent class.
*/
display: function() {
if(this.vectorMode) {
OpenLayers.Layer.Vector.prototype.display.apply(this,
arguments);
} else {
OpenLayers.Layer.Markers.prototype.display.apply(this,
arguments);
}
},
/**
* APIMethod: mergeNewParams
* Modify parameters for the layer and redraw.
*
* Parameters:
* newParams - {Object}
*/
mergeNewParams:function(newParams) {
var upperParams = OpenLayers.Util.upperCaseObject(newParams);
var newArguments = [upperParams];
return OpenLayers.Layer.HTTPRequest.prototype.mergeNewParams.apply(this,
newArguments);
},
/**
* APIMethod: clone
*
* Parameters:
* obj - {Object}
*
* Returns:
* {<OpenLayers.Layer.WFS>} An exact clone of this OpenLayers.Layer.WFS
*/
clone: function (obj) {
if (obj == null) {
obj = new OpenLayers.Layer.WFS(this.name,
this.url,
this.params,
this.getOptions());
}
//get all additions from superclasses
if (this.vectorMode) {
obj = OpenLayers.Layer.Vector.prototype.clone.apply(this, [obj]);
} else {
obj = OpenLayers.Layer.Markers.prototype.clone.apply(this, [obj]);
}
// copy/set any non-init, non-simple values here
return obj;
},
/**
* APIMethod: getFullRequestString
* combine the layer's url with its params and these newParams.
*
* Add the SRS parameter from 'projection' -- this is probably
* more eloquently done via a setProjection() method, but this
* works for now and always.
*
* Parameters:
* newParams - {Object}
* altUrl - {String} Use this as the url instead of the layer's url
*/
getFullRequestString:function(newParams, altUrl) {
var projectionCode = this.projection.getCode() || this.map.getProjection();
this.params.SRS = (projectionCode == "none") ? null : projectionCode;
return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(
this, arguments);
},
/**
* APIMethod: commit
* Write out the data to a WFS server.
*/
commit: function() {
if (!this.writer) {
var options = {};
if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
options.externalProjection = this.projection;
options.internalProjection = this.map.getProjectionObject();
}
this.writer = new OpenLayers.Format.WFS(options,this);
}
var data = this.writer.write(this.features);
OpenLayers.Request.POST({
url: this.url,
data: data,
success: this.commitSuccess,
failure: this.commitFailure,
scope: this
});
},
/**
* Method: commitSuccess
* Called when the Ajax request returns a response
*
* Parameters:
* response - {XmlNode} from server
*/
commitSuccess: function(request) {
var response = request.responseText;
if (response.indexOf('SUCCESS') != -1) {
this.commitReport(OpenLayers.i18n("commitSuccess", {'response':response}));
for(var i = 0; i < this.features.length; i++) {
this.features[i].state = null;
}
// TBD redraw the layer or reset the state of features
// foreach features: set state to null
} else if (response.indexOf('FAILED') != -1 ||
response.indexOf('Exception') != -1) {
this.commitReport(OpenLayers.i18n("commitFailed", {'response':response}));
}
},
/**
* Method: commitFailure
* Called when the Ajax request fails
*
* Parameters:
* response - {XmlNode} from server
*/
commitFailure: function(request) {},
/**
* APIMethod: commitReport
* Called with a 'success' message if the commit succeeded, otherwise
* a failure message, and the full request text as a second parameter.
* Override this function to provide custom transaction reporting.
*
* string - {String} reporting string
* response - {String} full XML response
*/
commitReport: function(string, response) {
OpenLayers.Console.userError(string);
},
/**
* APIMethod: refresh
* Refreshes all the features of the layer
*/
refresh: function() {
if (this.tile) {
if (this.vectorMode) {
this.renderer.clear();
this.features.length = 0;
} else {
this.clearMarkers();
this.markers.length = 0;
}
this.tile.draw();
}
},
/**
* APIMethod: getDataExtent
* Calculates the max extent which includes all of the layer data.
*
* Returns:
* {<OpenLayers.Bounds>}
*/
getDataExtent: function () {
var extent;
//get all additions from superclasses
if (this.vectorMode) {
extent = OpenLayers.Layer.Vector.prototype.getDataExtent.apply(this);
} else {
extent = OpenLayers.Layer.Markers.prototype.getDataExtent.apply(this);
}
return extent;
},
/**
* APIMethod: setOpacity
* Call the setOpacity method of the appropriate parent class to set the
* opacity.
*
* Parameters:
* opacity - {Float}
*/
setOpacity: function (opacity) {
if (this.vectorMode) {
OpenLayers.Layer.Vector.prototype.setOpacity.apply(this, [opacity]);
} else {
OpenLayers.Layer.Markers.prototype.setOpacity.apply(this, [opacity]);
}
},
CLASS_NAME: "OpenLayers.Layer.WFS"
});
/**
* Class: OpenLayers.Layer.VirtualEarth
* *Deprecated*. Use <OpenLayers.Layer.Bing> instead.
*
* Instances of OpenLayers.Layer.VirtualEarth are used to display the data from
* the Bing Maps AJAX Control (see e.g.
* http://msdn.microsoft.com/library/bb429619.aspx). Create a VirtualEarth
* layer with the <OpenLayers.Layer.VirtualEarth> constructor.
*
* Inherits from:
* - <OpenLayers.Layer.EventPane>
* - <OpenLayers.Layer.FixedZoomLevels>
*/
OpenLayers.Layer.VirtualEarth = OpenLayers.Class(
OpenLayers.Layer.EventPane,
OpenLayers.Layer.FixedZoomLevels, {
/**
* Constant: MIN_ZOOM_LEVEL
* {Integer} 1
*/
MIN_ZOOM_LEVEL: 1,
/**
* Constant: MAX_ZOOM_LEVEL
* {Integer} 19
*/
MAX_ZOOM_LEVEL: 19,
/**
* Constant: RESOLUTIONS
* {Array(Float)} Hardcode these resolutions so that they are more closely
* tied with the standard wms projection
*/
RESOLUTIONS: [
1.40625,
0.703125,
0.3515625,
0.17578125,
0.087890625,
0.0439453125,
0.02197265625,
0.010986328125,
0.0054931640625,
0.00274658203125,
0.001373291015625,
0.0006866455078125,
0.00034332275390625,
0.000171661376953125,
0.0000858306884765625,
0.00004291534423828125,
0.00002145767211914062,
0.00001072883605957031,
0.00000536441802978515
],
/**
* APIProperty: type
* {VEMapType}
*/
type: null,
/**
* APIProperty: wrapDateLine
* {Boolean} Allow user to pan forever east/west. Default is true.
* Setting this to false only restricts panning if
* <sphericalMercator> is true.
*/
wrapDateLine: true,
/**
* APIProperty: sphericalMercator
* {Boolean} Should the map act as a mercator-projected map? This will
* cause all interactions with the map to be in the actual map
* projection, which allows support for vector drawing, overlaying
* other maps, etc.
*/
sphericalMercator: false,
/**
* APIProperty: animationEnabled
* {Boolean} If set to true, the transition between zoom levels will be
* animated. Set to false to match the zooming experience of other
* layer types. Default is true.
*/
animationEnabled: true,
/**
* Constructor: OpenLayers.Layer.VirtualEarth
* Creates a new instance of a OpenLayers.Layer.VirtualEarth. If you use an
* instance of OpenLayers.Layer.VirtualEarth in you map, you should set
* the <OpenLayers.Map> option restrictedExtent to a meaningful value,
* e.g.:
* (code)
* var map = new OpenLayers.Map( 'map', {
* // other map options
* restrictedExtent : OpenLayers.Bounds(-20037508, -20037508, 20037508, 20037508)
* } );
*
* var veLayer = new OpenLayers.Layer.VirtualEarth (
* "Virtual Earth Layer"
* );
*
* map.addLayer( veLayer );
* (end)
*
* Parameters:
* name - {String}
* options - {Object}
*/
initialize: function(name, options) {
OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
arguments);
if(this.sphericalMercator) {
OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
this.initMercatorParameters();
}
},
/**
* Method: loadMapObject
*/
loadMapObject:function() {
// create div and set to same size as map
var veDiv = OpenLayers.Util.createDiv(this.name);
var sz = this.map.getSize();
veDiv.style.width = sz.w + "px";
veDiv.style.height = sz.h + "px";
this.div.appendChild(veDiv);
try { // crash prevention
this.mapObject = new VEMap(this.name);
} catch (e) { }
if (this.mapObject != null) {
try { // this is to catch a Mozilla bug without falling apart
// The fourth argument is whether the map is 'fixed' -- not
// draggable. See:
// http://blogs.msdn.com/virtualearth/archive/2007/09/28/locking-a-virtual-earth-map.aspx
//
this.mapObject.LoadMap(null, null, this.type, true);
this.mapObject.AttachEvent("onmousedown", OpenLayers.Function.True);
} catch (e) { }
this.mapObject.HideDashboard();
if(typeof this.mapObject.SetAnimationEnabled == "function") {
this.mapObject.SetAnimationEnabled(this.animationEnabled);
}
}
//can we do smooth panning? this is an unpublished method, so we need
// to be careful
if ( !this.mapObject ||
!this.mapObject.vemapcontrol ||
!this.mapObject.vemapcontrol.PanMap ||
(typeof this.mapObject.vemapcontrol.PanMap != "function")) {
this.dragPanMapObject = null;
}
},
/**
* Method: onMapResize
*/
onMapResize: function() {
this.mapObject.Resize(this.map.size.w, this.map.size.h);
},
/**
* APIMethod: getWarningHTML
*
* Returns:
* {String} String with information on why layer is broken, how to get
* it working.
*/
getWarningHTML:function() {
return OpenLayers.i18n(
"getLayerWarning", {'layerType':'VE', 'layerLib':'VirtualEarth'}
);
},
/************************************
* *
* MapObject Interface Controls *
* *
************************************/
// Get&Set Center, Zoom
/**
* APIMethod: setMapObjectCenter
* Set the mapObject to the specified center and zoom
*
* Parameters:
* center - {Object} MapObject LonLat format
* zoom - {int} MapObject zoom format
*/
setMapObjectCenter: function(center, zoom) {
this.mapObject.SetCenterAndZoom(center, zoom);
},
/**
* APIMethod: getMapObjectCenter
*
* Returns:
* {Object} The mapObject's current center in Map Object format
*/
getMapObjectCenter: function() {
return this.mapObject.GetCenter();
},
/**
* APIMethod: dragPanMapObject
*
* Parameters:
* dX - {Integer}
* dY - {Integer}
*/
dragPanMapObject: function(dX, dY) {
this.mapObject.vemapcontrol.PanMap(dX, -dY);
},
/**
* APIMethod: getMapObjectZoom
*
* Returns:
* {Integer} The mapObject's current zoom, in Map Object format
*/
getMapObjectZoom: function() {
return this.mapObject.GetZoomLevel();
},
// LonLat - Pixel Translation
/**
* APIMethod: getMapObjectLonLatFromMapObjectPixel
*
* Parameters:
* moPixel - {Object} MapObject Pixel format
*
* Returns:
* {Object} MapObject LonLat translated from MapObject Pixel
*/
getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
//the conditional here is to test if we are running the v6 of VE
return (typeof VEPixel != 'undefined')
? this.mapObject.PixelToLatLong(moPixel)
: this.mapObject.PixelToLatLong(moPixel.x, moPixel.y);
},
/**
* APIMethod: getMapObjectPixelFromMapObjectLonLat
*
* Parameters:
* moLonLat - {Object} MapObject LonLat format
*
* Returns:
* {Object} MapObject Pixel transtlated from MapObject LonLat
*/
getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
return this.mapObject.LatLongToPixel(moLonLat);
},
/************************************
* *
* MapObject Primitives *
* *
************************************/
// LonLat
/**
* APIMethod: getLongitudeFromMapObjectLonLat
*
* Parameters:
* moLonLat - {Object} MapObject LonLat format
*
* Returns:
* {Float} Longitude of the given MapObject LonLat
*/
getLongitudeFromMapObjectLonLat: function(moLonLat) {
return this.sphericalMercator ?
this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lon :
moLonLat.Longitude;
},
/**
* APIMethod: getLatitudeFromMapObjectLonLat
*
* Parameters:
* moLonLat - {Object} MapObject LonLat format
*
* Returns:
* {Float} Latitude of the given MapObject LonLat
*/
getLatitudeFromMapObjectLonLat: function(moLonLat) {
return this.sphericalMercator ?
this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lat :
moLonLat.Latitude;
},
/**
* APIMethod: getMapObjectLonLatFromLonLat
*
* Parameters:
* lon - {Float}
* lat - {Float}
*
* Returns:
* {Object} MapObject LonLat built from lon and lat params
*/
getMapObjectLonLatFromLonLat: function(lon, lat) {
var veLatLong;
if(this.sphericalMercator) {
var lonlat = this.inverseMercator(lon, lat);
veLatLong = new VELatLong(lonlat.lat, lonlat.lon);
} else {
veLatLong = new VELatLong(lat, lon);
}
return veLatLong;
},
// Pixel
/**
* APIMethod: getXFromMapObjectPixel
*
* Parameters:
* moPixel - {Object} MapObject Pixel format
*
* Returns:
* {Integer} X value of the MapObject Pixel
*/
getXFromMapObjectPixel: function(moPixel) {
return moPixel.x;
},
/**
* APIMethod: getYFromMapObjectPixel
*
* Parameters:
* moPixel - {Object} MapObject Pixel format
*
* Returns:
* {Integer} Y value of the MapObject Pixel
*/
getYFromMapObjectPixel: function(moPixel) {
return moPixel.y;
},
/**
* APIMethod: getMapObjectPixelFromXY
*
* Parameters:
* x - {Integer}
* y - {Integer}
*
* Returns:
* {Object} MapObject Pixel from x and y parameters
*/
getMapObjectPixelFromXY: function(x, y) {
//the conditional here is to test if we are running the v6 of VE
return (typeof VEPixel != 'undefined') ? new VEPixel(x, y)
: new Msn.VE.Pixel(x, y);
},
CLASS_NAME: "OpenLayers.Layer.VirtualEarth"
});
/*
* Copyright 2007, Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of Google Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Sets up google.gears.*, which is *the only* supported way to access Gears.
*
* Circumvent this file at your own risk!
*
* In the future, Gears may automatically define google.gears.* without this
* file. Gears may use these objects to transparently fix bugs and compatibility
* issues. Applications that use the code below will continue to work seamlessly
* when that happens.
*/
(function() {
// We are already defined. Hooray!
if (window.google && google.gears) {
return;
}
var factory = null;
// Firefox
if (typeof GearsFactory != 'undefined') {
factory = new GearsFactory();
} else {
// IE
try {
factory = new ActiveXObject('Gears.Factory');
// privateSetGlobalObject is only required and supported on WinCE.
if (factory.getBuildInfo().indexOf('ie_mobile') != -1) {
factory.privateSetGlobalObject(this);
}
} catch (e) {
// Safari
if ((typeof navigator.mimeTypes != 'undefined')
&& navigator.mimeTypes["application/x-googlegears"]) {
factory = document.createElement("object");
factory.style.display = "none";
factory.width = 0;
factory.height = 0;
factory.type = "application/x-googlegears";
document.documentElement.appendChild(factory);
}
}
}
// *Do not* define any objects if Gears is not installed. This mimics the
// behavior of Gears defining the objects in the future.
if (!factory) {
return;
}
// Now set up the objects, being careful not to overwrite anything.
//
// Note: In Internet Explorer for Windows Mobile, you can't add properties to
// the window object. However, global objects are automatically added as
// properties of the window object in all browsers.
if (!window.google) {
google = {};
}
if (!google.gears) {
google.gears = {factory: factory};
}
})();
/**
* Class: OpenLayers.Protocol.SQL
* Abstract SQL protocol class. Not to be instantiated directly. Use
* one of the SQL protocol subclasses instead.
*
* Inherits from:
* - <OpenLayers.Protocol>
*/
OpenLayers.Protocol.SQL = OpenLayers.Class(OpenLayers.Protocol, {
/**
* APIProperty: databaseName
* {String}
*/
databaseName: 'ol',
/**
* APIProperty: tableName
* Name of the database table into which Features should be saved.
*/
tableName: "ol_vector_features",
/**
* Property: postReadFiltering
* {Boolean} Whether the filter (if there's one) must be applied after
* the features have been read from the database; for example the
* BBOX strategy passes the read method a BBOX spatial filter, if
* postReadFiltering is true every feature read from the database
* will go through the BBOX spatial filter, which can be costly;
* defaults to true.
*/
postReadFiltering: true,
/**
* Constructor: OpenLayers.Protocol.SQL
*/
initialize: function(options) {
OpenLayers.Protocol.prototype.initialize.apply(this, [options]);
},
/**
* APIMethod: destroy
* Clean up the protocol.
*/
destroy: function() {
OpenLayers.Protocol.prototype.destroy.apply(this);
},
/**
* APIMethod: supported
* This should be overridden by specific subclasses
*
* Returns:
* {Boolean} Whether or not the browser supports the SQL backend
*/
supported: function() {
return false;
},
/**
* Method: evaluateFilter
* If postReadFiltering is true evaluate the filter against the feature
* and return the result of the evaluation, otherwise return true.
*
* Parameters:
* {<OpenLayers.Feature.Vector>} The feature.
* {<OpenLayers.Filter>} The filter.
*
* Returns:
* {Boolean} true if postReadFiltering if false, the result of the
* filter evaluation otherwise.
*/
evaluateFilter: function(feature, filter) {
return filter && this.postReadFiltering ?
filter.evaluate(feature) : true;
},
CLASS_NAME: "OpenLayers.Protocol.SQL"
});
/**
* Class: OpenLayers.Protocol.SQL.Gears
* This Protocol stores feature in the browser via the Gears Database module
* <http://code.google.com/apis/gears/api_database.html>.
*
* The main advantage is that all the read, create, update and delete operations
* can be done offline.
*
* Inherits from:
* - <OpenLayers.Protocol.SQL>
*/
OpenLayers.Protocol.SQL.Gears = OpenLayers.Class(OpenLayers.Protocol.SQL, {
/**
* Property: FID_PREFIX
* {String}
*/
FID_PREFIX: '__gears_fid__',
/**
* Property: NULL_GEOMETRY
* {String}
*/
NULL_GEOMETRY: '__gears_null_geometry__',
/**
* Property: NULL_FEATURE_STATE
* {String}
*/
NULL_FEATURE_STATE: '__gears_null_feature_state__',
/**
* Property: jsonParser
* {<OpenLayers.Format.JSON>}
*/
jsonParser: null,
/**
* Property: wktParser
* {<OpenLayers.Format.WKT>}
*/
wktParser: null,
/**
* Property: fidRegExp
* {RegExp} Regular expression to know whether a feature was
* created in offline mode.
*/
fidRegExp: null,
/**
* Property: saveFeatureState
* {Boolean} Whether to save the feature state (<OpenLayers.State>)
* into the database, defaults to true.
*/
saveFeatureState: true,
/**
* Property: typeOfFid
* {String} The type of the feature identifier, either "number" or
* "string", defaults to "string".
*/
typeOfFid: "string",
/**
* Property: db
* {GearsDatabase}
*/
db: null,
/**
* Constructor: OpenLayers.Protocol.SQL.Gears
*/
initialize: function(options) {
if (!this.supported()) {
return;
}
OpenLayers.Protocol.SQL.prototype.initialize.apply(this, [options]);
this.jsonParser = new OpenLayers.Format.JSON();
this.wktParser = new OpenLayers.Format.WKT();
this.fidRegExp = new RegExp('^' + this.FID_PREFIX);
this.initializeDatabase();
},
/**
* Method: initializeDatabase
*/
initializeDatabase: function() {
this.db = google.gears.factory.create('beta.database');
this.db.open(this.databaseName);
this.db.execute(
"CREATE TABLE IF NOT EXISTS " + this.tableName +
" (fid TEXT UNIQUE, geometry TEXT, properties TEXT," +
" state TEXT)");
},
/**
* APIMethod: destroy
* Clean up the protocol.
*/
destroy: function() {
this.db.close();
this.db = null;
this.jsonParser = null;
this.wktParser = null;
OpenLayers.Protocol.SQL.prototype.destroy.apply(this);
},
/**
* APIMethod: supported
* Determine whether a browser supports Gears
*
* Returns:
* {Boolean} The browser supports Gears
*/
supported: function() {
return !!(window.google && google.gears);
},
/**
* APIMethod: read
* Read all features from the database and return a
* <OpenLayers.Protocol.Response> instance. If the options parameter
* contains a callback attribute, the function is called with the response
* as a parameter.
*
* Parameters:
* options - {Object} Optional object for configuring the request; it
* can have the {Boolean} property "noFeatureStateReset" which
* specifies if the state of features read from the Gears
* database must be reset to null, if "noFeatureStateReset"
* is undefined or false then each feature's state is reset
* to null, if "noFeatureStateReset" is true the feature state
* is preserved.
*
* Returns:
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
* object.
*/
read: function(options) {
OpenLayers.Protocol.prototype.read.apply(this, arguments);
options = OpenLayers.Util.applyDefaults(options, this.options);
var feature, features = [];
var rs = this.db.execute("SELECT * FROM " + this.tableName);
while (rs.isValidRow()) {
feature = this.unfreezeFeature(rs);
if (this.evaluateFilter(feature, options.filter)) {
if (!options.noFeatureStateReset) {
feature.state = null;
}
features.push(feature);
}
rs.next();
}
rs.close();
var resp = new OpenLayers.Protocol.Response({
code: OpenLayers.Protocol.Response.SUCCESS,
requestType: "read",
features: features
});
if (options && options.callback) {
options.callback.call(options.scope, resp);
}
return resp;
},
/**
* Method: unfreezeFeature
*
* Parameters:
* row - {ResultSet}
*
* Returns:
* {<OpenLayers.Feature.Vector>}
*/
unfreezeFeature: function(row) {
var feature;
var wkt = row.fieldByName('geometry');
if (wkt == this.NULL_GEOMETRY) {
feature = new OpenLayers.Feature.Vector();
} else {
feature = this.wktParser.read(wkt);
}
feature.attributes = this.jsonParser.read(
row.fieldByName('properties'));
feature.fid = this.extractFidFromField(row.fieldByName('fid'));
var state = row.fieldByName('state');
if (state == this.NULL_FEATURE_STATE) {
state = null;
}
feature.state = state;
return feature;
},
/**
* Method: extractFidFromField
*
* Parameters:
* field - {String}
*
* Returns
* {String} or {Number} The fid.
*/
extractFidFromField: function(field) {
if (!field.match(this.fidRegExp) && this.typeOfFid == "number") {
field = parseFloat(field);
}
return field;
},
/**
* APIMethod: create
* Create new features into the database.
*
* Parameters:
* features - {Array({<OpenLayers.Feature.Vector>})} or
* {<OpenLayers.Feature.Vector>} The features to create in
* the database.
* options - {Object} Optional object for configuring the request.
*
* Returns:
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
* object.
*/
create: function(features, options) {
options = OpenLayers.Util.applyDefaults(options, this.options);
var resp = this.createOrUpdate(features);
resp.requestType = "create";
if (options && options.callback) {
options.callback.call(options.scope, resp);
}
return resp;
},
/**
* APIMethod: update
* Construct a request updating modified feature.
*
* Parameters:
* features - {Array({<OpenLayers.Feature.Vector>})} or
* {<OpenLayers.Feature.Vector>} The features to update in
* the database.
* options - {Object} Optional object for configuring the request.
*
* Returns:
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
* object.
*/
update: function(features, options) {
options = OpenLayers.Util.applyDefaults(options, this.options);
var resp = this.createOrUpdate(features);
resp.requestType = "update";
if (options && options.callback) {
options.callback.call(options.scope, resp);
}
return resp;
},
/**
* Method: createOrUpdate
* Construct a request for updating or creating features in the
* database.
*
* Parameters:
* features - {Array({<OpenLayers.Feature.Vector>})} or
* {<OpenLayers.Feature.Vector>} The feature to create or update
* in the database.
*
* Returns:
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
* object.
*/
createOrUpdate: function(features) {
if (!(OpenLayers.Util.isArray(features))) {
features = [features];
}
var i, len = features.length, feature;
var insertedFeatures = new Array(len);
for (i = 0; i < len; i++) {
feature = features[i];
var params = this.freezeFeature(feature);
this.db.execute(
"REPLACE INTO " + this.tableName +
" (fid, geometry, properties, state)" +
" VALUES (?, ?, ?, ?)",
params);
var clone = feature.clone();
clone.fid = this.extractFidFromField(params[0]);
insertedFeatures[i] = clone;
}
return new OpenLayers.Protocol.Response({
code: OpenLayers.Protocol.Response.SUCCESS,
features: insertedFeatures,
reqFeatures: features
});
},
/**
* Method: freezeFeature
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
* state - {String} The feature state to store in the database.
*
* Returns:
* {Array}
*/
freezeFeature: function(feature) {
// 2 notes:
// - fid might not be a string
// - getFeatureStateForFreeze needs the feature fid to it's stored
// in the feature here
feature.fid = feature.fid != null ?
"" + feature.fid : OpenLayers.Util.createUniqueID(this.FID_PREFIX);
var geometry = feature.geometry != null ?
feature.geometry.toString() : this.NULL_GEOMETRY;
var properties = this.jsonParser.write(feature.attributes);
var state = this.getFeatureStateForFreeze(feature);
return [feature.fid, geometry, properties, state];
},
/**
* Method: getFeatureStateForFreeze
* Get the state of the feature to store into the database.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>} The feature.
*
* Returns
* {String} The state
*/
getFeatureStateForFreeze: function(feature) {
var state;
if (!this.saveFeatureState) {
state = this.NULL_FEATURE_STATE;
} else if (this.createdOffline(feature)) {
// if the feature was created in offline mode, its
// state must remain INSERT
state = OpenLayers.State.INSERT;
} else {
state = feature.state;
}
return state;
},
/**
* APIMethod: delete
* Delete features from the database.
*
* Parameters:
* features - {Array({<OpenLayers.Feature.Vector>})} or
* {<OpenLayers.Feature.Vector>}
* options - {Object} Optional object for configuring the request.
* This object is modified and should not be reused.
*
* Returns:
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
* object.
*/
"delete": function(features, options) {
if (!(OpenLayers.Util.isArray(features))) {
features = [features];
}
options = OpenLayers.Util.applyDefaults(options, this.options);
var i, len, feature;
for (i = 0, len = features.length; i < len; i++) {
feature = features[i];
// if saveFeatureState is set to true and if the feature wasn't created
// in offline mode we don't delete it in the database but just update
// it state column
if (this.saveFeatureState && !this.createdOffline(feature)) {
var toDelete = feature.clone();
toDelete.fid = feature.fid;
if (toDelete.geometry) {
toDelete.geometry.destroy();
toDelete.geometry = null;
}
toDelete.state = feature.state;
this.createOrUpdate(toDelete);
} else {
this.db.execute(
"DELETE FROM " + this.tableName +
" WHERE fid = ?", [feature.fid]);
}
}
var resp = new OpenLayers.Protocol.Response({
code: OpenLayers.Protocol.Response.SUCCESS,
requestType: "delete",
reqFeatures: features
});
if (options && options.callback) {
options.callback.call(options.scope, resp);
}
return resp;
},
/**
* Method: createdOffline
* Returns true if the feature had a feature id when it was created in
* the Gears database, false otherwise; this is determined by
* checking the form of the feature's fid value.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
*
* Returns:
* {Boolean}
*/
createdOffline: function(feature) {
return (typeof feature.fid == "string" &&
!!(feature.fid.match(this.fidRegExp)));
},
/**
* APIMethod: commit
* Go over the features and for each take action
* based on the feature state. Possible actions are create,
* update and delete.
*
* Parameters:
* features - {Array({<OpenLayers.Feature.Vector>})}
* options - {Object} Object whose possible keys are "create", "update",
* "delete", "callback" and "scope", the values referenced by the
* first three are objects as passed to the "create", "update", and
* "delete" methods, the value referenced by the "callback" key is
* a function which is called when the commit operation is complete
* using the scope referenced by the "scope" key.
*
* Returns:
* {Array({<OpenLayers.Protocol.Response>})} An array of
* <OpenLayers.Protocol.Response> objects, one per request made
* to the database.
*/
commit: function(features, options) {
var opt, resp = [], nRequests = 0, nResponses = 0;
function callback(resp) {
if (++nResponses < nRequests) {
resp.last = false;
}
this.callUserCallback(options, resp);
}
var feature, toCreate = [], toUpdate = [], toDelete = [];
for (var i = features.length - 1; i >= 0; i--) {
feature = features[i];
switch (feature.state) {
case OpenLayers.State.INSERT:
toCreate.push(feature);
break;
case OpenLayers.State.UPDATE:
toUpdate.push(feature);
break;
case OpenLayers.State.DELETE:
toDelete.push(feature);
break;
}
}
if (toCreate.length > 0) {
nRequests++;
opt = OpenLayers.Util.applyDefaults(
{"callback": callback, "scope": this},
options.create
);
resp.push(this.create(toCreate, opt));
}
if (toUpdate.length > 0) {
nRequests++;
opt = OpenLayers.Util.applyDefaults(
{"callback": callback, "scope": this},
options.update
);
resp.push(this.update(toUpdate, opt));
}
if (toDelete.length > 0) {
nRequests++;
opt = OpenLayers.Util.applyDefaults(
{"callback": callback, "scope": this},
options["delete"]
);
resp.push(this["delete"](toDelete, opt));
}
return resp;
},
/**
* Method: clear
* Removes all rows of the table.
*/
clear: function() {
this.db.execute("DELETE FROM " + this.tableName);
},
/**
* Method: callUserCallback
* This method is called from within commit each time a request is made
* to the database, it is responsible for calling the user-supplied
* callbacks.
*
* Parameters:
* options - {Object} The map of options passed to the commit call.
* resp - {<OpenLayers.Protocol.Response>}
*/
callUserCallback: function(options, resp) {
var opt = options[resp.requestType];
if (opt && opt.callback) {
opt.callback.call(opt.scope, resp);
}
if (resp.last && options.callback) {
options.callback.call(options.scope);
}
},
CLASS_NAME: "OpenLayers.Protocol.SQL.Gears"
});
/**
* Class: OpenLayers.Layer.Yahoo
*
* Inherits from:
* - <OpenLayers.Layer.EventPane>
* - <OpenLayers.Layer.FixedZoomLevels>
*/
OpenLayers.Layer.Yahoo = OpenLayers.Class(
OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {
/**
* Constant: MIN_ZOOM_LEVEL
* {Integer} 0
*/
MIN_ZOOM_LEVEL: 0,
/**
* Constant: MAX_ZOOM_LEVEL
* {Integer} 17
*/
MAX_ZOOM_LEVEL: 17,
/**
* Constant: RESOLUTIONS
* {Array(Float)} Hardcode these resolutions so that they are more closely
* tied with the standard wms projection
*/
RESOLUTIONS: [
1.40625,
0.703125,
0.3515625,
0.17578125,
0.087890625,
0.0439453125,
0.02197265625,
0.010986328125,
0.0054931640625,
0.00274658203125,
0.001373291015625,
0.0006866455078125,
0.00034332275390625,
0.000171661376953125,
0.0000858306884765625,
0.00004291534423828125,
0.00002145767211914062,
0.00001072883605957031
],
/**
* APIProperty: type
* {YahooMapType}
*/
type: null,
/**
* APIProperty: wrapDateLine
* {Boolean} Allow user to pan forever east/west. Default is true.
* Setting this to false only restricts panning if
* <sphericalMercator> is true.
*/
wrapDateLine: true,
/**
* APIProperty: sphericalMercator
* {Boolean} Should the map act as a mercator-projected map? This will
* cause all interactions with the map to be in the actual map projection,
* which allows support for vector drawing, overlaying other maps, etc.
*/
sphericalMercator: false,
/**
* Constructor: OpenLayers.Layer.Yahoo
*
* Parameters:
* name - {String}
* options - {Object}
*/
initialize: function(name, options) {
OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
arguments);
if(this.sphericalMercator) {
OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
this.initMercatorParameters();
}
},
/**
* Method: loadMapObject
*/
loadMapObject:function() {
try { //do not crash!
var size = this.getMapObjectSizeFromOLSize(this.map.getSize());
this.mapObject = new YMap(this.div, this.type, size);
this.mapObject.disableKeyControls();
this.mapObject.disableDragMap();
//can we do smooth panning? (moveByXY is not an API function)
if ( !this.mapObject.moveByXY ||
(typeof this.mapObject.moveByXY != "function" ) ) {
this.dragPanMapObject = null;
}
} catch(e) {}
},
/**
* Method: onMapResize
*
*/
onMapResize: function() {
try {
var size = this.getMapObjectSizeFromOLSize(this.map.getSize());
this.mapObject.resizeTo(size);
} catch(e) {}
},
/**
* APIMethod: setMap
* Overridden from EventPane because we need to remove this yahoo event
* pane which prohibits our drag and drop, and we can only do this
* once the map has been loaded and centered.
*
* Parameters:
* map - {<OpenLayers.Map>}
*/
setMap: function(map) {
OpenLayers.Layer.EventPane.prototype.setMap.apply(this, arguments);
this.map.events.register("moveend", this, this.fixYahooEventPane);
},
/**
* Method: fixYahooEventPane
* The map has been centered, so the mysterious yahoo eventpane has been
* added. we remove it so that it doesnt mess with *our* event pane.
*/
fixYahooEventPane: function() {
var yahooEventPane = OpenLayers.Util.getElement("ygddfdiv");
if (yahooEventPane != null) {
if (yahooEventPane.parentNode != null) {
yahooEventPane.parentNode.removeChild(yahooEventPane);
}
this.map.events.unregister("moveend", this,
this.fixYahooEventPane);
}
},
/**
* APIMethod: getWarningHTML
*
* Returns:
* {String} String with information on why layer is broken, how to get
* it working.
*/
getWarningHTML:function() {
return OpenLayers.i18n(
"getLayerWarning", {'layerType':'Yahoo', 'layerLib':'Yahoo'}
);
},
/********************************************************/
/* */
/* Translation Functions */
/* */
/* The following functions translate GMaps and OL */
/* formats for Pixel, LonLat, Bounds, and Zoom */
/* */
/********************************************************/
//
// TRANSLATION: MapObject Zoom <-> OpenLayers Zoom
//
/**
* APIMethod: getOLZoomFromMapObjectZoom
*
* Parameters:
* gZoom - {Integer}
*
* Returns:
* {Integer} An OpenLayers Zoom level, translated from the passed in gZoom
* Returns null if null value is passed in.
*/
getOLZoomFromMapObjectZoom: function(moZoom) {
var zoom = null;
if (moZoom != null) {
zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getOLZoomFromMapObjectZoom.apply(this, [moZoom]);
zoom = 18 - zoom;
}
return zoom;
},
/**
* APIMethod: getMapObjectZoomFromOLZoom
*
* Parameters:
* olZoom - {Integer}
*
* Returns:
* {Integer} A MapObject level, translated from the passed in olZoom
* Returns null if null value is passed in
*/
getMapObjectZoomFromOLZoom: function(olZoom) {
var zoom = null;
if (olZoom != null) {
zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getMapObjectZoomFromOLZoom.apply(this, [olZoom]);
zoom = 18 - zoom;
}
return zoom;
},
/************************************
* *
* MapObject Interface Controls *
* *
************************************/
// Get&Set Center, Zoom
/**
* APIMethod: setMapObjectCenter
* Set the mapObject to the specified center and zoom
*
* Parameters:
* center - {Object} MapObject LonLat format
* zoom - {int} MapObject zoom format
*/
setMapObjectCenter: function(center, zoom) {
this.mapObject.drawZoomAndCenter(center, zoom);
},
/**
* APIMethod: getMapObjectCenter
*
* Returns:
* {Object} The mapObject's current center in Map Object format
*/
getMapObjectCenter: function() {
return this.mapObject.getCenterLatLon();
},
/**
* APIMethod: dragPanMapObject
*
* Parameters:
* dX - {Integer}
* dY - {Integer}
*/
dragPanMapObject: function(dX, dY) {
this.mapObject.moveByXY({
'x': -dX,
'y': dY
});
},
/**
* APIMethod: getMapObjectZoom
*
* Returns:
* {Integer} The mapObject's current zoom, in Map Object format
*/
getMapObjectZoom: function() {
return this.mapObject.getZoomLevel();
},
// LonLat - Pixel Translation
/**
* APIMethod: getMapObjectLonLatFromMapObjectPixel
*
* Parameters:
* moPixel - {Object} MapObject Pixel format
*
* Returns:
* {Object} MapObject LonLat translated from MapObject Pixel
*/
getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
return this.mapObject.convertXYLatLon(moPixel);
},
/**
* APIMethod: getMapObjectPixelFromMapObjectLonLat
*
* Parameters:
* moLonLat - {Object} MapObject LonLat format
*
* Returns:
* {Object} MapObject Pixel transtlated from MapObject LonLat
*/
getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
return this.mapObject.convertLatLonXY(moLonLat);
},
/************************************
* *
* MapObject Primitives *
* *
************************************/
// LonLat
/**
* APIMethod: getLongitudeFromMapObjectLonLat
*
* Parameters:
* moLonLat - {Object} MapObject LonLat format
*
* Returns:
* {Float} Longitude of the given MapObject LonLat
*/
getLongitudeFromMapObjectLonLat: function(moLonLat) {
return this.sphericalMercator ?
this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lon :
moLonLat.Lon;
},
/**
* APIMethod: getLatitudeFromMapObjectLonLat
*
* Parameters:
* moLonLat - {Object} MapObject LonLat format
*
* Returns:
* {Float} Latitude of the given MapObject LonLat
*/
getLatitudeFromMapObjectLonLat: function(moLonLat) {
return this.sphericalMercator ?
this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lat :
moLonLat.Lat;
},
/**
* APIMethod: getMapObjectLonLatFromLonLat
*
* Parameters:
* lon - {Float}
* lat - {Float}
*
* Returns:
* {Object} MapObject LonLat built from lon and lat params
*/
getMapObjectLonLatFromLonLat: function(lon, lat) {
var yLatLong;
if(this.sphericalMercator) {
var lonlat = this.inverseMercator(lon, lat);
yLatLong = new YGeoPoint(lonlat.lat, lonlat.lon);
} else {
yLatLong = new YGeoPoint(lat, lon);
}
return yLatLong;
},
// Pixel
/**
* APIMethod: getXFromMapObjectPixel
*
* Parameters:
* moPixel - {Object} MapObject Pixel format
*
* Returns:
* {Integer} X value of the MapObject Pixel
*/
getXFromMapObjectPixel: function(moPixel) {
return moPixel.x;
},
/**
* APIMethod: getYFromMapObjectPixel
*
* Parameters:
* moPixel - {Object} MapObject Pixel format
*
* Returns:
* {Integer} Y value of the MapObject Pixel
*/
getYFromMapObjectPixel: function(moPixel) {
return moPixel.y;
},
/**
* APIMethod: getMapObjectPixelFromXY
*
* Parameters:
* x - {Integer}
* y - {Integer}
*
* Returns:
* {Object} MapObject Pixel from x and y parameters
*/
getMapObjectPixelFromXY: function(x, y) {
return new YCoordPoint(x, y);
},
// Size
/**
* APIMethod: getMapObjectSizeFromOLSize
*
* Parameters:
* olSize - {<OpenLayers.Size>}
*
* Returns:
* {Object} MapObject Size from olSize parameter
*/
getMapObjectSizeFromOLSize: function(olSize) {
return new YSize(olSize.w, olSize.h);
},
CLASS_NAME: "OpenLayers.Layer.Yahoo"
});
/**
* Class: OpenLayers.Layer.GML
* Create a vector layer by parsing a GML file. The GML file is
* passed in as a parameter.
* *Deprecated*. To be removed in 3.0. Instead use OpenLayers.Layer.Vector
* with Protocol.HTTP and Strategy.Fixed. Provide the protocol with a
* format parameter to get the parser you want for your data.
*
* Inherits from:
* - <OpenLayers.Layer.Vector>
*/
OpenLayers.Layer.GML = OpenLayers.Class(OpenLayers.Layer.Vector, {
/**
* Property: loaded
* {Boolean} Flag for whether the GML data has been loaded yet.
*/
loaded: false,
/**
* APIProperty: format
* {<OpenLayers.Format>} The format you want the data to be parsed with.
*/
format: null,
/**
* APIProperty: formatOptions
* {Object} Hash of options which should be passed to the format when it is
* created. Must be passed in the constructor.
*/
formatOptions: null,
/**
* Constructor: OpenLayers.Layer.GML
* Load and parse a single file on the web, according to the format
* provided via the 'format' option, defaulting to GML.
*
* Parameters:
* name - {String}
* url - {String} URL of a GML file.
* options - {Object} Hashtable of extra options to tag onto the layer.
*/
initialize: function(name, url, options) {
var newArguments = [];
newArguments.push(name, options);
OpenLayers.Layer.Vector.prototype.initialize.apply(this, newArguments);
this.url = url;
},
/**
* APIMethod: setVisibility
* Set the visibility flag for the layer and hide/show&redraw accordingly.
* Fire event unless otherwise specified
* GML will be loaded if the layer is being made visible for the first
* time.
*
* Parameters:
* visible - {Boolean} Whether or not to display the layer
* (if in range)
* noEvent - {Boolean}
*/
setVisibility: function(visibility, noEvent) {
OpenLayers.Layer.Vector.prototype.setVisibility.apply(this, arguments);
if(this.visibility && !this.loaded){
// Load the GML
this.loadGML();
}
},
/**
* Method: moveTo
* If layer is visible and GML has not been loaded, load GML, then load GML
* and call OpenLayers.Layer.Vector.moveTo() to redraw at the new location.
*
* Parameters:
* bounds - {Object}
* zoomChanged - {Object}
* minor - {Object}
*/
moveTo:function(bounds, zoomChanged, minor) {
OpenLayers.Layer.Vector.prototype.moveTo.apply(this, arguments);
// Wait until initialisation is complete before loading GML
// otherwise we can get a race condition where the root HTML DOM is
// loaded after the GML is paited.
// See http://trac.openlayers.org/ticket/404
if(this.visibility && !this.loaded){
this.loadGML();
}
},
/**
* Method: loadGML
*/
loadGML: function() {
if (!this.loaded) {
this.events.triggerEvent("loadstart");
OpenLayers.Request.GET({
url: this.url,
success: this.requestSuccess,
failure: this.requestFailure,
scope: this
});
this.loaded = true;
}
},
/**
* Method: setUrl
* Change the URL and reload the GML
*
* Parameters:
* url - {String} URL of a GML file.
*/
setUrl:function(url) {
this.url = url;
this.destroyFeatures();
this.loaded = false;
this.loadGML();
},
/**
* Method: requestSuccess
* Process GML after it has been loaded.
* Called by initialize() and loadUrl() after the GML has been loaded.
*
* Parameters:
* request - {String}
*/
requestSuccess:function(request) {
var doc = request.responseXML;
if (!doc || !doc.documentElement) {
doc = request.responseText;
}
var options = {};
OpenLayers.Util.extend(options, this.formatOptions);
if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
options.externalProjection = this.projection;
options.internalProjection = this.map.getProjectionObject();
}
var gml = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
this.addFeatures(gml.read(doc));
this.events.triggerEvent("loadend");
},
/**
* Method: requestFailure
* Process a failed loading of GML.
* Called by initialize() and loadUrl() if there was a problem loading GML.
*
* Parameters:
* request - {String}
*/
requestFailure: function(request) {
OpenLayers.Console.userError('Error in loading GML file ' + this.url);
this.events.triggerEvent("loadend");
},
CLASS_NAME: "OpenLayers.Layer.GML"
});
/**
* Class: OpenLayers.Geometry.Rectangle
* This class is *not supported*, and probably isn't what you're looking for.
* Instead, most users probably want something like:
* (code)
* var poly = new OpenLayers.Bounds(0,0,10,10).toGeometry();
* (end)
* This will create a rectangular Polygon geometry.
*
* Inherits:
* - <OpenLayers.Geometry>
*/
OpenLayers.Geometry.Rectangle = OpenLayers.Class(OpenLayers.Geometry, {
/**
* Property: x
* {Float}
*/
x: null,
/**
* Property: y
* {Float}
*/
y: null,
/**
* Property: width
* {Float}
*/
width: null,
/**
* Property: height
* {Float}
*/
height: null,
/**
* Constructor: OpenLayers.Geometry.Rectangle
*
* Parameters:
* points - {Array(<OpenLayers.Geometry.Point>)}
*/
initialize: function(x, y, width, height) {
OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
this.x = x;
this.y = y;
this.width = width;
this.height = height;
},
/**
* Method: calculateBounds
* Recalculate the bounds for the geometry.
*/
calculateBounds: function() {
this.bounds = new OpenLayers.Bounds(this.x, this.y,
this.x + this.width,
this.y + this.height);
},
/**
* APIMethod: getLength
*
* Returns:
* {Float} The length of the geometry
*/
getLength: function() {
var length = (2 * this.width) + (2 * this.height);
return length;
},
/**
* APIMethod: getArea
*
* Returns:
* {Float} The area of the geometry
*/
getArea: function() {
var area = this.width * this.height;
return area;
},
CLASS_NAME: "OpenLayers.Geometry.Rectangle"
});
/**
* Class: OpenLayers.Renderer.NG
*
* Inherits from:
* - <OpenLayers.Renderer.Elements>
*/
OpenLayers.Renderer.NG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
/**
* Constant: labelNodeType
* {String} The node type for text label containers. To be defined by
* subclasses.
*/
labelNodeType: null,
/**
* Constructor: OpenLayers.Renderer.NG
*
* Parameters:
* containerID - {String}
* options - {Object} options for this renderer. Supported options are:
* * yOrdering - {Boolean} Whether to use y-ordering
* * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored
* if yOrdering is set to true.
*/
/**
* Method: updateDimensions
* To be extended by subclasses - here we set positioning related styles
* on HTML elements, subclasses have to do the same for renderer specific
* elements (e.g. viewBox, width and height of the rendererRoot)
*
* Parameters:
* zoomChanged - {Boolean} Has the zoom changed? If so, subclasses may have
* to update feature styles/dimensions.
*/
updateDimensions: function(zoomChanged) {
var mapExtent = this.map.getExtent();
var renderExtent = mapExtent.scale(3);
this.setExtent(renderExtent, true);
var res = this.getResolution();
var div = this.rendererRoot.parentNode;
var layerLeft = parseFloat(div.parentNode.style.left);
var layerTop = parseFloat(div.parentNode.style.top);
div.style.left = ((renderExtent.left - mapExtent.left) / res - layerLeft) + "px";
div.style.top = ((mapExtent.top - renderExtent.top) / res - layerTop) + "px";
},
/**
* Method: resize
*/
setSize: function() {
this.map.getExtent() && this.updateDimensions();
},
/**
* Method: drawFeature
* Draw the feature. The optional style argument can be used
* to override the feature's own style. This method should only
* be called from layer.drawFeature().
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
* style - {<Object>}
*
* Returns:
* {Boolean} true if the feature has been drawn completely, false if not,
* undefined if the feature had no geometry
*/
drawFeature: function(feature, style) {
if(style == null) {
style = feature.style;
}
if (feature.geometry) {
var rendered = this.drawGeometry(feature.geometry, style, feature.id);
if(rendered !== false && style.label) {
var location = feature.geometry.getCentroid();
this.drawText(feature.id, style, location);
} else {
this.removeText(feature.id);
}
return rendered;
}
},
/**
* Method: drawText
* Function for drawing text labels.
* This method is only called by the renderer itself.
*
* Parameters:
* featureId - {String|DOMElement}
* style - {Object}
* location - {<OpenLayers.Geometry.Point>}, will be modified inline
*
* Returns:
* {DOMElement} container holding the text label (to be populated by
* subclasses)
*/
drawText: function(featureId, style, location) {
var label;
if (typeof featureId !== "string") {
label = featureId;
} else {
label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, this.labelNodeType);
label._featureId = featureId;
}
label._style = style;
label._x = location.x;
label._y = location.y;
if(style.labelXOffset || style.labelYOffset) {
var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;
var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;
var res = this.getResolution();
location.move(xOffset*res, yOffset*res);
}
if(label.parentNode !== this.textRoot) {
this.textRoot.appendChild(label);
}
return label;
},
CLASS_NAME: "OpenLayers.Renderer.NG"
});
// Monkey-patching Layer.Vector for Renderer.NG support
(function() {
var moveTo = OpenLayers.Layer.Vector.prototype.moveTo;
OpenLayers.Layer.Vector.prototype.moveTo = function(bounds, zoomChanged, dragging) {
if (OpenLayers.Renderer.NG && this.renderer instanceof OpenLayers.Renderer.NG) {
OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
dragging || this.renderer.updateDimensions(zoomChanged);
if (!this.drawn) {
this.drawn = true;
var feature;
for(var i=0, len=this.features.length; i<len; i++) {
this.renderer.locked = (i !== (len - 1));
feature = this.features[i];
this.drawFeature(feature);
}
}
} else {
moveTo.apply(this, arguments);
}
}
var redraw = OpenLayers.Layer.Vector.prototype.redraw;
OpenLayers.Layer.Vector.prototype.redraw = function() {
if (OpenLayers.Renderer.NG && this.renderer instanceof OpenLayers.Renderer.NG) {
this.drawn = false;
}
redraw.apply(this, arguments);
}
})();
/**
* Class: OpenLayers.Renderer.SVG2
*
* Inherits from:
* - <OpenLayers.Renderer.NG>
*/
OpenLayers.Renderer.SVG2 = OpenLayers.Class(OpenLayers.Renderer.NG, {
/**
* Property: xmlns
* {String}
*/
xmlns: "http://www.w3.org/2000/svg",
/**
* Property: xlinkns
* {String}
*/
xlinkns: "http://www.w3.org/1999/xlink",
/**
* Property: symbolMetrics
* {Object} Cache for symbol metrics according to their svg coordinate
* space. This is an object keyed by the symbol's id, and values are
* an object with size, x and y properties.
*/
symbolMetrics: null,
/**
* Constant: labelNodeType
* {String} The node type for text label containers.
*/
labelNodeType: "g",
/**
* Constructor: OpenLayers.Renderer.SVG2
*
* Parameters:
* containerID - {String}
*/
initialize: function(containerID) {
if (!this.supported()) {
return;
}
OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
arguments);
this.symbolMetrics = {};
},
/**
* APIMethod: supported
*
* Returns:
* {Boolean} Whether or not the browser supports the SVG renderer
*/
supported: function() {
var svgFeature = "http://www.w3.org/TR/SVG11/feature#";
return (document.implementation &&
(document.implementation.hasFeature("org.w3c.svg", "1.0") ||
document.implementation.hasFeature(svgFeature + "SVG", "1.1") ||
document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1") ));
},
/**
* Method: updateDimensions
*
* Parameters:
* zoomChanged - {Boolean}
*/
updateDimensions: function(zoomChanged) {
OpenLayers.Renderer.NG.prototype.updateDimensions.apply(this, arguments);
var res = this.getResolution();
var width = this.extent.getWidth();
var height = this.extent.getHeight();
var extentString = [
this.extent.left,
-this.extent.top,
width,
height
].join(" ");
this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
this.rendererRoot.setAttributeNS(null, "width", width / res);
this.rendererRoot.setAttributeNS(null, "height", height / res);
if (zoomChanged === true) {
// update styles for the new resolution
var i, len;
var nodes = this.vectorRoot.childNodes;
for (i=0, len=nodes.length; i<len; ++i) {
this.setStyle(nodes[i]);
}
var textNodes = this.textRoot.childNodes;
var label;
for (i=0, len=textNodes.length; i<len; ++i) {
label = textNodes[i];
this.drawText(label, label._style,
new OpenLayers.Geometry.Point(label._x, label._y)
);
}
}
},
/**
* Method: getNodeType
*
* Parameters:
* geometry - {<OpenLayers.Geometry>}
* style - {Object}
*
* Returns:
* {String} The corresponding node type for the specified geometry
*/
getNodeType: function(geometry, style) {
var nodeType = null;
switch (geometry.CLASS_NAME) {
case "OpenLayers.Geometry.Point":
if (style.externalGraphic) {
nodeType = "image";
} else if (this.isComplexSymbol(style.graphicName)) {
nodeType = "svg";
} else {
nodeType = "circle";
}
break;
case "OpenLayers.Geometry.Rectangle":
nodeType = "rect";
break;
case "OpenLayers.Geometry.LineString":
nodeType = "polyline";
break;
case "OpenLayers.Geometry.LinearRing":
nodeType = "polygon";
break;
case "OpenLayers.Geometry.Polygon":
case "OpenLayers.Geometry.Curve":
nodeType = "path";
break;
default:
break;
}
return nodeType;
},
/**
* Method: setStyle
* Use to set all the style attributes to a SVG node.
*
* Takes care to adjust stroke width and point radius to be
* resolution-relative
*
* Parameters:
* node - {SVGDomElement} An SVG element to decorate
* style - {Object}
* options - {Object} Currently supported options include
* 'isFilled' {Boolean} and
* 'isStroked' {Boolean}
*/
setStyle: function(node, style, options) {
style = style || node._style;
options = options || node._options;
var resolution = this.getResolution();
var r = node._radius;
var widthFactor = resolution;
if (node._geometryClass == "OpenLayers.Geometry.Point" && r) {
node.style.visibility = "";
if (style.graphic === false) {
node.style.visibility = "hidden";
} else if (style.externalGraphic) {
if (style.graphicTitle) {
node.setAttributeNS(null, "title", style.graphicTitle);
//Standards-conformant SVG
// Prevent duplicate nodes. See issue https://github.com/openlayers/openlayers/issues/92
var titleNode = node.getElementsByTagName("title");
if (titleNode.length > 0) {
titleNode[0].firstChild.textContent = style.graphicTitle;
} else {
var label = this.nodeFactory(null, "title");
label.textContent = style.graphicTitle;
node.appendChild(label);
}
}
if (style.graphicWidth && style.graphicHeight) {
node.setAttributeNS(null, "preserveAspectRatio", "none");
}
var width = style.graphicWidth || style.graphicHeight;
var height = style.graphicHeight || style.graphicWidth;
width = width ? width : style.pointRadius*2;
height = height ? height : style.pointRadius*2;
width *= resolution;
height *= resolution;
var xOffset = (style.graphicXOffset != undefined) ?
style.graphicXOffset * resolution : -(0.5 * width);
var yOffset = (style.graphicYOffset != undefined) ?
style.graphicYOffset * resolution : -(0.5 * height);
var opacity = style.graphicOpacity || style.fillOpacity;
node.setAttributeNS(null, "x", node._x + xOffset);
node.setAttributeNS(null, "y", node._y + yOffset);
node.setAttributeNS(null, "width", width);
node.setAttributeNS(null, "height", height);
node.setAttributeNS(this.xlinkns, "href", style.externalGraphic);
node.setAttributeNS(null, "style", "opacity: "+opacity);
node.onclick = OpenLayers.Renderer.SVG2.preventDefault;
} else if (this.isComplexSymbol(style.graphicName)) {
// the symbol viewBox is three times as large as the symbol
var offset = style.pointRadius * 3 * resolution;
var size = offset * 2;
var src = this.importSymbol(style.graphicName);
widthFactor = this.symbolMetrics[src.id].size * 3 / size * resolution;
// remove the node from the dom before we modify it. This
// prevents various rendering issues in Safari and FF
var parent = node.parentNode;
var nextSibling = node.nextSibling;
if(parent) {
parent.removeChild(node);
}
// The more appropriate way to implement this would be use/defs,
// but due to various issues in several browsers, it is safer to
// copy the symbols instead of referencing them.
// See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985
// and this email thread
// http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html
node.firstChild && node.removeChild(node.firstChild);
node.appendChild(src.firstChild.cloneNode(true));
node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox"));
node.setAttributeNS(null, "width", size);
node.setAttributeNS(null, "height", size);
node.setAttributeNS(null, "x", node._x - offset);
node.setAttributeNS(null, "y", node._y - offset);
// now that the node has all its new properties, insert it
// back into the dom where it was
if(nextSibling) {
parent.insertBefore(node, nextSibling);
} else if(parent) {
parent.appendChild(node);
}
} else {
node.setAttributeNS(null, "r", style.pointRadius * resolution);
}
var rotation = style.rotation;
if (rotation !== undefined || node._rotation !== undefined) {
node._rotation = rotation;
rotation |= 0;
if (node.nodeName !== "svg") {
node.setAttributeNS(null, "transform",
["rotate(", rotation, node._x, node._y, ")"].join(" ")
);
} else {
var metrics = this.symbolMetrics[src.id];
node.firstChild.setAttributeNS(null, "transform",
["rotate(", rotation, metrics.x, metrics.y, ")"].join(" ")
);
}
}
}
if (options.isFilled) {
node.setAttributeNS(null, "fill", style.fillColor);
node.setAttributeNS(null, "fill-opacity", style.fillOpacity);
} else {
node.setAttributeNS(null, "fill", "none");
}
if (options.isStroked) {
node.setAttributeNS(null, "stroke", style.strokeColor);
node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity);
node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor);
node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap || "round");
// Hard-coded linejoin for now, to make it look the same as in VML.
// There is no strokeLinejoin property yet for symbolizers.
node.setAttributeNS(null, "stroke-linejoin", "round");
style.strokeDashstyle && node.setAttributeNS(null,
"stroke-dasharray", this.dashStyle(style, widthFactor));
} else {
node.setAttributeNS(null, "stroke", "none");
}
if (style.pointerEvents) {
node.setAttributeNS(null, "pointer-events", style.pointerEvents);
}
if (style.cursor != null) {
node.setAttributeNS(null, "cursor", style.cursor);
}
return node;
},
/**
* Method: dashStyle
*
* Parameters:
* style - {Object}
* widthFactor - {Number}
*
* Returns:
* {String} A SVG compliant 'stroke-dasharray' value
*/
dashStyle: function(style, widthFactor) {
var w = style.strokeWidth * widthFactor;
var str = style.strokeDashstyle;
switch (str) {
case 'solid':
return 'none';
case 'dot':
return [widthFactor, 4 * w].join();
case 'dash':
return [4 * w, 4 * w].join();
case 'dashdot':
return [4 * w, 4 * w, widthFactor, 4 * w].join();
case 'longdash':
return [8 * w, 4 * w].join();
case 'longdashdot':
return [8 * w, 4 * w, widthFactor, 4 * w].join();
default:
var parts = OpenLayers.String.trim(str).split(/\s+/g);
for (var i=0, ii=parts.length; i<ii; i++) {
parts[i] = parts[i] * widthFactor;
}
return parts.join();
}
},
/**
* Method: createNode
*
* Parameters:
* type - {String} Kind of node to draw
* id - {String} Id for node
*
* Returns:
* {DOMElement} A new node of the given type and id
*/
createNode: function(type, id) {
var node = document.createElementNS(this.xmlns, type);
if (id) {
node.setAttributeNS(null, "id", id);
}
return node;
},
/**
* Method: nodeTypeCompare
*
* Parameters:
* node - {SVGDomElement} An SVG element
* type - {String} Kind of node
*
* Returns:
* {Boolean} Whether or not the specified node is of the specified type
*/
nodeTypeCompare: function(node, type) {
return (type == node.nodeName);
},
/**
* Method: createRenderRoot
*
* Returns:
* {DOMElement} The specific render engine's root element
*/
createRenderRoot: function() {
return this.nodeFactory(this.container.id + "_svgRoot", "svg");
},
/**
* Method: createRoot
*
* Parameters:
* suffix - {String} suffix to append to the id
*
* Returns:
* {DOMElement}
*/
createRoot: function(suffix) {
return this.nodeFactory(this.container.id + suffix, "g");
},
/**
* Method: createDefs
*
* Returns:
* {DOMElement} The element to which we'll add the symbol definitions
*/
createDefs: function() {
var defs = this.nodeFactory(this.container.id + "_defs", "defs");
this.rendererRoot.appendChild(defs);
return defs;
},
/**************************************
* *
* GEOMETRY DRAWING FUNCTIONS *
* *
**************************************/
/**
* Method: drawPoint
* This method is only called by the renderer itself.
*
* Parameters:
* node - {DOMElement}
* geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or false if the renderer could not draw the point
*/
drawPoint: function(node, geometry) {
return this.drawCircle(node, geometry, 1);
},
/**
* Method: drawCircle
* This method is only called by the renderer itself.
*
* Parameters:
* node - {DOMElement}
* geometry - {<OpenLayers.Geometry>}
* radius - {Float}
*
* Returns:
* {DOMElement} or false if the renderer could not draw the circle
*/
drawCircle: function(node, geometry, radius) {
var x = geometry.x;
var y = -geometry.y;
node.setAttributeNS(null, "cx", x);
node.setAttributeNS(null, "cy", y);
node._x = x;
node._y = y;
node._radius = radius;
return node;
},
/**
* Method: drawLineString
* This method is only called by the renderer itself.
*
* Parameters:
* node - {DOMElement}
* geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or null if the renderer could not draw all components of
* the linestring, or false if nothing could be drawn
*/
drawLineString: function(node, geometry) {
var path = this.getComponentsString(geometry.components);
node.setAttributeNS(null, "points", path);
return node;
},
/**
* Method: drawLinearRing
* This method is only called by the renderer itself.
*
* Parameters:
* node - {DOMElement}
* geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or null if the renderer could not draw all components
* of the linear ring, or false if nothing could be drawn
*/
drawLinearRing: function(node, geometry) {
var path = this.getComponentsString(geometry.components);
node.setAttributeNS(null, "points", path);
return node;
},
/**
* Method: drawPolygon
* This method is only called by the renderer itself.
*
* Parameters:
* node - {DOMElement}
* geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or null if the renderer could not draw all components
* of the polygon, or false if nothing could be drawn
*/
drawPolygon: function(node, geometry) {
var d = [];
var draw = true;
var complete = true;
var linearRingResult, path;
for (var j=0, len=geometry.components.length; j<len; j++) {
d.push("M");
path = this.getComponentsString(
geometry.components[j].components, " ");
d.push(path);
}
d.push("z");
node.setAttributeNS(null, "d", d.join(" "));
node.setAttributeNS(null, "fill-rule", "evenodd");
return node;
},
/**
* Method: drawRectangle
* This method is only called by the renderer itself.
*
* Parameters:
* node - {DOMElement}
* geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {DOMElement} or false if the renderer could not draw the rectangle
*/
drawRectangle: function(node, geometry) {
node.setAttributeNS(null, "x", geometry.x);
node.setAttributeNS(null, "y", -geometry.y);
node.setAttributeNS(null, "width", geometry.width);
node.setAttributeNS(null, "height", geometry.height);
return node;
},
/**
* Method: drawText
* Function for drawing text labels.
* This method is only called by the renderer itself.
*
* Parameters:
* featureId - {String|DOMElement}
* style - {Object}
* location - {<OpenLayers.Geometry.Point>}, will be modified inline
*
* Returns:
* {DOMElement} container holding the text label
*/
drawText: function(featureId, style, location) {
var g = OpenLayers.Renderer.NG.prototype.drawText.apply(this, arguments);
var text = g.firstChild ||
this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_text", "text");
var res = this.getResolution();
text.setAttributeNS(null, "x", location.x / res);
text.setAttributeNS(null, "y", - location.y / res);
g.setAttributeNS(null, "transform", "scale(" + res + ")");
if (style.fontColor) {
text.setAttributeNS(null, "fill", style.fontColor);
}
if (style.fontOpacity) {
text.setAttributeNS(null, "opacity", style.fontOpacity);
}
if (style.fontFamily) {
text.setAttributeNS(null, "font-family", style.fontFamily);
}
if (style.fontSize) {
text.setAttributeNS(null, "font-size", style.fontSize);
}
if (style.fontWeight) {
text.setAttributeNS(null, "font-weight", style.fontWeight);
}
if (style.fontStyle) {
text.setAttributeNS(null, "font-style", style.fontStyle);
}
if (style.labelSelect === true) {
text.setAttributeNS(null, "pointer-events", "visible");
text._featureId = featureId;
} else {
text.setAttributeNS(null, "pointer-events", "none");
}
var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign;
text.setAttributeNS(null, "text-anchor",
OpenLayers.Renderer.SVG2.LABEL_ALIGN[align[0]] || "middle");
if (OpenLayers.IS_GECKO === true) {
text.setAttributeNS(null, "dominant-baseline",
OpenLayers.Renderer.SVG2.LABEL_ALIGN[align[1]] || "central");
}
var labelRows = style.label.split('\n');
var numRows = labelRows.length;
while (text.childNodes.length > numRows) {
text.removeChild(text.lastChild);
}
for (var i = 0; i < numRows; i++) {
var tspan = text.childNodes[i] ||
this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan_" + i, "tspan");
if (style.labelSelect === true) {
tspan._featureId = featureId;
}
if (OpenLayers.IS_GECKO === false) {
tspan.setAttributeNS(null, "baseline-shift",
OpenLayers.Renderer.SVG2.LABEL_VSHIFT[align[1]] || "-35%");
}
tspan.setAttribute("x", location.x / res);
if (i == 0) {
var vfactor = OpenLayers.Renderer.SVG2.LABEL_VFACTOR[align[1]];
if (vfactor == null) {
vfactor = -.5;
}
tspan.setAttribute("dy", (vfactor*(numRows-1)) + "em");
} else {
tspan.setAttribute("dy", "1em");
}
tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i];
if (!tspan.parentNode) {
text.appendChild(tspan);
}
}
if (!text.parentNode) {
g.appendChild(text);
}
return g;
},
/**
* Method: getComponentString
*
* Parameters:
* components - {Array(<OpenLayers.Geometry.Point>)} Array of points
* separator - {String} character between coordinate pairs. Defaults to ","
*
* Returns:
* {Object} hash with properties "path" (the string created from the
* components and "complete" (false if the renderer was unable to
* draw all components)
*/
getComponentsString: function(components, separator) {
var len = components.length;
var strings = new Array(len);
for (var i=0; i<len; i++) {
strings[i] = this.getShortString(components[i]);
}
return strings.join(separator || ",");
},
/**
* Method: getShortString
*
* Parameters:
* point - {<OpenLayers.Geometry.Point>}
*
* Returns:
* {String} or false if point is outside the valid range
*/
getShortString: function(point) {
return point.x + "," + (-point.y);
},
/**
* Method: importSymbol
* add a new symbol definition from the rendererer's symbol hash
*
* Parameters:
* graphicName - {String} name of the symbol to import
*
* Returns:
* {DOMElement} - the imported symbol
*/
importSymbol: function (graphicName) {
if (!this.defs) {
// create svg defs tag
this.defs = this.createDefs();
}
var id = this.container.id + "-" + graphicName;
// check if symbol already exists in the defs
var existing = document.getElementById(id);
if (existing != null) {
return existing;
}
var symbol = OpenLayers.Renderer.symbol[graphicName];
if (!symbol) {
throw new Error(graphicName + ' is not a valid symbol name');
}
var symbolNode = this.nodeFactory(id, "symbol");
var node = this.nodeFactory(null, "polygon");
symbolNode.appendChild(node);
var symbolExtent = new OpenLayers.Bounds(
Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
var points = [];
var x,y;
for (var i=0, len=symbol.length; i<len; i=i+2) {
x = symbol[i];
y = symbol[i+1];
symbolExtent.left = Math.min(symbolExtent.left, x);
symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
symbolExtent.right = Math.max(symbolExtent.right, x);
symbolExtent.top = Math.max(symbolExtent.top, y);
points.push(x, ",", y);
}
node.setAttributeNS(null, "points", points.join(" "));
var width = symbolExtent.getWidth();
var height = symbolExtent.getHeight();
// create a viewBox three times as large as the symbol itself,
// to allow for strokeWidth being displayed correctly at the corners.
var viewBox = [symbolExtent.left - width,
symbolExtent.bottom - height, width * 3, height * 3];
symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" "));
this.symbolMetrics[id] = {
size: Math.max(width, height),
x: symbolExtent.getCenterLonLat().lon,
y: symbolExtent.getCenterLonLat().lat
};
this.defs.appendChild(symbolNode);
return symbolNode;
},
/**
* Method: getFeatureIdFromEvent
*
* Parameters:
* evt - {Object} An <OpenLayers.Event> object
*
* Returns:
* {String} A feature id or undefined.
*/
getFeatureIdFromEvent: function(evt) {
var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);
if(!featureId) {
var target = evt.target;
featureId = target.parentNode && target != this.rendererRoot ?
target.parentNode._featureId : undefined;
}
return featureId;
},
CLASS_NAME: "OpenLayers.Renderer.SVG2"
});
/**
* Constant: OpenLayers.Renderer.SVG2.LABEL_ALIGN
* {Object}
*/
OpenLayers.Renderer.SVG2.LABEL_ALIGN = {
"l": "start",
"r": "end",
"b": "bottom",
"t": "hanging"
};
/**
* Constant: OpenLayers.Renderer.SVG2.LABEL_VSHIFT
* {Object}
*/
OpenLayers.Renderer.SVG2.LABEL_VSHIFT = {
// according to
// http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html
// a baseline-shift of -70% shifts the text exactly from the
// bottom to the top of the baseline, so -35% moves the text to
// the center of the baseline.
"t": "-70%",
"b": "0"
};
/**
* Constant: OpenLayers.Renderer.SVG2.LABEL_VFACTOR
* {Object}
*/
OpenLayers.Renderer.SVG2.LABEL_VFACTOR = {
"t": 0,
"b": -1
};
/**
* Function: OpenLayers.Renderer.SVG2.preventDefault
* Used to prevent default events (especially opening images in a new tab on
* ctrl-click) from being executed for externalGraphic and graphicName symbols
*/
OpenLayers.Renderer.SVG2.preventDefault = function(e) {
e.preventDefault && e.preventDefault();
};
/**
* Class: OpenLayers.Popup.AnchoredBubble
* This class is *deprecated*. Use {<OpenLayers.Popup.Anchored>} and
* round corners using CSS3's border-radius property.
*
* Inherits from:
* - <OpenLayers.Popup.Anchored>
*/
OpenLayers.Popup.AnchoredBubble = OpenLayers.Class(OpenLayers.Popup.Anchored, {
/**
* Property: rounded
* {Boolean} Has the popup been rounded yet?
*/
rounded: false,
/**
* Constructor: OpenLayers.Popup.AnchoredBubble
*
* Parameters:
* id - {String}
* lonlat - {<OpenLayers.LonLat>}
* contentSize - {<OpenLayers.Size>}
* contentHTML - {String}
* anchor - {Object} Object to which we'll anchor the popup. Must expose
* a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
* (Note that this is generally an <OpenLayers.Icon>).
* closeBox - {Boolean}
* closeBoxCallback - {Function} Function to be called on closeBox click.
*/
initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
closeBoxCallback) {
this.padding = new OpenLayers.Bounds(
0, OpenLayers.Popup.AnchoredBubble.CORNER_SIZE,
0, OpenLayers.Popup.AnchoredBubble.CORNER_SIZE
);
OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
},
/**
* Method: draw
*
* Parameters:
* px - {<OpenLayers.Pixel>}
*
* Returns:
* {DOMElement} Reference to a div that contains the drawn popup.
*/
draw: function(px) {
OpenLayers.Popup.Anchored.prototype.draw.apply(this, arguments);
this.setContentHTML();
//set the popup color and opacity
this.setBackgroundColor();
this.setOpacity();
return this.div;
},
/**
* Method: updateRelativePosition
* The popup has been moved to a new relative location, in which case
* we will want to re-do the rico corners.
*/
updateRelativePosition: function() {
this.setRicoCorners();
},
/**
* APIMethod: setSize
*
* Parameters:
* contentSize - {<OpenLayers.Size>} the new size for the popup's
* contents div (in pixels).
*/
setSize:function(contentSize) {
OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
this.setRicoCorners();
},
/**
* APIMethod: setBackgroundColor
*
* Parameters:
* color - {String}
*/
setBackgroundColor:function(color) {
if (color != undefined) {
this.backgroundColor = color;
}
if (this.div != null) {
if (this.contentDiv != null) {
this.div.style.background = "transparent";
OpenLayers.Rico.Corner.changeColor(this.groupDiv,
this.backgroundColor);
}
}
},
/**
* APIMethod: setOpacity
*
* Parameters:
* opacity - {float}
*/
setOpacity:function(opacity) {
OpenLayers.Popup.Anchored.prototype.setOpacity.call(this, opacity);
if (this.div != null) {
if (this.groupDiv != null) {
OpenLayers.Rico.Corner.changeOpacity(this.groupDiv,
this.opacity);
}
}
},
/**
* Method: setBorder
* Always sets border to 0. Bubble Popups can not have a border.
*
* Parameters:
* border - {Integer}
*/
setBorder:function(border) {
this.border = 0;
},
/**
* Method: setRicoCorners
* Update RICO corners according to the popup's current relative postion.
*/
setRicoCorners:function() {
var corners = this.getCornersToRound(this.relativePosition);
var options = {corners: corners,
color: this.backgroundColor,
bgColor: "transparent",
blend: false};
if (!this.rounded) {
OpenLayers.Rico.Corner.round(this.div, options);
this.rounded = true;
} else {
OpenLayers.Rico.Corner.reRound(this.groupDiv, options);
//set the popup color and opacity
this.setBackgroundColor();
this.setOpacity();
}
},
/**
* Method: getCornersToRound
*
* Returns:
* {String} The proper corners string ("tr tl bl br") for rico to round.
*/
getCornersToRound:function() {
var corners = ['tl', 'tr', 'bl', 'br'];
//we want to round all the corners _except_ the opposite one.
var corner = OpenLayers.Bounds.oppositeQuadrant(this.relativePosition);
OpenLayers.Util.removeItem(corners, corner);
return corners.join(" ");
},
CLASS_NAME: "OpenLayers.Popup.AnchoredBubble"
});
/**
* Constant: CORNER_SIZE
* {Integer} 5. Border space for the RICO corners.
*/
OpenLayers.Popup.AnchoredBubble.CORNER_SIZE = 5;
|