Initial commit

This commit is contained in:
2022-11-21 09:47:28 +01:00
commit 76cec83d26
11652 changed files with 1980467 additions and 0 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 753 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 B

View File

@ -0,0 +1,209 @@
html, body {
margin: 0;
background: #FFFFFF;
font-family: Lucida Grande, Tahoma, sans-serif;
font-size: 11px;
overflow: hidden;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.toolbar {
height: 14px;
border-top: 1px solid ThreeDHighlight;
border-bottom: 1px solid ThreeDShadow;
padding: 2px 6px;
background: ThreeDFace;
}
.toolbarRight {
position: absolute;
top: 4px;
right: 6px;
}
#log {
overflow: auto;
position: absolute;
left: 0;
width: 100%;
}
#commandLine {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 18px;
border: none;
border-top: 1px solid ThreeDShadow;
}
/************************************************************************************************/
.logRow {
position: relative;
border-bottom: 1px solid #D7D7D7;
padding: 2px 4px 1px 6px;
background-color: #FFFFFF;
}
.logRow-command {
font-family: Monaco, monospace;
color: blue;
}
.objectBox-null {
padding: 0 2px;
border: 1px solid #666666;
background-color: #888888;
color: #FFFFFF;
}
.objectBox-string {
font-family: Monaco, monospace;
color: red;
white-space: pre;
}
.objectBox-number {
color: #000088;
}
.objectBox-function {
font-family: Monaco, monospace;
color: DarkGreen;
}
.objectBox-object {
color: DarkGreen;
font-weight: bold;
}
/************************************************************************************************/
.logRow-info,
.logRow-error,
.logRow-warning {
background: #FFFFFF no-repeat 2px 2px;
padding-left: 20px;
padding-bottom: 3px;
}
.logRow-info {
background-image: url(infoIcon.png);
}
.logRow-warning {
background-color: cyan;
background-image: url(warningIcon.png);
}
.logRow-error {
background-color: LightYellow;
background-image: url(errorIcon.png);
}
.errorMessage {
vertical-align: top;
color: #FF0000;
}
.objectBox-sourceLink {
position: absolute;
right: 4px;
top: 2px;
padding-left: 8px;
font-family: Lucida Grande, sans-serif;
font-weight: bold;
color: #0000FF;
}
/************************************************************************************************/
.logRow-group {
background: #EEEEEE;
border-bottom: none;
}
.logGroup {
background: #EEEEEE;
}
.logGroupBox {
margin-left: 24px;
border-top: 1px solid #D7D7D7;
border-left: 1px solid #D7D7D7;
}
/************************************************************************************************/
.selectorTag,
.selectorId,
.selectorClass {
font-family: Monaco, monospace;
font-weight: normal;
}
.selectorTag {
color: #0000FF;
}
.selectorId {
color: DarkBlue;
}
.selectorClass {
color: red;
}
/************************************************************************************************/
.objectBox-element {
font-family: Monaco, monospace;
color: #000088;
}
.nodeChildren {
margin-left: 16px;
}
.nodeTag {
color: blue;
}
.nodeValue {
color: #FF0000;
font-weight: normal;
}
.nodeText,
.nodeComment {
margin: 0 2px;
vertical-align: top;
}
.nodeText {
color: #333333;
}
.nodeComment {
color: DarkGreen;
}
/************************************************************************************************/
.propertyNameCell {
vertical-align: top;
}
.propertyName {
font-weight: bold;
}

View File

@ -0,0 +1,23 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Firebug</title>
<link rel="stylesheet" type="text/css" href="firebug.css">
</head>
<body>
<div id="toolbar" class="toolbar">
<a href="#" onclick="parent.console.clear()">Clear</a>
<span class="toolbarRight">
<a href="#" onclick="parent.console.close()">Close</a>
</span>
</div>
<div id="log"></div>
<input type="text" id="commandLine">
<script>parent.onFirebugReady(document);</script>
</body>
</html>

View File

@ -0,0 +1,674 @@
if (!window.console || !console.firebug) { (function()
{
window.console =
{
log: function()
{
logFormatted(arguments, "");
},
debug: function()
{
logFormatted(arguments, "debug");
},
info: function()
{
logFormatted(arguments, "info");
},
warn: function()
{
logFormatted(arguments, "warning");
},
error: function()
{
logFormatted(arguments, "error");
},
assert: function(truth, message)
{
if (!truth)
{
var args = [];
for (var i = 1; i < arguments.length; ++i)
args.push(arguments[i]);
logFormatted(args.length ? args : ["Assertion Failure"], "error");
throw message ? message : "Assertion Failure";
}
},
dir: function(object)
{
var html = [];
var pairs = [];
for (var name in object)
{
try
{
pairs.push([name, object[name]]);
}
catch (exc)
{
}
}
pairs.sort(function(a, b) { return a[0] < b[0] ? -1 : 1; });
html.push('<table>');
for (var i = 0; i < pairs.length; ++i)
{
var name = pairs[i][0], value = pairs[i][1];
html.push('<tr>',
'<td class="propertyNameCell"><span class="propertyName">',
escapeHTML(name), '</span></td>', '<td><span class="propertyValue">');
appendObject(value, html);
html.push('</span></td></tr>');
}
html.push('</table>');
logRow(html, "dir");
},
dirxml: function(node)
{
var html = [];
appendNode(node, html);
logRow(html, "dirxml");
},
group: function()
{
logRow(arguments, "group", pushGroup);
},
groupEnd: function()
{
logRow(arguments, "", popGroup);
},
time: function(name)
{
timeMap[name] = (new Date()).getTime();
},
timeEnd: function(name)
{
if (name in timeMap)
{
var delta = (new Date()).getTime() - timeMap[name];
logFormatted([name+ ":", delta+"ms"]);
delete timeMap[name];
}
},
count: function()
{
this.warn(["count() not supported."]);
},
trace: function()
{
this.warn(["trace() not supported."]);
},
profile: function()
{
this.warn(["profile() not supported."]);
},
profileEnd: function()
{
},
clear: function()
{
consoleBody.innerHTML = "";
},
open: function()
{
toggleConsole(true);
},
close: function()
{
if (frameVisible)
toggleConsole();
}
};
// ********************************************************************************************
var consoleFrame = null;
var consoleBody = null;
var commandLine = null;
var frameVisible = false;
var messageQueue = [];
var groupStack = [];
var timeMap = {};
var clPrefix = ">>> ";
var isFirefox = navigator.userAgent.indexOf("Firefox") != -1;
var isIE = navigator.userAgent.indexOf("MSIE") != -1;
var isOpera = navigator.userAgent.indexOf("Opera") != -1;
var isSafari = navigator.userAgent.indexOf("AppleWebKit") != -1;
// ********************************************************************************************
function toggleConsole(forceOpen)
{
frameVisible = forceOpen || !frameVisible;
if (consoleFrame)
consoleFrame.style.visibility = frameVisible ? "visible" : "hidden";
else
waitForBody();
}
function focusCommandLine()
{
toggleConsole(true);
if (commandLine)
commandLine.focus();
}
function waitForBody()
{
if (document.body)
createFrame();
else
setTimeout(waitForBody, 200);
}
function createFrame()
{
if (consoleFrame)
return;
window.onFirebugReady = function(doc)
{
window.onFirebugReady = null;
var toolbar = doc.getElementById("toolbar");
toolbar.onmousedown = onSplitterMouseDown;
commandLine = doc.getElementById("commandLine");
addEvent(commandLine, "keydown", onCommandLineKeyDown);
addEvent(doc, isIE || isSafari ? "keydown" : "keypress", onKeyDown);
consoleBody = doc.getElementById("log");
layout();
flush();
};
var baseURL = getFirebugURL();
consoleFrame = document.createElement("iframe");
consoleFrame.setAttribute("src", baseURL+"/firebug.html");
consoleFrame.setAttribute("frameBorder", "0");
consoleFrame.style.visibility = (frameVisible ? "visible" : "hidden");
consoleFrame.style.zIndex = "2147483583";
consoleFrame.style.position = document.all ? "absolute" : "fixed";
consoleFrame.style.width = "100%";
consoleFrame.style.left = "0";
consoleFrame.style.bottom = "0";
consoleFrame.style.height = "200px";
document.body.appendChild(consoleFrame);
}
function getFirebugURL()
{
var scripts = document.getElementsByTagName("script");
for (var i = 0; i < scripts.length; ++i)
{
if (scripts[i].src.indexOf("firebug.js") != -1)
{
var lastSlash = scripts[i].src.lastIndexOf("/");
return scripts[i].src.substr(0, lastSlash);
}
}
}
function evalCommandLine()
{
var text = commandLine.value;
commandLine.value = "";
logRow([clPrefix, text], "command");
var value;
try
{
value = eval(text);
}
catch (exc)
{
}
console.log(value);
}
function layout()
{
var toolbar = consoleBody.ownerDocument.getElementById("toolbar");
var height = consoleFrame.offsetHeight - (toolbar.offsetHeight + commandLine.offsetHeight);
height = Math.max(height, 0);
consoleBody.style.top = toolbar.offsetHeight + "px";
consoleBody.style.height = height + "px";
commandLine.style.top = (consoleFrame.offsetHeight - commandLine.offsetHeight) + "px";
}
function logRow(message, className, handler)
{
if (consoleBody)
writeMessage(message, className, handler);
else
{
messageQueue.push([message, className, handler]);
waitForBody();
}
}
function flush()
{
var queue = messageQueue;
messageQueue = [];
for (var i = 0; i < queue.length; ++i)
writeMessage(queue[i][0], queue[i][1], queue[i][2]);
}
function writeMessage(message, className, handler)
{
var isScrolledToBottom =
consoleBody.scrollTop + consoleBody.offsetHeight >= consoleBody.scrollHeight;
if (!handler)
handler = writeRow;
handler(message, className);
if (isScrolledToBottom)
consoleBody.scrollTop = consoleBody.scrollHeight - consoleBody.offsetHeight;
}
function appendRow(row)
{
var container = groupStack.length ? groupStack[groupStack.length-1] : consoleBody;
container.appendChild(row);
}
function writeRow(message, className)
{
var row = consoleBody.ownerDocument.createElement("div");
row.className = "logRow" + (className ? " logRow-"+className : "");
row.innerHTML = message.join("");
appendRow(row);
}
function pushGroup(message, className)
{
logFormatted(message, className);
var groupRow = consoleBody.ownerDocument.createElement("div");
groupRow.className = "logGroup";
var groupRowBox = consoleBody.ownerDocument.createElement("div");
groupRowBox.className = "logGroupBox";
groupRow.appendChild(groupRowBox);
appendRow(groupRowBox);
groupStack.push(groupRowBox);
}
function popGroup()
{
groupStack.pop();
}
// ********************************************************************************************
function logFormatted(objects, className)
{
var html = [];
var format = objects[0];
var objIndex = 0;
if (typeof(format) != "string")
{
format = "";
objIndex = -1;
}
var parts = parseFormat(format);
for (var i = 0; i < parts.length; ++i)
{
var part = parts[i];
if (part && typeof(part) == "object")
{
var object = objects[++objIndex];
part.appender(object, html);
}
else
appendText(part, html);
}
for (var i = objIndex+1; i < objects.length; ++i)
{
appendText(" ", html);
var object = objects[i];
if (typeof(object) == "string")
appendText(object, html);
else
appendObject(object, html);
}
logRow(html, className);
}
function parseFormat(format)
{
var parts = [];
var reg = /((^%|[^\\]%)(\d+)?(\.)([a-zA-Z]))|((^%|[^\\]%)([a-zA-Z]))/;
var appenderMap = {s: appendText, d: appendInteger, i: appendInteger, f: appendFloat};
for (var m = reg.exec(format); m; m = reg.exec(format))
{
var type = m[8] ? m[8] : m[5];
var appender = type in appenderMap ? appenderMap[type] : appendObject;
var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0);
parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1));
parts.push({appender: appender, precision: precision});
format = format.substr(m.index+m[0].length);
}
parts.push(format);
return parts;
}
function escapeHTML(value)
{
function replaceChars(ch)
{
switch (ch)
{
case "<":
return "&lt;";
case ">":
return "&gt;";
case "&":
return "&amp;";
case "'":
return "&#39;";
case '"':
return "&quot;";
}
return "?";
};
return String(value).replace(/[<>&"']/g, replaceChars);
}
function objectToString(object)
{
try
{
return object+"";
}
catch (exc)
{
return null;
}
}
// ********************************************************************************************
function appendText(object, html)
{
html.push(escapeHTML(objectToString(object)));
}
function appendNull(object, html)
{
html.push('<span class="objectBox-null">', escapeHTML(objectToString(object)), '</span>');
}
function appendString(object, html)
{
html.push('<span class="objectBox-string">&quot;', escapeHTML(objectToString(object)),
'&quot;</span>');
}
function appendInteger(object, html)
{
html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>');
}
function appendFloat(object, html)
{
html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>');
}
function appendFunction(object, html)
{
var reName = /function ?(.*?)\(/;
var m = reName.exec(objectToString(object));
var name = m ? m[1] : "function";
html.push('<span class="objectBox-function">', escapeHTML(name), '()</span>');
}
function appendObject(object, html)
{
try
{
if (object == undefined)
appendNull("undefined", html);
else if (object == null)
appendNull("null", html);
else if (typeof object == "string")
appendString(object, html);
else if (typeof object == "number")
appendInteger(object, html);
else if (typeof object == "function")
appendFunction(object, html);
else if (object.nodeType == 1)
appendSelector(object, html);
else if (typeof object == "object")
appendObjectFormatted(object, html);
else
appendText(object, html);
}
catch (exc)
{
}
}
function appendObjectFormatted(object, html)
{
var text = objectToString(object);
var reObject = /\[object (.*?)\]/;
var m = reObject.exec(text);
html.push('<span class="objectBox-object">', m ? m[1] : text, '</span>')
}
function appendSelector(object, html)
{
html.push('<span class="objectBox-selector">');
html.push('<span class="selectorTag">', escapeHTML(object.nodeName.toLowerCase()), '</span>');
if (object.id)
html.push('<span class="selectorId">#', escapeHTML(object.id), '</span>');
if (object.className)
html.push('<span class="selectorClass">.', escapeHTML(object.className), '</span>');
html.push('</span>');
}
function appendNode(node, html)
{
if (node.nodeType == 1)
{
html.push(
'<div class="objectBox-element">',
'&lt;<span class="nodeTag">', node.nodeName.toLowerCase(), '</span>');
for (var i = 0; i < node.attributes.length; ++i)
{
var attr = node.attributes[i];
if (!attr.specified)
continue;
html.push('&nbsp;<span class="nodeName">', attr.nodeName.toLowerCase(),
'</span>=&quot;<span class="nodeValue">', escapeHTML(attr.nodeValue),
'</span>&quot;')
}
if (node.firstChild)
{
html.push('&gt;</div><div class="nodeChildren">');
for (var child = node.firstChild; child; child = child.nextSibling)
appendNode(child, html);
html.push('</div><div class="objectBox-element">&lt;/<span class="nodeTag">',
node.nodeName.toLowerCase(), '&gt;</span></div>');
}
else
html.push('/&gt;</div>');
}
else if (node.nodeType == 3)
{
html.push('<div class="nodeText">', escapeHTML(node.nodeValue),
'</div>');
}
}
// ********************************************************************************************
function addEvent(object, name, handler)
{
if (document.all)
object.attachEvent("on"+name, handler);
else
object.addEventListener(name, handler, false);
}
function removeEvent(object, name, handler)
{
if (document.all)
object.detachEvent("on"+name, handler);
else
object.removeEventListener(name, handler, false);
}
function cancelEvent(event)
{
if (document.all)
event.cancelBubble = true;
else
event.stopPropagation();
}
function onError(msg, href, lineNo)
{
var html = [];
var lastSlash = href.lastIndexOf("/");
var fileName = lastSlash == -1 ? href : href.substr(lastSlash+1);
html.push(
'<span class="errorMessage">', msg, '</span>',
'<div class="objectBox-sourceLink">', fileName, ' (line ', lineNo, ')</div>'
);
logRow(html, "error");
};
function onKeyDown(event)
{
if (event.keyCode == 123)
toggleConsole();
else if ((event.keyCode == 108 || event.keyCode == 76) && event.shiftKey
&& (event.metaKey || event.ctrlKey))
focusCommandLine();
else
return;
cancelEvent(event);
}
function onSplitterMouseDown(event)
{
if (isSafari || isOpera)
return;
addEvent(document, "mousemove", onSplitterMouseMove);
addEvent(document, "mouseup", onSplitterMouseUp);
for (var i = 0; i < frames.length; ++i)
{
addEvent(frames[i].document, "mousemove", onSplitterMouseMove);
addEvent(frames[i].document, "mouseup", onSplitterMouseUp);
}
}
function onSplitterMouseMove(event)
{
var win = document.all
? event.srcElement.ownerDocument.parentWindow
: event.target.ownerDocument.defaultView;
var clientY = event.clientY;
if (win != win.parent)
clientY += win.frameElement ? win.frameElement.offsetTop : 0;
var height = consoleFrame.offsetTop + consoleFrame.clientHeight;
var toolbar = consoleBody.ownerDocument.getElementById("toolbar");
var y = Math.max(height - clientY,
toolbar.offsetHeight + commandLine.offsetHeight);
consoleFrame.style.height = y + "px";
layout();
}
function onSplitterMouseUp(event)
{
removeEvent(document, "mousemove", onSplitterMouseMove);
removeEvent(document, "mouseup", onSplitterMouseUp);
for (var i = 0; i < frames.length; ++i)
{
removeEvent(frames[i].document, "mousemove", onSplitterMouseMove);
removeEvent(frames[i].document, "mouseup", onSplitterMouseUp);
}
}
function onCommandLineKeyDown(event)
{
if (event.keyCode == 13)
evalCommandLine();
else if (event.keyCode == 27)
commandLine.value = "";
}
window.onerror = onError;
addEvent(document, isIE || isSafari ? "keydown" : "keypress", onKeyDown);
if (document.documentElement.getAttribute("debug") == "true")
toggleConsole(true);
})();
}

View File

@ -0,0 +1,10 @@
(function() {
if (!window.console || !console.firebug) {
var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
"group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
window.console = {};
for (var i = 0; i < names.length; ++i)
window.console[names[i]] = function() {}
}
})();

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

View File

@ -0,0 +1,30 @@
Software License Agreement (BSD License)
Copyright (c) 2007, Parakey Inc.
All rights reserved.
Redistribution and use of this software in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* 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.
* Neither the name of Parakey Inc. nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of Parakey Inc.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR
CONTRIBUTORS 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.

View File

@ -0,0 +1,13 @@
This directory contains the source for Firebug Lite
(http://www.getfirebug.com/lite.html). This code is distributed with a
BSD License, Copyright (c) 2007, Parakey Inc. See the included license.txt
for the full text of the license.
This is a patched version of the trunk from
http://fbug.googlecode.com/svn/trunk.
Revision 36 was patched to resolve the issue described here
http://code.google.com/p/fbug/issues/detail?id=85
When this issue is resolved, Firebug Lite can be used directly - no further
modifications are needed for OpenLayers.

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

View File

@ -0,0 +1,429 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/*
* @requires OpenLayers/BaseTypes.js
* @requires OpenLayers/Lang/en.js
* @requires OpenLayers/Console.js
*/
/*
* TODO: In 3.0, we will stop supporting build profiles that include
* OpenLayers.js. This means we will not need the singleFile and scriptFile
* variables, because we don't have to handle the singleFile case any more.
*/
(function() {
/**
* Before creating the OpenLayers namespace, check to see if
* OpenLayers.singleFile is true. This occurs if the
* OpenLayers/SingleFile.js script is included before this one - as is the
* case with old single file build profiles that included both
* OpenLayers.js and OpenLayers/SingleFile.js.
*/
var singleFile = (typeof OpenLayers == "object" && OpenLayers.singleFile);
/**
* Relative path of this script.
*/
var scriptName = (!singleFile) ? "lib/OpenLayers.js" : "OpenLayers.js";
/*
* If window.OpenLayers isn't set when this script (OpenLayers.js) is
* evaluated (and if singleFile is false) then this script will load
* *all* OpenLayers scripts. If window.OpenLayers is set to an array
* then this script will attempt to load scripts for each string of
* the array, using the string as the src of the script.
*
* Example:
* (code)
* <script type="text/javascript">
* window.OpenLayers = [
* "OpenLayers/Util.js",
* "OpenLayers/BaseTypes.js"
* ];
* </script>
* <script type="text/javascript" src="../lib/OpenLayers.js"></script>
* (end)
* In this example OpenLayers.js will load Util.js and BaseTypes.js only.
*/
var jsFiles = window.OpenLayers;
/**
* Namespace: OpenLayers
* The OpenLayers object provides a namespace for all things OpenLayers
*/
window.OpenLayers = {
/**
* Method: _getScriptLocation
* Return the path to this script. This is also implemented in
* OpenLayers/SingleFile.js
*
* Returns:
* {String} Path to this script
*/
_getScriptLocation: (function() {
var r = new RegExp("(^|(.*?\\/))(" + scriptName + ")(\\?|$)"),
s = document.getElementsByTagName('script'),
src, m, l = "";
for(var i=0, len=s.length; i<len; i++) {
src = s[i].getAttribute('src');
if(src) {
m = src.match(r);
if(m) {
l = m[1];
break;
}
}
}
return (function() { return l; });
})(),
/**
* APIProperty: ImgPath
* {String} Set this to the path where control images are stored, a path
* given here must end with a slash. If set to '' (which is the default)
* OpenLayers will use its script location + "img/".
*
* You will need to set this property when you have a singlefile build of
* OpenLayers that either is not named "OpenLayers.js" or if you move
* the file in a way such that the image directory cannot be derived from
* the script location.
*
* If your custom OpenLayers build is named "my-custom-ol.js" and the images
* of OpenLayers are in a folder "/resources/external/images/ol" a correct
* way of including OpenLayers in your HTML would be:
*
* (code)
* <script src="/path/to/my-custom-ol.js" type="text/javascript"></script>
* <script type="text/javascript">
* // tell OpenLayers where the control images are
* // remember the trailing slash
* OpenLayers.ImgPath = "/resources/external/images/ol/";
* </script>
* (end code)
*
* Please remember that when your OpenLayers script is not named
* "OpenLayers.js" you will have to make sure that the default theme is
* loaded into the page by including an appropriate <link>-tag,
* e.g.:
*
* (code)
* <link rel="stylesheet" href="/path/to/default/style.css" type="text/css">
* (end code)
*/
ImgPath : ''
};
/**
* OpenLayers.singleFile is a flag indicating this file is being included
* in a Single File Library build of the OpenLayers Library.
*
* When we are *not* part of a SFL build we dynamically include the
* OpenLayers library code.
*
* When we *are* part of a SFL build we do not dynamically include the
* OpenLayers library code as it will be appended at the end of this file.
*/
if(!singleFile) {
if (!jsFiles) {
jsFiles = [
"OpenLayers/BaseTypes/Class.js",
"OpenLayers/Util.js",
"OpenLayers/Util/vendorPrefix.js",
"OpenLayers/Animation.js",
"OpenLayers/BaseTypes.js",
"OpenLayers/BaseTypes/Bounds.js",
"OpenLayers/BaseTypes/Date.js",
"OpenLayers/BaseTypes/Element.js",
"OpenLayers/BaseTypes/LonLat.js",
"OpenLayers/BaseTypes/Pixel.js",
"OpenLayers/BaseTypes/Size.js",
"OpenLayers/Console.js",
"OpenLayers/Tween.js",
"OpenLayers/Kinetic.js",
"OpenLayers/Events.js",
"OpenLayers/Events/buttonclick.js",
"OpenLayers/Events/featureclick.js",
"OpenLayers/Request.js",
"OpenLayers/Request/XMLHttpRequest.js",
"OpenLayers/Projection.js",
"OpenLayers/Map.js",
"OpenLayers/Layer.js",
"OpenLayers/Icon.js",
"OpenLayers/Marker.js",
"OpenLayers/Marker/Box.js",
"OpenLayers/Popup.js",
"OpenLayers/Tile.js",
"OpenLayers/Tile/Image.js",
"OpenLayers/Tile/Image/IFrame.js",
"OpenLayers/Tile/UTFGrid.js",
"OpenLayers/Layer/Image.js",
"OpenLayers/Layer/SphericalMercator.js",
"OpenLayers/Layer/EventPane.js",
"OpenLayers/Layer/FixedZoomLevels.js",
"OpenLayers/Layer/Google.js",
"OpenLayers/Layer/Google/v3.js",
"OpenLayers/Layer/HTTPRequest.js",
"OpenLayers/Layer/Grid.js",
"OpenLayers/Layer/MapGuide.js",
"OpenLayers/Layer/MapServer.js",
"OpenLayers/Layer/KaMap.js",
"OpenLayers/Layer/KaMapCache.js",
"OpenLayers/Layer/Markers.js",
"OpenLayers/Layer/Text.js",
"OpenLayers/Layer/WorldWind.js",
"OpenLayers/Layer/ArcGIS93Rest.js",
"OpenLayers/Layer/WMS.js",
"OpenLayers/Layer/WMTS.js",
"OpenLayers/Layer/ArcIMS.js",
"OpenLayers/Layer/GeoRSS.js",
"OpenLayers/Layer/Boxes.js",
"OpenLayers/Layer/XYZ.js",
"OpenLayers/Layer/UTFGrid.js",
"OpenLayers/Layer/OSM.js",
"OpenLayers/Layer/Bing.js",
"OpenLayers/Layer/TMS.js",
"OpenLayers/Layer/TileCache.js",
"OpenLayers/Layer/Zoomify.js",
"OpenLayers/Layer/ArcGISCache.js",
"OpenLayers/Popup/Anchored.js",
"OpenLayers/Popup/Framed.js",
"OpenLayers/Popup/FramedCloud.js",
"OpenLayers/Feature.js",
"OpenLayers/Feature/Vector.js",
"OpenLayers/Handler.js",
"OpenLayers/Handler/Click.js",
"OpenLayers/Handler/Hover.js",
"OpenLayers/Handler/Point.js",
"OpenLayers/Handler/Path.js",
"OpenLayers/Handler/Polygon.js",
"OpenLayers/Handler/Feature.js",
"OpenLayers/Handler/Drag.js",
"OpenLayers/Handler/Pinch.js",
"OpenLayers/Handler/RegularPolygon.js",
"OpenLayers/Handler/Box.js",
"OpenLayers/Handler/MouseWheel.js",
"OpenLayers/Handler/Keyboard.js",
"OpenLayers/Control.js",
"OpenLayers/Control/Attribution.js",
"OpenLayers/Control/Button.js",
"OpenLayers/Control/CacheRead.js",
"OpenLayers/Control/CacheWrite.js",
"OpenLayers/Control/ZoomBox.js",
"OpenLayers/Control/ZoomToMaxExtent.js",
"OpenLayers/Control/DragPan.js",
"OpenLayers/Control/Navigation.js",
"OpenLayers/Control/PinchZoom.js",
"OpenLayers/Control/TouchNavigation.js",
"OpenLayers/Control/MousePosition.js",
"OpenLayers/Control/OverviewMap.js",
"OpenLayers/Control/KeyboardDefaults.js",
"OpenLayers/Control/PanZoom.js",
"OpenLayers/Control/PanZoomBar.js",
"OpenLayers/Control/ArgParser.js",
"OpenLayers/Control/Permalink.js",
"OpenLayers/Control/Scale.js",
"OpenLayers/Control/ScaleLine.js",
"OpenLayers/Control/Snapping.js",
"OpenLayers/Control/Split.js",
"OpenLayers/Control/LayerSwitcher.js",
"OpenLayers/Control/DrawFeature.js",
"OpenLayers/Control/DragFeature.js",
"OpenLayers/Control/ModifyFeature.js",
"OpenLayers/Control/Panel.js",
"OpenLayers/Control/SelectFeature.js",
"OpenLayers/Control/NavigationHistory.js",
"OpenLayers/Control/Measure.js",
"OpenLayers/Control/WMSGetFeatureInfo.js",
"OpenLayers/Control/WMTSGetFeatureInfo.js",
"OpenLayers/Control/Graticule.js",
"OpenLayers/Control/TransformFeature.js",
"OpenLayers/Control/UTFGrid.js",
"OpenLayers/Control/SLDSelect.js",
"OpenLayers/Control/Zoom.js",
"OpenLayers/Geometry.js",
"OpenLayers/Geometry/Collection.js",
"OpenLayers/Geometry/Point.js",
"OpenLayers/Geometry/MultiPoint.js",
"OpenLayers/Geometry/Curve.js",
"OpenLayers/Geometry/LineString.js",
"OpenLayers/Geometry/LinearRing.js",
"OpenLayers/Geometry/Polygon.js",
"OpenLayers/Geometry/MultiLineString.js",
"OpenLayers/Geometry/MultiPolygon.js",
"OpenLayers/Renderer.js",
"OpenLayers/Renderer/Elements.js",
"OpenLayers/Renderer/SVG.js",
"OpenLayers/Renderer/Canvas.js",
"OpenLayers/Renderer/VML.js",
"OpenLayers/Layer/Vector.js",
"OpenLayers/Layer/PointGrid.js",
"OpenLayers/Layer/Vector/RootContainer.js",
"OpenLayers/Strategy.js",
"OpenLayers/Strategy/Filter.js",
"OpenLayers/Strategy/Fixed.js",
"OpenLayers/Strategy/Cluster.js",
"OpenLayers/Strategy/Paging.js",
"OpenLayers/Strategy/BBOX.js",
"OpenLayers/Strategy/Save.js",
"OpenLayers/Strategy/Refresh.js",
"OpenLayers/Filter.js",
"OpenLayers/Filter/FeatureId.js",
"OpenLayers/Filter/Logical.js",
"OpenLayers/Filter/Comparison.js",
"OpenLayers/Filter/Spatial.js",
"OpenLayers/Filter/Function.js",
"OpenLayers/Protocol.js",
"OpenLayers/Protocol/HTTP.js",
"OpenLayers/Protocol/WFS.js",
"OpenLayers/Protocol/WFS/v1.js",
"OpenLayers/Protocol/WFS/v1_0_0.js",
"OpenLayers/Protocol/WFS/v1_1_0.js",
"OpenLayers/Protocol/CSW.js",
"OpenLayers/Protocol/CSW/v2_0_2.js",
"OpenLayers/Protocol/Script.js",
"OpenLayers/Protocol/SOS.js",
"OpenLayers/Protocol/SOS/v1_0_0.js",
"OpenLayers/Layer/PointTrack.js",
"OpenLayers/Style.js",
"OpenLayers/Style2.js",
"OpenLayers/StyleMap.js",
"OpenLayers/Rule.js",
"OpenLayers/Format.js",
"OpenLayers/Format/QueryStringFilter.js",
"OpenLayers/Format/XML.js",
"OpenLayers/Format/XML/VersionedOGC.js",
"OpenLayers/Format/Context.js",
"OpenLayers/Format/ArcXML.js",
"OpenLayers/Format/ArcXML/Features.js",
"OpenLayers/Format/GML.js",
"OpenLayers/Format/GML/Base.js",
"OpenLayers/Format/GML/v2.js",
"OpenLayers/Format/GML/v3.js",
"OpenLayers/Format/Atom.js",
"OpenLayers/Format/EncodedPolyline.js",
"OpenLayers/Format/KML.js",
"OpenLayers/Format/GeoRSS.js",
"OpenLayers/Format/WFS.js",
"OpenLayers/Format/OWSCommon.js",
"OpenLayers/Format/OWSCommon/v1.js",
"OpenLayers/Format/OWSCommon/v1_0_0.js",
"OpenLayers/Format/OWSCommon/v1_1_0.js",
"OpenLayers/Format/WCSCapabilities.js",
"OpenLayers/Format/WCSCapabilities/v1.js",
"OpenLayers/Format/WCSCapabilities/v1_0_0.js",
"OpenLayers/Format/WCSCapabilities/v1_1_0.js",
"OpenLayers/Format/WFSCapabilities.js",
"OpenLayers/Format/WFSCapabilities/v1.js",
"OpenLayers/Format/WFSCapabilities/v1_0_0.js",
"OpenLayers/Format/WFSCapabilities/v1_1_0.js",
"OpenLayers/Format/WFSDescribeFeatureType.js",
"OpenLayers/Format/WMSDescribeLayer.js",
"OpenLayers/Format/WMSDescribeLayer/v1_1.js",
"OpenLayers/Format/WKT.js",
"OpenLayers/Format/CQL.js",
"OpenLayers/Format/OSM.js",
"OpenLayers/Format/GPX.js",
"OpenLayers/Format/Filter.js",
"OpenLayers/Format/Filter/v1.js",
"OpenLayers/Format/Filter/v1_0_0.js",
"OpenLayers/Format/Filter/v1_1_0.js",
"OpenLayers/Format/SLD.js",
"OpenLayers/Format/SLD/v1.js",
"OpenLayers/Format/SLD/v1_0_0.js",
"OpenLayers/Format/SLD/v1_0_0_GeoServer.js",
"OpenLayers/Format/OWSCommon.js",
"OpenLayers/Format/OWSCommon/v1.js",
"OpenLayers/Format/OWSCommon/v1_0_0.js",
"OpenLayers/Format/OWSCommon/v1_1_0.js",
"OpenLayers/Format/CSWGetDomain.js",
"OpenLayers/Format/CSWGetDomain/v2_0_2.js",
"OpenLayers/Format/CSWGetRecords.js",
"OpenLayers/Format/CSWGetRecords/v2_0_2.js",
"OpenLayers/Format/WFST.js",
"OpenLayers/Format/WFST/v1.js",
"OpenLayers/Format/WFST/v1_0_0.js",
"OpenLayers/Format/WFST/v1_1_0.js",
"OpenLayers/Format/Text.js",
"OpenLayers/Format/JSON.js",
"OpenLayers/Format/GeoJSON.js",
"OpenLayers/Format/WMC.js",
"OpenLayers/Format/WMC/v1.js",
"OpenLayers/Format/WMC/v1_0_0.js",
"OpenLayers/Format/WMC/v1_1_0.js",
"OpenLayers/Format/WCSGetCoverage.js",
"OpenLayers/Format/WMSCapabilities.js",
"OpenLayers/Format/WMSCapabilities/v1.js",
"OpenLayers/Format/WMSCapabilities/v1_1.js",
"OpenLayers/Format/WMSCapabilities/v1_1_0.js",
"OpenLayers/Format/WMSCapabilities/v1_1_1.js",
"OpenLayers/Format/WMSCapabilities/v1_3.js",
"OpenLayers/Format/WMSCapabilities/v1_3_0.js",
"OpenLayers/Format/WMSCapabilities/v1_1_1_WMSC.js",
"OpenLayers/Format/WMSGetFeatureInfo.js",
"OpenLayers/Format/SOSCapabilities.js",
"OpenLayers/Format/SOSCapabilities/v1_0_0.js",
"OpenLayers/Format/SOSGetFeatureOfInterest.js",
"OpenLayers/Format/SOSGetObservation.js",
"OpenLayers/Format/OWSContext.js",
"OpenLayers/Format/OWSContext/v0_3_1.js",
"OpenLayers/Format/WMTSCapabilities.js",
"OpenLayers/Format/WMTSCapabilities/v1_0_0.js",
"OpenLayers/Format/WPSCapabilities.js",
"OpenLayers/Format/WPSCapabilities/v1_0_0.js",
"OpenLayers/Format/WPSDescribeProcess.js",
"OpenLayers/Format/WPSExecute.js",
"OpenLayers/Format/XLS.js",
"OpenLayers/Format/XLS/v1.js",
"OpenLayers/Format/XLS/v1_1_0.js",
"OpenLayers/Format/OGCExceptionReport.js",
"OpenLayers/Control/GetFeature.js",
"OpenLayers/Control/NavToolbar.js",
"OpenLayers/Control/PanPanel.js",
"OpenLayers/Control/Pan.js",
"OpenLayers/Control/ZoomIn.js",
"OpenLayers/Control/ZoomOut.js",
"OpenLayers/Control/ZoomPanel.js",
"OpenLayers/Control/EditingToolbar.js",
"OpenLayers/Control/Geolocate.js",
"OpenLayers/Symbolizer.js",
"OpenLayers/Symbolizer/Point.js",
"OpenLayers/Symbolizer/Line.js",
"OpenLayers/Symbolizer/Polygon.js",
"OpenLayers/Symbolizer/Text.js",
"OpenLayers/Symbolizer/Raster.js",
"OpenLayers/Lang.js",
"OpenLayers/Lang/en.js",
"OpenLayers/Spherical.js",
"OpenLayers/TileManager.js",
"OpenLayers/WPSClient.js",
"OpenLayers/WPSProcess.js"
]; // etc.
}
// use "parser-inserted scripts" for guaranteed execution order
// http://hsivonen.iki.fi/script-execution/
var scriptTags = new Array(jsFiles.length);
var host = OpenLayers._getScriptLocation() + "lib/";
for (var i=0, len=jsFiles.length; i<len; i++) {
scriptTags[i] = "<script src='" + host + jsFiles[i] +
"'></script>";
}
if (scriptTags.length > 0) {
document.write(scriptTags.join(""));
}
}
})();
/**
* Constant: VERSION_NUMBER
*
* This constant identifies the version of OpenLayers.
*
* When asking questions or reporting issues, make sure to include the output of
* OpenLayers.VERSION_NUMBER in the question or issue-description.
*/
OpenLayers.VERSION_NUMBER="Release 2.13.1";

View File

@ -0,0 +1,102 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/SingleFile.js
* @requires OpenLayers/Util/vendorPrefix.js
*/
/**
* Namespace: OpenLayers.Animation
* A collection of utility functions for executing methods that repaint a
* portion of the browser window. These methods take advantage of the
* browser's scheduled repaints where requestAnimationFrame is available.
*/
OpenLayers.Animation = (function(window) {
/**
* Property: isNative
* {Boolean} true if a native requestAnimationFrame function is available
*/
var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, "requestAnimationFrame");
var isNative = !!(requestAnimationFrame);
/**
* Function: requestFrame
* Schedule a function to be called at the next available animation frame.
* Uses the native method where available. Where requestAnimationFrame is
* not available, setTimeout will be called with a 16ms delay.
*
* Parameters:
* callback - {Function} The function to be called at the next animation frame.
* element - {DOMElement} Optional element that visually bounds the animation.
*/
var requestFrame = (function() {
var request = window[requestAnimationFrame] ||
function(callback, element) {
window.setTimeout(callback, 16);
};
// bind to window to avoid illegal invocation of native function
return function(callback, element) {
request.apply(window, [callback, element]);
};
})();
// private variables for animation loops
var counter = 0;
var loops = {};
/**
* Function: start
* Executes a method with <requestFrame> in series for some
* duration.
*
* Parameters:
* callback - {Function} The function to be called at the next animation frame.
* duration - {Number} Optional duration for the loop. If not provided, the
* animation loop will execute indefinitely.
* element - {DOMElement} Optional element that visually bounds the animation.
*
* Returns:
* {Number} Identifier for the animation loop. Used to stop animations with
* <stop>.
*/
function start(callback, duration, element) {
duration = duration > 0 ? duration : Number.POSITIVE_INFINITY;
var id = ++counter;
var start = +new Date;
loops[id] = function() {
if (loops[id] && +new Date - start <= duration) {
callback();
if (loops[id]) {
requestFrame(loops[id], element);
}
} else {
delete loops[id];
}
};
requestFrame(loops[id], element);
return id;
}
/**
* Function: stop
* Terminates an animation loop started with <start>.
*
* Parameters:
* id - {Number} Identifier returned from <start>.
*/
function stop(id) {
delete loops[id];
}
return {
isNative: isNative,
requestFrame: requestFrame,
start: start,
stop: stop
};
})(window);

View File

@ -0,0 +1,463 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/SingleFile.js
*/
/**
* Header: OpenLayers Base Types
* OpenLayers custom string, number and function functions are described here.
*/
/**
* Namespace: OpenLayers.String
* Contains convenience functions for string manipulation.
*/
OpenLayers.String = {
/**
* APIFunction: startsWith
* Test whether a string starts with another string.
*
* Parameters:
* str - {String} The string to test.
* sub - {String} The substring to look for.
*
* Returns:
* {Boolean} The first string starts with the second.
*/
startsWith: function(str, sub) {
return (str.indexOf(sub) == 0);
},
/**
* APIFunction: contains
* Test whether a string contains another string.
*
* Parameters:
* str - {String} The string to test.
* sub - {String} The substring to look for.
*
* Returns:
* {Boolean} The first string contains the second.
*/
contains: function(str, sub) {
return (str.indexOf(sub) != -1);
},
/**
* APIFunction: trim
* Removes leading and trailing whitespace characters from a string.
*
* Parameters:
* str - {String} The (potentially) space padded string. This string is not
* modified.
*
* Returns:
* {String} A trimmed version of the string with all leading and
* trailing spaces removed.
*/
trim: function(str) {
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
},
/**
* APIFunction: camelize
* Camel-case a hyphenated string.
* Ex. "chicken-head" becomes "chickenHead", and
* "-chicken-head" becomes "ChickenHead".
*
* Parameters:
* str - {String} The string to be camelized. The original is not modified.
*
* Returns:
* {String} The string, camelized
*/
camelize: function(str) {
var oStringList = str.split('-');
var camelizedString = oStringList[0];
for (var i=1, len=oStringList.length; i<len; i++) {
var s = oStringList[i];
camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
}
return camelizedString;
},
/**
* APIFunction: format
* Given a string with tokens in the form ${token}, return a string
* with tokens replaced with properties from the given context
* object. Represent a literal "${" by doubling it, e.g. "${${".
*
* Parameters:
* template - {String} A string with tokens to be replaced. A template
* has the form "literal ${token}" where the token will be replaced
* by the value of context["token"].
* context - {Object} An optional object with properties corresponding
* to the tokens in the format string. If no context is sent, the
* window object will be used.
* args - {Array} Optional arguments to pass to any functions found in
* the context. If a context property is a function, the token
* will be replaced by the return from the function called with
* these arguments.
*
* Returns:
* {String} A string with tokens replaced from the context object.
*/
format: function(template, context, args) {
if(!context) {
context = window;
}
// Example matching:
// str = ${foo.bar}
// match = foo.bar
var replacer = function(str, match) {
var replacement;
// Loop through all subs. Example: ${a.b.c}
// 0 -> replacement = context[a];
// 1 -> replacement = context[a][b];
// 2 -> replacement = context[a][b][c];
var subs = match.split(/\.+/);
for (var i=0; i< subs.length; i++) {
if (i == 0) {
replacement = context;
}
if (replacement === undefined) {
break;
}
replacement = replacement[subs[i]];
}
if(typeof replacement == "function") {
replacement = args ?
replacement.apply(null, args) :
replacement();
}
// If replacement is undefined, return the string 'undefined'.
// This is a workaround for a bugs in browsers not properly
// dealing with non-participating groups in regular expressions:
// http://blog.stevenlevithan.com/archives/npcg-javascript
if (typeof replacement == 'undefined') {
return 'undefined';
} else {
return replacement;
}
};
return template.replace(OpenLayers.String.tokenRegEx, replacer);
},
/**
* Property: tokenRegEx
* Used to find tokens in a string.
* Examples: ${a}, ${a.b.c}, ${a-b}, ${5}
*/
tokenRegEx: /\$\{([\w.]+?)\}/g,
/**
* Property: numberRegEx
* Used to test strings as numbers.
*/
numberRegEx: /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/,
/**
* APIFunction: isNumeric
* Determine whether a string contains only a numeric value.
*
* Examples:
* (code)
* OpenLayers.String.isNumeric("6.02e23") // true
* OpenLayers.String.isNumeric("12 dozen") // false
* OpenLayers.String.isNumeric("4") // true
* OpenLayers.String.isNumeric(" 4 ") // false
* (end)
*
* Returns:
* {Boolean} String contains only a number.
*/
isNumeric: function(value) {
return OpenLayers.String.numberRegEx.test(value);
},
/**
* APIFunction: numericIf
* Converts a string that appears to be a numeric value into a number.
*
* Parameters:
* value - {String}
* trimWhitespace - {Boolean}
*
* Returns:
* {Number|String} a Number if the passed value is a number, a String
* otherwise.
*/
numericIf: function(value, trimWhitespace) {
var originalValue = value;
if (trimWhitespace === true && value != null && value.replace) {
value = value.replace(/^\s*|\s*$/g, "");
}
return OpenLayers.String.isNumeric(value) ? parseFloat(value) : originalValue;
}
};
/**
* Namespace: OpenLayers.Number
* Contains convenience functions for manipulating numbers.
*/
OpenLayers.Number = {
/**
* Property: decimalSeparator
* Decimal separator to use when formatting numbers.
*/
decimalSeparator: ".",
/**
* Property: thousandsSeparator
* Thousands separator to use when formatting numbers.
*/
thousandsSeparator: ",",
/**
* APIFunction: limitSigDigs
* Limit the number of significant digits on a float.
*
* Parameters:
* num - {Float}
* sig - {Integer}
*
* Returns:
* {Float} The number, rounded to the specified number of significant
* digits.
*/
limitSigDigs: function(num, sig) {
var fig = 0;
if (sig > 0) {
fig = parseFloat(num.toPrecision(sig));
}
return fig;
},
/**
* APIFunction: format
* Formats a number for output.
*
* Parameters:
* num - {Float}
* dec - {Integer} Number of decimal places to round to.
* Defaults to 0. Set to null to leave decimal places unchanged.
* tsep - {String} Thousands separator.
* Default is ",".
* dsep - {String} Decimal separator.
* Default is ".".
*
* Returns:
* {String} A string representing the formatted number.
*/
format: function(num, dec, tsep, dsep) {
dec = (typeof dec != "undefined") ? dec : 0;
tsep = (typeof tsep != "undefined") ? tsep :
OpenLayers.Number.thousandsSeparator;
dsep = (typeof dsep != "undefined") ? dsep :
OpenLayers.Number.decimalSeparator;
if (dec != null) {
num = parseFloat(num.toFixed(dec));
}
var parts = num.toString().split(".");
if (parts.length == 1 && dec == null) {
// integer where we do not want to touch the decimals
dec = 0;
}
var integer = parts[0];
if (tsep) {
var thousands = /(-?[0-9]+)([0-9]{3})/;
while(thousands.test(integer)) {
integer = integer.replace(thousands, "$1" + tsep + "$2");
}
}
var str;
if (dec == 0) {
str = integer;
} else {
var rem = parts.length > 1 ? parts[1] : "0";
if (dec != null) {
rem = rem + new Array(dec - rem.length + 1).join("0");
}
str = integer + dsep + rem;
}
return str;
},
/**
* Method: zeroPad
* Create a zero padded string optionally with a radix for casting numbers.
*
* Parameters:
* num - {Number} The number to be zero padded.
* len - {Number} The length of the string to be returned.
* radix - {Number} An integer between 2 and 36 specifying the base to use
* for representing numeric values.
*/
zeroPad: function(num, len, radix) {
var str = num.toString(radix || 10);
while (str.length < len) {
str = "0" + str;
}
return str;
}
};
/**
* Namespace: OpenLayers.Function
* Contains convenience functions for function manipulation.
*/
OpenLayers.Function = {
/**
* APIFunction: bind
* Bind a function to an object. Method to easily create closures with
* 'this' altered.
*
* Parameters:
* func - {Function} Input function.
* object - {Object} The object to bind to the input function (as this).
*
* Returns:
* {Function} A closure with 'this' set to the passed in object.
*/
bind: function(func, object) {
// create a reference to all arguments past the second one
var args = Array.prototype.slice.apply(arguments, [2]);
return function() {
// Push on any additional arguments from the actual function call.
// These will come after those sent to the bind call.
var newArgs = args.concat(
Array.prototype.slice.apply(arguments, [0])
);
return func.apply(object, newArgs);
};
},
/**
* APIFunction: bindAsEventListener
* Bind a function to an object, and configure it to receive the event
* object as first parameter when called.
*
* Parameters:
* func - {Function} Input function to serve as an event listener.
* object - {Object} A reference to this.
*
* Returns:
* {Function}
*/
bindAsEventListener: function(func, object) {
return function(event) {
return func.call(object, event || window.event);
};
},
/**
* APIFunction: False
* A simple function to that just does "return false". We use this to
* avoid attaching anonymous functions to DOM event handlers, which
* causes "issues" on IE<8.
*
* Usage:
* document.onclick = OpenLayers.Function.False;
*
* Returns:
* {Boolean}
*/
False : function() {
return false;
},
/**
* APIFunction: True
* A simple function to that just does "return true". We use this to
* avoid attaching anonymous functions to DOM event handlers, which
* causes "issues" on IE<8.
*
* Usage:
* document.onclick = OpenLayers.Function.True;
*
* Returns:
* {Boolean}
*/
True : function() {
return true;
},
/**
* APIFunction: Void
* A reusable function that returns ``undefined``.
*
* Returns:
* {undefined}
*/
Void: function() {}
};
/**
* Namespace: OpenLayers.Array
* Contains convenience functions for array manipulation.
*/
OpenLayers.Array = {
/**
* APIMethod: filter
* Filter an array. Provides the functionality of the
* Array.prototype.filter extension to the ECMA-262 standard. Where
* available, Array.prototype.filter will be used.
*
* Based on well known example from http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/filter
*
* Parameters:
* array - {Array} The array to be filtered. This array is not mutated.
* Elements added to this array by the callback will not be visited.
* callback - {Function} A function that is called for each element in
* the array. If this function returns true, the element will be
* included in the return. The function will be called with three
* arguments: the element in the array, the index of that element, and
* the array itself. If the optional caller parameter is specified
* the callback will be called with this set to caller.
* caller - {Object} Optional object to be set as this when the callback
* is called.
*
* Returns:
* {Array} An array of elements from the passed in array for which the
* callback returns true.
*/
filter: function(array, callback, caller) {
var selected = [];
if (Array.prototype.filter) {
selected = array.filter(callback, caller);
} else {
var len = array.length;
if (typeof callback != "function") {
throw new TypeError();
}
for(var i=0; i<len; i++) {
if (i in array) {
var val = array[i];
if (callback.call(caller, val, i, array)) {
selected.push(val);
}
}
}
}
return selected;
}
};

View File

@ -0,0 +1,837 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/BaseTypes/Class.js
*/
/**
* Class: OpenLayers.Bounds
* Instances of this class represent bounding boxes. Data stored as left,
* bottom, right, top floats. All values are initialized to null, however,
* you should make sure you set them before using the bounds for anything.
*
* Possible use case:
* (code)
* bounds = new OpenLayers.Bounds();
* bounds.extend(new OpenLayers.LonLat(4,5));
* bounds.extend(new OpenLayers.LonLat(5,6));
* bounds.toBBOX(); // returns 4,5,5,6
* (end)
*/
OpenLayers.Bounds = OpenLayers.Class({
/**
* Property: left
* {Number} Minimum horizontal coordinate.
*/
left: null,
/**
* Property: bottom
* {Number} Minimum vertical coordinate.
*/
bottom: null,
/**
* Property: right
* {Number} Maximum horizontal coordinate.
*/
right: null,
/**
* Property: top
* {Number} Maximum vertical coordinate.
*/
top: null,
/**
* Property: centerLonLat
* {<OpenLayers.LonLat>} A cached center location. This should not be
* accessed directly. Use <getCenterLonLat> instead.
*/
centerLonLat: null,
/**
* Constructor: OpenLayers.Bounds
* Construct a new bounds object. Coordinates can either be passed as four
* arguments, or as a single argument.
*
* Parameters (four arguments):
* left - {Number} The left bounds of the box. Note that for width
* calculations, this is assumed to be less than the right value.
* bottom - {Number} The bottom bounds of the box. Note that for height
* calculations, this is assumed to be less than the top value.
* right - {Number} The right bounds.
* top - {Number} The top bounds.
*
* Parameters (single argument):
* bounds - {Array(Number)} [left, bottom, right, top]
*/
initialize: function(left, bottom, right, top) {
if (OpenLayers.Util.isArray(left)) {
top = left[3];
right = left[2];
bottom = left[1];
left = left[0];
}
if (left != null) {
this.left = OpenLayers.Util.toFloat(left);
}
if (bottom != null) {
this.bottom = OpenLayers.Util.toFloat(bottom);
}
if (right != null) {
this.right = OpenLayers.Util.toFloat(right);
}
if (top != null) {
this.top = OpenLayers.Util.toFloat(top);
}
},
/**
* Method: clone
* Create a cloned instance of this bounds.
*
* Returns:
* {<OpenLayers.Bounds>} A fresh copy of the bounds
*/
clone:function() {
return new OpenLayers.Bounds(this.left, this.bottom,
this.right, this.top);
},
/**
* Method: equals
* Test a two bounds for equivalence.
*
* Parameters:
* bounds - {<OpenLayers.Bounds>}
*
* Returns:
* {Boolean} The passed-in bounds object has the same left,
* right, top, bottom components as this. Note that if bounds
* passed in is null, returns false.
*/
equals:function(bounds) {
var equals = false;
if (bounds != null) {
equals = ((this.left == bounds.left) &&
(this.right == bounds.right) &&
(this.top == bounds.top) &&
(this.bottom == bounds.bottom));
}
return equals;
},
/**
* APIMethod: toString
* Returns a string representation of the bounds object.
*
* Returns:
* {String} String representation of bounds object.
*/
toString:function() {
return [this.left, this.bottom, this.right, this.top].join(",");
},
/**
* APIMethod: toArray
* Returns an array representation of the bounds object.
*
* Returns an array of left, bottom, right, top properties, or -- when the
* optional parameter is true -- an array of the bottom, left, top,
* right properties.
*
* Parameters:
* reverseAxisOrder - {Boolean} Should we reverse the axis order?
*
* Returns:
* {Array} array of left, bottom, right, top
*/
toArray: function(reverseAxisOrder) {
if (reverseAxisOrder === true) {
return [this.bottom, this.left, this.top, this.right];
} else {
return [this.left, this.bottom, this.right, this.top];
}
},
/**
* APIMethod: toBBOX
* Returns a boundingbox-string representation of the bounds object.
*
* Parameters:
* decimal - {Integer} How many significant digits in the bbox coords?
* Default is 6
* reverseAxisOrder - {Boolean} Should we reverse the axis order?
*
* Returns:
* {String} Simple String representation of bounds object.
* (e.g. "5,42,10,45")
*/
toBBOX:function(decimal, reverseAxisOrder) {
if (decimal== null) {
decimal = 6;
}
var mult = Math.pow(10, decimal);
var xmin = Math.round(this.left * mult) / mult;
var ymin = Math.round(this.bottom * mult) / mult;
var xmax = Math.round(this.right * mult) / mult;
var ymax = Math.round(this.top * mult) / mult;
if (reverseAxisOrder === true) {
return ymin + "," + xmin + "," + ymax + "," + xmax;
} else {
return xmin + "," + ymin + "," + xmax + "," + ymax;
}
},
/**
* APIMethod: toGeometry
* Create a new polygon geometry based on this bounds.
*
* Returns:
* {<OpenLayers.Geometry.Polygon>} A new polygon with the coordinates
* of this bounds.
*/
toGeometry: function() {
return new OpenLayers.Geometry.Polygon([
new OpenLayers.Geometry.LinearRing([
new OpenLayers.Geometry.Point(this.left, this.bottom),
new OpenLayers.Geometry.Point(this.right, this.bottom),
new OpenLayers.Geometry.Point(this.right, this.top),
new OpenLayers.Geometry.Point(this.left, this.top)
])
]);
},
/**
* APIMethod: getWidth
* Returns the width of the bounds.
*
* Returns:
* {Float} The width of the bounds (right minus left).
*/
getWidth:function() {
return (this.right - this.left);
},
/**
* APIMethod: getHeight
* Returns the height of the bounds.
*
* Returns:
* {Float} The height of the bounds (top minus bottom).
*/
getHeight:function() {
return (this.top - this.bottom);
},
/**
* APIMethod: getSize
* Returns an <OpenLayers.Size> object of the bounds.
*
* Returns:
* {<OpenLayers.Size>} The size of the bounds.
*/
getSize:function() {
return new OpenLayers.Size(this.getWidth(), this.getHeight());
},
/**
* APIMethod: getCenterPixel
* Returns the <OpenLayers.Pixel> object which represents the center of the
* bounds.
*
* Returns:
* {<OpenLayers.Pixel>} The center of the bounds in pixel space.
*/
getCenterPixel:function() {
return new OpenLayers.Pixel( (this.left + this.right) / 2,
(this.bottom + this.top) / 2);
},
/**
* APIMethod: getCenterLonLat
* Returns the <OpenLayers.LonLat> object which represents the center of the
* bounds.
*
* Returns:
* {<OpenLayers.LonLat>} The center of the bounds in map space.
*/
getCenterLonLat:function() {
if(!this.centerLonLat) {
this.centerLonLat = new OpenLayers.LonLat(
(this.left + this.right) / 2, (this.bottom + this.top) / 2
);
}
return this.centerLonLat;
},
/**
* APIMethod: scale
* Scales the bounds around a pixel or lonlat. Note that the new
* bounds may return non-integer properties, even if a pixel
* is passed.
*
* Parameters:
* ratio - {Float}
* origin - {<OpenLayers.Pixel> or <OpenLayers.LonLat>}
* Default is center.
*
* Returns:
* {<OpenLayers.Bounds>} A new bounds that is scaled by ratio
* from origin.
*/
scale: function(ratio, origin){
if(origin == null){
origin = this.getCenterLonLat();
}
var origx,origy;
// get origin coordinates
if(origin.CLASS_NAME == "OpenLayers.LonLat"){
origx = origin.lon;
origy = origin.lat;
} else {
origx = origin.x;
origy = origin.y;
}
var left = (this.left - origx) * ratio + origx;
var bottom = (this.bottom - origy) * ratio + origy;
var right = (this.right - origx) * ratio + origx;
var top = (this.top - origy) * ratio + origy;
return new OpenLayers.Bounds(left, bottom, right, top);
},
/**
* APIMethod: add
* Shifts the coordinates of the bound by the given horizontal and vertical
* deltas.
*
* (start code)
* var bounds = new OpenLayers.Bounds(0, 0, 10, 10);
* bounds.toString();
* // => "0,0,10,10"
*
* bounds.add(-1.5, 4).toString();
* // => "-1.5,4,8.5,14"
* (end)
*
* This method will throw a TypeError if it is passed null as an argument.
*
* Parameters:
* x - {Float} horizontal delta
* y - {Float} vertical delta
*
* Returns:
* {<OpenLayers.Bounds>} A new bounds whose coordinates are the same as
* this, but shifted by the passed-in x and y values.
*/
add:function(x, y) {
if ( (x == null) || (y == null) ) {
throw new TypeError('Bounds.add cannot receive null values');
}
return new OpenLayers.Bounds(this.left + x, this.bottom + y,
this.right + x, this.top + y);
},
/**
* APIMethod: extend
* Extend the bounds to include the <OpenLayers.LonLat>,
* <OpenLayers.Geometry.Point> or <OpenLayers.Bounds> specified.
*
* Please note that this function assumes that left < right and
* bottom < top.
*
* Parameters:
* object - {<OpenLayers.LonLat>, <OpenLayers.Geometry.Point> or
* <OpenLayers.Bounds>} The object to be included in the new bounds
* object.
*/
extend:function(object) {
if (object) {
switch(object.CLASS_NAME) {
case "OpenLayers.LonLat":
this.extendXY(object.lon, object.lat);
break;
case "OpenLayers.Geometry.Point":
this.extendXY(object.x, object.y);
break;
case "OpenLayers.Bounds":
// clear cached center location
this.centerLonLat = null;
if ( (this.left == null) || (object.left < this.left)) {
this.left = object.left;
}
if ( (this.bottom == null) || (object.bottom < this.bottom) ) {
this.bottom = object.bottom;
}
if ( (this.right == null) || (object.right > this.right) ) {
this.right = object.right;
}
if ( (this.top == null) || (object.top > this.top) ) {
this.top = object.top;
}
break;
}
}
},
/**
* APIMethod: extendXY
* Extend the bounds to include the XY coordinate specified.
*
* Parameters:
* x - {number} The X part of the the coordinate.
* y - {number} The Y part of the the coordinate.
*/
extendXY:function(x, y) {
// clear cached center location
this.centerLonLat = null;
if ((this.left == null) || (x < this.left)) {
this.left = x;
}
if ((this.bottom == null) || (y < this.bottom)) {
this.bottom = y;
}
if ((this.right == null) || (x > this.right)) {
this.right = x;
}
if ((this.top == null) || (y > this.top)) {
this.top = y;
}
},
/**
* APIMethod: containsLonLat
* Returns whether the bounds object contains the given <OpenLayers.LonLat>.
*
* Parameters:
* ll - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
* object with a 'lon' and 'lat' properties.
* options - {Object} Optional parameters
*
* Acceptable options:
* inclusive - {Boolean} Whether or not to include the border.
* Default is true.
* worldBounds - {<OpenLayers.Bounds>} If a worldBounds is provided, the
* ll will be considered as contained if it exceeds the world bounds,
* but can be wrapped around the dateline so it is contained by this
* bounds.
*
* Returns:
* {Boolean} The passed-in lonlat is within this bounds.
*/
containsLonLat: function(ll, options) {
if (typeof options === "boolean") {
options = {inclusive: options};
}
options = options || {};
var contains = this.contains(ll.lon, ll.lat, options.inclusive),
worldBounds = options.worldBounds;
if (worldBounds && !contains) {
var worldWidth = worldBounds.getWidth();
var worldCenterX = (worldBounds.left + worldBounds.right) / 2;
var worldsAway = Math.round((ll.lon - worldCenterX) / worldWidth);
contains = this.containsLonLat({
lon: ll.lon - worldsAway * worldWidth,
lat: ll.lat
}, {inclusive: options.inclusive});
}
return contains;
},
/**
* APIMethod: containsPixel
* Returns whether the bounds object contains the given <OpenLayers.Pixel>.
*
* Parameters:
* px - {<OpenLayers.Pixel>}
* inclusive - {Boolean} Whether or not to include the border. Default is
* true.
*
* Returns:
* {Boolean} The passed-in pixel is within this bounds.
*/
containsPixel:function(px, inclusive) {
return this.contains(px.x, px.y, inclusive);
},
/**
* APIMethod: contains
* Returns whether the bounds object contains the given x and y.
*
* Parameters:
* x - {Float}
* y - {Float}
* inclusive - {Boolean} Whether or not to include the border. Default is
* true.
*
* Returns:
* {Boolean} Whether or not the passed-in coordinates are within this
* bounds.
*/
contains:function(x, y, inclusive) {
//set default
if (inclusive == null) {
inclusive = true;
}
if (x == null || y == null) {
return false;
}
x = OpenLayers.Util.toFloat(x);
y = OpenLayers.Util.toFloat(y);
var contains = false;
if (inclusive) {
contains = ((x >= this.left) && (x <= this.right) &&
(y >= this.bottom) && (y <= this.top));
} else {
contains = ((x > this.left) && (x < this.right) &&
(y > this.bottom) && (y < this.top));
}
return contains;
},
/**
* APIMethod: intersectsBounds
* Determine whether the target bounds intersects this bounds. Bounds are
* considered intersecting if any of their edges intersect or if one
* bounds contains the other.
*
* Parameters:
* bounds - {<OpenLayers.Bounds>} The target bounds.
* options - {Object} Optional parameters.
*
* Acceptable options:
* inclusive - {Boolean} Treat coincident borders as intersecting. Default
* is true. If false, bounds that do not overlap but only touch at the
* border will not be considered as intersecting.
* worldBounds - {<OpenLayers.Bounds>} If a worldBounds is provided, two
* bounds will be considered as intersecting if they intersect when
* shifted to within the world bounds. This applies only to bounds that
* cross or are completely outside the world bounds.
*
* Returns:
* {Boolean} The passed-in bounds object intersects this bounds.
*/
intersectsBounds:function(bounds, options) {
if (typeof options === "boolean") {
options = {inclusive: options};
}
options = options || {};
if (options.worldBounds) {
var self = this.wrapDateLine(options.worldBounds);
bounds = bounds.wrapDateLine(options.worldBounds);
} else {
self = this;
}
if (options.inclusive == null) {
options.inclusive = true;
}
var intersects = false;
var mightTouch = (
self.left == bounds.right ||
self.right == bounds.left ||
self.top == bounds.bottom ||
self.bottom == bounds.top
);
// if the two bounds only touch at an edge, and inclusive is false,
// then the bounds don't *really* intersect.
if (options.inclusive || !mightTouch) {
// otherwise, if one of the boundaries even partially contains another,
// inclusive of the edges, then they do intersect.
var inBottom = (
((bounds.bottom >= self.bottom) && (bounds.bottom <= self.top)) ||
((self.bottom >= bounds.bottom) && (self.bottom <= bounds.top))
);
var inTop = (
((bounds.top >= self.bottom) && (bounds.top <= self.top)) ||
((self.top > bounds.bottom) && (self.top < bounds.top))
);
var inLeft = (
((bounds.left >= self.left) && (bounds.left <= self.right)) ||
((self.left >= bounds.left) && (self.left <= bounds.right))
);
var inRight = (
((bounds.right >= self.left) && (bounds.right <= self.right)) ||
((self.right >= bounds.left) && (self.right <= bounds.right))
);
intersects = ((inBottom || inTop) && (inLeft || inRight));
}
// document me
if (options.worldBounds && !intersects) {
var world = options.worldBounds;
var width = world.getWidth();
var selfCrosses = !world.containsBounds(self);
var boundsCrosses = !world.containsBounds(bounds);
if (selfCrosses && !boundsCrosses) {
bounds = bounds.add(-width, 0);
intersects = self.intersectsBounds(bounds, {inclusive: options.inclusive});
} else if (boundsCrosses && !selfCrosses) {
self = self.add(-width, 0);
intersects = bounds.intersectsBounds(self, {inclusive: options.inclusive});
}
}
return intersects;
},
/**
* APIMethod: containsBounds
* Returns whether the bounds object contains the given <OpenLayers.Bounds>.
*
* bounds - {<OpenLayers.Bounds>} The target bounds.
* partial - {Boolean} If any of the target corners is within this bounds
* consider the bounds contained. Default is false. If false, the
* entire target bounds must be contained within this bounds.
* inclusive - {Boolean} Treat shared edges as contained. Default is
* true.
*
* Returns:
* {Boolean} The passed-in bounds object is contained within this bounds.
*/
containsBounds:function(bounds, partial, inclusive) {
if (partial == null) {
partial = false;
}
if (inclusive == null) {
inclusive = true;
}
var bottomLeft = this.contains(bounds.left, bounds.bottom, inclusive);
var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive);
var topLeft = this.contains(bounds.left, bounds.top, inclusive);
var topRight = this.contains(bounds.right, bounds.top, inclusive);
return (partial) ? (bottomLeft || bottomRight || topLeft || topRight)
: (bottomLeft && bottomRight && topLeft && topRight);
},
/**
* APIMethod: determineQuadrant
* Returns the the quadrant ("br", "tr", "tl", "bl") in which the given
* <OpenLayers.LonLat> lies.
*
* Parameters:
* lonlat - {<OpenLayers.LonLat>}
*
* Returns:
* {String} The quadrant ("br" "tr" "tl" "bl") of the bounds in which the
* coordinate lies.
*/
determineQuadrant: function(lonlat) {
var quadrant = "";
var center = this.getCenterLonLat();
quadrant += (lonlat.lat < center.lat) ? "b" : "t";
quadrant += (lonlat.lon < center.lon) ? "l" : "r";
return quadrant;
},
/**
* APIMethod: transform
* Transform the Bounds object from source to dest.
*
* Parameters:
* source - {<OpenLayers.Projection>} Source projection.
* dest - {<OpenLayers.Projection>} Destination projection.
*
* Returns:
* {<OpenLayers.Bounds>} Itself, for use in chaining operations.
*/
transform: function(source, dest) {
// clear cached center location
this.centerLonLat = null;
var ll = OpenLayers.Projection.transform(
{'x': this.left, 'y': this.bottom}, source, dest);
var lr = OpenLayers.Projection.transform(
{'x': this.right, 'y': this.bottom}, source, dest);
var ul = OpenLayers.Projection.transform(
{'x': this.left, 'y': this.top}, source, dest);
var ur = OpenLayers.Projection.transform(
{'x': this.right, 'y': this.top}, source, dest);
this.left = Math.min(ll.x, ul.x);
this.bottom = Math.min(ll.y, lr.y);
this.right = Math.max(lr.x, ur.x);
this.top = Math.max(ul.y, ur.y);
return this;
},
/**
* APIMethod: wrapDateLine
* Wraps the bounds object around the dateline.
*
* Parameters:
* maxExtent - {<OpenLayers.Bounds>}
* options - {Object} Some possible options are:
*
* Allowed Options:
* leftTolerance - {float} Allow for a margin of error
* with the 'left' value of this
* bound.
* Default is 0.
* rightTolerance - {float} Allow for a margin of error
* with the 'right' value of
* this bound.
* Default is 0.
*
* Returns:
* {<OpenLayers.Bounds>} A copy of this bounds, but wrapped around the
* "dateline" (as specified by the borders of
* maxExtent). Note that this function only returns
* a different bounds value if this bounds is
* *entirely* outside of the maxExtent. If this
* bounds straddles the dateline (is part in/part
* out of maxExtent), the returned bounds will always
* cross the left edge of the given maxExtent.
*.
*/
wrapDateLine: function(maxExtent, options) {
options = options || {};
var leftTolerance = options.leftTolerance || 0;
var rightTolerance = options.rightTolerance || 0;
var newBounds = this.clone();
if (maxExtent) {
var width = maxExtent.getWidth();
//shift right?
while (newBounds.left < maxExtent.left &&
newBounds.right - rightTolerance <= maxExtent.left ) {
newBounds = newBounds.add(width, 0);
}
//shift left?
while (newBounds.left + leftTolerance >= maxExtent.right &&
newBounds.right > maxExtent.right ) {
newBounds = newBounds.add(-width, 0);
}
// crosses right only? force left
var newLeft = newBounds.left + leftTolerance;
if (newLeft < maxExtent.right && newLeft > maxExtent.left &&
newBounds.right - rightTolerance > maxExtent.right) {
newBounds = newBounds.add(-width, 0);
}
}
return newBounds;
},
CLASS_NAME: "OpenLayers.Bounds"
});
/**
* APIFunction: fromString
* Alternative constructor that builds a new OpenLayers.Bounds from a
* parameter string.
*
* (begin code)
* OpenLayers.Bounds.fromString("5,42,10,45");
* // => equivalent to ...
* new OpenLayers.Bounds(5, 42, 10, 45);
* (end)
*
* Parameters:
* str - {String} Comma-separated bounds string. (e.g. "5,42,10,45")
* reverseAxisOrder - {Boolean} Does the string use reverse axis order?
*
* Returns:
* {<OpenLayers.Bounds>} New bounds object built from the
* passed-in String.
*/
OpenLayers.Bounds.fromString = function(str, reverseAxisOrder) {
var bounds = str.split(",");
return OpenLayers.Bounds.fromArray(bounds, reverseAxisOrder);
};
/**
* APIFunction: fromArray
* Alternative constructor that builds a new OpenLayers.Bounds from an array.
*
* (begin code)
* OpenLayers.Bounds.fromArray( [5, 42, 10, 45] );
* // => equivalent to ...
* new OpenLayers.Bounds(5, 42, 10, 45);
* (end)
*
* Parameters:
* bbox - {Array(Float)} Array of bounds values (e.g. [5,42,10,45])
* reverseAxisOrder - {Boolean} Does the array use reverse axis order?
*
* Returns:
* {<OpenLayers.Bounds>} New bounds object built from the passed-in Array.
*/
OpenLayers.Bounds.fromArray = function(bbox, reverseAxisOrder) {
return reverseAxisOrder === true ?
new OpenLayers.Bounds(bbox[1], bbox[0], bbox[3], bbox[2]) :
new OpenLayers.Bounds(bbox[0], bbox[1], bbox[2], bbox[3]);
};
/**
* APIFunction: fromSize
* Alternative constructor that builds a new OpenLayers.Bounds from a size.
*
* (begin code)
* OpenLayers.Bounds.fromSize( new OpenLayers.Size(10, 20) );
* // => equivalent to ...
* new OpenLayers.Bounds(0, 20, 10, 0);
* (end)
*
* Parameters:
* size - {<OpenLayers.Size> or Object} <OpenLayers.Size> or an object with
* both 'w' and 'h' properties.
*
* Returns:
* {<OpenLayers.Bounds>} New bounds object built from the passed-in size.
*/
OpenLayers.Bounds.fromSize = function(size) {
return new OpenLayers.Bounds(0,
size.h,
size.w,
0);
};
/**
* Function: oppositeQuadrant
* Get the opposite quadrant for a given quadrant string.
*
* (begin code)
* OpenLayers.Bounds.oppositeQuadrant( "tl" );
* // => "br"
*
* OpenLayers.Bounds.oppositeQuadrant( "tr" );
* // => "bl"
* (end)
*
* Parameters:
* quadrant - {String} two character quadrant shortstring
*
* Returns:
* {String} The opposing quadrant ("br" "tr" "tl" "bl"). For Example, if
* you pass in "bl" it returns "tr", if you pass in "br" it
* returns "tl", etc.
*/
OpenLayers.Bounds.oppositeQuadrant = function(quadrant) {
var opp = "";
opp += (quadrant.charAt(0) == 't') ? 'b' : 't';
opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l';
return opp;
};

View File

@ -0,0 +1,121 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/SingleFile.js
*/
/**
* Constructor: OpenLayers.Class
* Base class used to construct all other classes. Includes support for
* multiple inheritance.
*
* This constructor is new in OpenLayers 2.5. At OpenLayers 3.0, the old
* syntax for creating classes and dealing with inheritance
* will be removed.
*
* To create a new OpenLayers-style class, use the following syntax:
* (code)
* var MyClass = OpenLayers.Class(prototype);
* (end)
*
* To create a new OpenLayers-style class with multiple inheritance, use the
* following syntax:
* (code)
* var MyClass = OpenLayers.Class(Class1, Class2, prototype);
* (end)
*
* Note that instanceof reflection will only reveal Class1 as superclass.
*
*/
OpenLayers.Class = function() {
var len = arguments.length;
var P = arguments[0];
var F = arguments[len-1];
var C = typeof F.initialize == "function" ?
F.initialize :
function(){ P.prototype.initialize.apply(this, arguments); };
if (len > 1) {
var newArgs = [C, P].concat(
Array.prototype.slice.call(arguments).slice(1, len-1), F);
OpenLayers.inherit.apply(null, newArgs);
} else {
C.prototype = F;
}
return C;
};
/**
* Function: OpenLayers.inherit
*
* Parameters:
* C - {Object} the class that inherits
* P - {Object} the superclass to inherit from
*
* In addition to the mandatory C and P parameters, an arbitrary number of
* objects can be passed, which will extend C.
*/
OpenLayers.inherit = function(C, P) {
var F = function() {};
F.prototype = P.prototype;
C.prototype = new F;
var i, l, o;
for(i=2, l=arguments.length; i<l; i++) {
o = arguments[i];
if(typeof o === "function") {
o = o.prototype;
}
OpenLayers.Util.extend(C.prototype, o);
}
};
/**
* APIFunction: extend
* Copy all properties of a source object to a destination object. Modifies
* the passed in destination object. Any properties on the source object
* that are set to undefined will not be (re)set on the destination object.
*
* Parameters:
* destination - {Object} The object that will be modified
* source - {Object} The object with properties to be set on the destination
*
* Returns:
* {Object} The destination object.
*/
OpenLayers.Util = OpenLayers.Util || {};
OpenLayers.Util.extend = function(destination, source) {
destination = destination || {};
if (source) {
for (var property in source) {
var value = source[property];
if (value !== undefined) {
destination[property] = value;
}
}
/**
* IE doesn't include the toString property when iterating over an object's
* properties with the for(property in object) syntax. Explicitly check if
* the source has its own toString property.
*/
/*
* FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
* prototype object" when calling hawOwnProperty if the source object
* is an instance of window.Event.
*/
var sourceIsEvt = typeof window.Event == "function"
&& source instanceof window.Event;
if (!sourceIsEvt
&& source.hasOwnProperty && source.hasOwnProperty("toString")) {
destination.toString = source.toString;
}
}
return destination;
};

View File

@ -0,0 +1,123 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/SingleFile.js
*/
/**
* Namespace: OpenLayers.Date
* Contains implementations of Date.parse and date.toISOString that match the
* ECMAScript 5 specification for parsing RFC 3339 dates.
* http://tools.ietf.org/html/rfc3339
*/
OpenLayers.Date = {
/**
* APIProperty: dateRegEx
* The regex to be used for validating dates. You can provide your own
* regex for instance for adding support for years before BC. Default
* value is: /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/
*/
dateRegEx: /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/,
/**
* APIMethod: toISOString
* Generates a string representing a date. The format of the string follows
* the profile of ISO 8601 for date and time on the Internet (see
* http://tools.ietf.org/html/rfc3339). If the toISOString method is
* available on the Date prototype, that is used. The toISOString
* method for Date instances is defined in ECMA-262.
*
* Parameters:
* date - {Date} A date object.
*
* Returns:
* {String} A string representing the date (e.g.
* "2010-08-07T16:58:23.123Z"). If the date does not have a valid time
* (i.e. isNaN(date.getTime())) this method returns the string "Invalid
* Date". The ECMA standard says the toISOString method should throw
* RangeError in this case, but Firefox returns a string instead. For
* best results, use isNaN(date.getTime()) to determine date validity
* before generating date strings.
*/
toISOString: (function() {
if ("toISOString" in Date.prototype) {
return function(date) {
return date.toISOString();
};
} else {
return function(date) {
var str;
if (isNaN(date.getTime())) {
// ECMA-262 says throw RangeError, Firefox returns
// "Invalid Date"
str = "Invalid Date";
} else {
str =
date.getUTCFullYear() + "-" +
OpenLayers.Number.zeroPad(date.getUTCMonth() + 1, 2) + "-" +
OpenLayers.Number.zeroPad(date.getUTCDate(), 2) + "T" +
OpenLayers.Number.zeroPad(date.getUTCHours(), 2) + ":" +
OpenLayers.Number.zeroPad(date.getUTCMinutes(), 2) + ":" +
OpenLayers.Number.zeroPad(date.getUTCSeconds(), 2) + "." +
OpenLayers.Number.zeroPad(date.getUTCMilliseconds(), 3) + "Z";
}
return str;
};
}
})(),
/**
* APIMethod: parse
* Generate a date object from a string. The format for the string follows
* the profile of ISO 8601 for date and time on the Internet (see
* http://tools.ietf.org/html/rfc3339). We don't call the native
* Date.parse because of inconsistency between implmentations. In
* Chrome, calling Date.parse with a string that doesn't contain any
* indication of the timezone (e.g. "2011"), the date is interpreted
* in local time. On Firefox, the assumption is UTC.
*
* Parameters:
* str - {String} A string representing the date (e.g.
* "2010", "2010-08", "2010-08-07", "2010-08-07T16:58:23.123Z",
* "2010-08-07T11:58:23.123-06").
*
* Returns:
* {Date} A date object. If the string could not be parsed, an invalid
* date is returned (i.e. isNaN(date.getTime())).
*/
parse: function(str) {
var date;
var match = str.match(this.dateRegEx);
if (match && (match[1] || match[7])) { // must have at least year or time
var year = parseInt(match[1], 10) || 0;
var month = (parseInt(match[2], 10) - 1) || 0;
var day = parseInt(match[3], 10) || 1;
date = new Date(Date.UTC(year, month, day));
// optional time
var type = match[7];
if (type) {
var hours = parseInt(match[4], 10);
var minutes = parseInt(match[5], 10);
var secFrac = parseFloat(match[6]);
var seconds = secFrac | 0;
var milliseconds = Math.round(1000 * (secFrac - seconds));
date.setUTCHours(hours, minutes, seconds, milliseconds);
// check offset
if (type !== "Z") {
var hoursOffset = parseInt(type, 10);
var minutesOffset = parseInt(match[8], 10) || 0;
var offset = -1000 * (60 * (hoursOffset * 60) + minutesOffset * 60);
date = new Date(date.getTime() + offset);
}
}
} else {
date = new Date("invalid");
}
return date;
}
};

View File

@ -0,0 +1,189 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Util.js
* @requires OpenLayers/BaseTypes.js
*/
/**
* Namespace: OpenLayers.Element
*/
OpenLayers.Element = {
/**
* APIFunction: visible
*
* Parameters:
* element - {DOMElement}
*
* Returns:
* {Boolean} Is the element visible?
*/
visible: function(element) {
return OpenLayers.Util.getElement(element).style.display != 'none';
},
/**
* APIFunction: toggle
* Toggle the visibility of element(s) passed in
*
* Parameters:
* element - {DOMElement} Actually user can pass any number of elements
*/
toggle: function() {
for (var i=0, len=arguments.length; i<len; i++) {
var element = OpenLayers.Util.getElement(arguments[i]);
var display = OpenLayers.Element.visible(element) ? 'none'
: '';
element.style.display = display;
}
},
/**
* APIFunction: remove
* Remove the specified element from the DOM.
*
* Parameters:
* element - {DOMElement}
*/
remove: function(element) {
element = OpenLayers.Util.getElement(element);
element.parentNode.removeChild(element);
},
/**
* APIFunction: getHeight
*
* Parameters:
* element - {DOMElement}
*
* Returns:
* {Integer} The offset height of the element passed in
*/
getHeight: function(element) {
element = OpenLayers.Util.getElement(element);
return element.offsetHeight;
},
/**
* Function: hasClass
* Tests if an element has the given CSS class name.
*
* Parameters:
* element - {DOMElement} A DOM element node.
* name - {String} The CSS class name to search for.
*
* Returns:
* {Boolean} The element has the given class name.
*/
hasClass: function(element, name) {
var names = element.className;
return (!!names && new RegExp("(^|\\s)" + name + "(\\s|$)").test(names));
},
/**
* Function: addClass
* Add a CSS class name to an element. Safe where element already has
* the class name.
*
* Parameters:
* element - {DOMElement} A DOM element node.
* name - {String} The CSS class name to add.
*
* Returns:
* {DOMElement} The element.
*/
addClass: function(element, name) {
if(!OpenLayers.Element.hasClass(element, name)) {
element.className += (element.className ? " " : "") + name;
}
return element;
},
/**
* Function: removeClass
* Remove a CSS class name from an element. Safe where element does not
* have the class name.
*
* Parameters:
* element - {DOMElement} A DOM element node.
* name - {String} The CSS class name to remove.
*
* Returns:
* {DOMElement} The element.
*/
removeClass: function(element, name) {
var names = element.className;
if(names) {
element.className = OpenLayers.String.trim(
names.replace(
new RegExp("(^|\\s+)" + name + "(\\s+|$)"), " "
)
);
}
return element;
},
/**
* Function: toggleClass
* Remove a CSS class name from an element if it exists. Add the class name
* if it doesn't exist.
*
* Parameters:
* element - {DOMElement} A DOM element node.
* name - {String} The CSS class name to toggle.
*
* Returns:
* {DOMElement} The element.
*/
toggleClass: function(element, name) {
if(OpenLayers.Element.hasClass(element, name)) {
OpenLayers.Element.removeClass(element, name);
} else {
OpenLayers.Element.addClass(element, name);
}
return element;
},
/**
* APIFunction: getStyle
*
* Parameters:
* element - {DOMElement}
* style - {?}
*
* Returns:
* {?}
*/
getStyle: function(element, style) {
element = OpenLayers.Util.getElement(element);
var value = null;
if (element && element.style) {
value = element.style[OpenLayers.String.camelize(style)];
if (!value) {
if (document.defaultView &&
document.defaultView.getComputedStyle) {
var css = document.defaultView.getComputedStyle(element, null);
value = css ? css.getPropertyValue(style) : null;
} else if (element.currentStyle) {
value = element.currentStyle[OpenLayers.String.camelize(style)];
}
}
var positions = ['left', 'top', 'right', 'bottom'];
if (window.opera &&
(OpenLayers.Util.indexOf(positions,style) != -1) &&
(OpenLayers.Element.getStyle(element, 'position') == 'static')) {
value = 'auto';
}
}
return value == 'auto' ? null : value;
}
};

View File

@ -0,0 +1,215 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/BaseTypes/Class.js
*/
/**
* Class: OpenLayers.LonLat
* This class represents a longitude and latitude pair
*/
OpenLayers.LonLat = OpenLayers.Class({
/**
* APIProperty: lon
* {Float} The x-axis coodinate in map units
*/
lon: 0.0,
/**
* APIProperty: lat
* {Float} The y-axis coordinate in map units
*/
lat: 0.0,
/**
* Constructor: OpenLayers.LonLat
* Create a new map location. Coordinates can be passed either as two
* arguments, or as a single argument.
*
* Parameters (two arguments):
* lon - {Number} The x-axis coordinate in map units. If your map is in
* a geographic projection, this will be the Longitude. Otherwise,
* it will be the x coordinate of the map location in your map units.
* lat - {Number} The y-axis coordinate in map units. If your map is in
* a geographic projection, this will be the Latitude. Otherwise,
* it will be the y coordinate of the map location in your map units.
*
* Parameters (single argument):
* location - {Array(Float)} [lon, lat]
*/
initialize: function(lon, lat) {
if (OpenLayers.Util.isArray(lon)) {
lat = lon[1];
lon = lon[0];
}
this.lon = OpenLayers.Util.toFloat(lon);
this.lat = OpenLayers.Util.toFloat(lat);
},
/**
* Method: toString
* Return a readable string version of the lonlat
*
* Returns:
* {String} String representation of OpenLayers.LonLat object.
* (e.g. <i>"lon=5,lat=42"</i>)
*/
toString:function() {
return ("lon=" + this.lon + ",lat=" + this.lat);
},
/**
* APIMethod: toShortString
*
* Returns:
* {String} Shortened String representation of OpenLayers.LonLat object.
* (e.g. <i>"5, 42"</i>)
*/
toShortString:function() {
return (this.lon + ", " + this.lat);
},
/**
* APIMethod: clone
*
* Returns:
* {<OpenLayers.LonLat>} New OpenLayers.LonLat object with the same lon
* and lat values
*/
clone:function() {
return new OpenLayers.LonLat(this.lon, this.lat);
},
/**
* APIMethod: add
*
* Parameters:
* lon - {Float}
* lat - {Float}
*
* Returns:
* {<OpenLayers.LonLat>} A new OpenLayers.LonLat object with the lon and
* lat passed-in added to this's.
*/
add:function(lon, lat) {
if ( (lon == null) || (lat == null) ) {
throw new TypeError('LonLat.add cannot receive null values');
}
return new OpenLayers.LonLat(this.lon + OpenLayers.Util.toFloat(lon),
this.lat + OpenLayers.Util.toFloat(lat));
},
/**
* APIMethod: equals
*
* Parameters:
* ll - {<OpenLayers.LonLat>}
*
* Returns:
* {Boolean} Boolean value indicating whether the passed-in
* <OpenLayers.LonLat> object has the same lon and lat
* components as this.
* Note: if ll passed in is null, returns false
*/
equals:function(ll) {
var equals = false;
if (ll != null) {
equals = ((this.lon == ll.lon && this.lat == ll.lat) ||
(isNaN(this.lon) && isNaN(this.lat) && isNaN(ll.lon) && isNaN(ll.lat)));
}
return equals;
},
/**
* APIMethod: transform
* Transform the LonLat object from source to dest. This transformation is
* *in place*: if you want a *new* lonlat, use .clone() first.
*
* Parameters:
* source - {<OpenLayers.Projection>} Source projection.
* dest - {<OpenLayers.Projection>} Destination projection.
*
* Returns:
* {<OpenLayers.LonLat>} Itself, for use in chaining operations.
*/
transform: function(source, dest) {
var point = OpenLayers.Projection.transform(
{'x': this.lon, 'y': this.lat}, source, dest);
this.lon = point.x;
this.lat = point.y;
return this;
},
/**
* APIMethod: wrapDateLine
*
* Parameters:
* maxExtent - {<OpenLayers.Bounds>}
*
* Returns:
* {<OpenLayers.LonLat>} A copy of this lonlat, but wrapped around the
* "dateline" (as specified by the borders of
* maxExtent)
*/
wrapDateLine: function(maxExtent) {
var newLonLat = this.clone();
if (maxExtent) {
//shift right?
while (newLonLat.lon < maxExtent.left) {
newLonLat.lon += maxExtent.getWidth();
}
//shift left?
while (newLonLat.lon > maxExtent.right) {
newLonLat.lon -= maxExtent.getWidth();
}
}
return newLonLat;
},
CLASS_NAME: "OpenLayers.LonLat"
});
/**
* Function: fromString
* Alternative constructor that builds a new <OpenLayers.LonLat> from a
* parameter string
*
* Parameters:
* str - {String} Comma-separated Lon,Lat coordinate string.
* (e.g. <i>"5,40"</i>)
*
* Returns:
* {<OpenLayers.LonLat>} New <OpenLayers.LonLat> object built from the
* passed-in String.
*/
OpenLayers.LonLat.fromString = function(str) {
var pair = str.split(",");
return new OpenLayers.LonLat(pair[0], pair[1]);
};
/**
* Function: fromArray
* Alternative constructor that builds a new <OpenLayers.LonLat> from an
* array of two numbers that represent lon- and lat-values.
*
* Parameters:
* arr - {Array(Float)} Array of lon/lat values (e.g. [5,-42])
*
* Returns:
* {<OpenLayers.LonLat>} New <OpenLayers.LonLat> object built from the
* passed-in array.
*/
OpenLayers.LonLat.fromArray = function(arr) {
var gotArr = OpenLayers.Util.isArray(arr),
lon = gotArr && arr[0],
lat = gotArr && arr[1];
return new OpenLayers.LonLat(lon, lat);
};

View File

@ -0,0 +1,143 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/BaseTypes/Class.js
*/
/**
* Class: OpenLayers.Pixel
* This class represents a screen coordinate, in x and y coordinates
*/
OpenLayers.Pixel = OpenLayers.Class({
/**
* APIProperty: x
* {Number} The x coordinate
*/
x: 0.0,
/**
* APIProperty: y
* {Number} The y coordinate
*/
y: 0.0,
/**
* Constructor: OpenLayers.Pixel
* Create a new OpenLayers.Pixel instance
*
* Parameters:
* x - {Number} The x coordinate
* y - {Number} The y coordinate
*
* Returns:
* An instance of OpenLayers.Pixel
*/
initialize: function(x, y) {
this.x = parseFloat(x);
this.y = parseFloat(y);
},
/**
* Method: toString
* Cast this object into a string
*
* Returns:
* {String} The string representation of Pixel. ex: "x=200.4,y=242.2"
*/
toString:function() {
return ("x=" + this.x + ",y=" + this.y);
},
/**
* APIMethod: clone
* Return a clone of this pixel object
*
* Returns:
* {<OpenLayers.Pixel>} A clone pixel
*/
clone:function() {
return new OpenLayers.Pixel(this.x, this.y);
},
/**
* APIMethod: equals
* Determine whether one pixel is equivalent to another
*
* Parameters:
* px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object with
* a 'x' and 'y' properties.
*
* Returns:
* {Boolean} The point passed in as parameter is equal to this. Note that
* if px passed in is null, returns false.
*/
equals:function(px) {
var equals = false;
if (px != null) {
equals = ((this.x == px.x && this.y == px.y) ||
(isNaN(this.x) && isNaN(this.y) && isNaN(px.x) && isNaN(px.y)));
}
return equals;
},
/**
* APIMethod: distanceTo
* Returns the distance to the pixel point passed in as a parameter.
*
* Parameters:
* px - {<OpenLayers.Pixel>}
*
* Returns:
* {Float} The pixel point passed in as parameter to calculate the
* distance to.
*/
distanceTo:function(px) {
return Math.sqrt(
Math.pow(this.x - px.x, 2) +
Math.pow(this.y - px.y, 2)
);
},
/**
* APIMethod: add
*
* Parameters:
* x - {Integer}
* y - {Integer}
*
* Returns:
* {<OpenLayers.Pixel>} A new Pixel with this pixel's x&y augmented by the
* values passed in.
*/
add:function(x, y) {
if ( (x == null) || (y == null) ) {
throw new TypeError('Pixel.add cannot receive null values');
}
return new OpenLayers.Pixel(this.x + x, this.y + y);
},
/**
* APIMethod: offset
*
* Parameters
* px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object with
* a 'x' and 'y' properties.
*
* Returns:
* {<OpenLayers.Pixel>} A new Pixel with this pixel's x&y augmented by the
* x&y values of the pixel passed in.
*/
offset:function(px) {
var newPx = this.clone();
if (px) {
newPx = this.add(px.x, px.y);
}
return newPx;
},
CLASS_NAME: "OpenLayers.Pixel"
});

View File

@ -0,0 +1,89 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/BaseTypes/Class.js
*/
/**
* Class: OpenLayers.Size
* Instances of this class represent a width/height pair
*/
OpenLayers.Size = OpenLayers.Class({
/**
* APIProperty: w
* {Number} width
*/
w: 0.0,
/**
* APIProperty: h
* {Number} height
*/
h: 0.0,
/**
* Constructor: OpenLayers.Size
* Create an instance of OpenLayers.Size
*
* Parameters:
* w - {Number} width
* h - {Number} height
*/
initialize: function(w, h) {
this.w = parseFloat(w);
this.h = parseFloat(h);
},
/**
* Method: toString
* Return the string representation of a size object
*
* Returns:
* {String} The string representation of OpenLayers.Size object.
* (e.g. <i>"w=55,h=66"</i>)
*/
toString:function() {
return ("w=" + this.w + ",h=" + this.h);
},
/**
* APIMethod: clone
* Create a clone of this size object
*
* Returns:
* {<OpenLayers.Size>} A new OpenLayers.Size object with the same w and h
* values
*/
clone:function() {
return new OpenLayers.Size(this.w, this.h);
},
/**
*
* APIMethod: equals
* Determine where this size is equal to another
*
* Parameters:
* sz - {<OpenLayers.Size>|Object} An OpenLayers.Size or an object with
* a 'w' and 'h' properties.
*
* Returns:
* {Boolean} The passed in size has the same h and w properties as this one.
* Note that if sz passed in is null, returns false.
*/
equals:function(sz) {
var equals = false;
if (sz != null) {
equals = ((this.w == sz.w && this.h == sz.h) ||
(isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h)));
}
return equals;
},
CLASS_NAME: "OpenLayers.Size"
});

View File

@ -0,0 +1,250 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/BaseTypes/Class.js
*/
/**
* Namespace: OpenLayers.Console
* The OpenLayers.Console namespace is used for debugging and error logging.
* If the Firebug Lite (../Firebug/firebug.js) is included before this script,
* calls to OpenLayers.Console methods will get redirected to window.console.
* This makes use of the Firebug extension where available and allows for
* cross-browser debugging Firebug style.
*
* Note:
* Note that behavior will differ with the Firebug extention and Firebug Lite.
* Most notably, the Firebug Lite console does not currently allow for
* hyperlinks to code or for clicking on object to explore their properties.
*
*/
OpenLayers.Console = {
/**
* Create empty functions for all console methods. The real value of these
* properties will be set if Firebug Lite (../Firebug/firebug.js script) is
* included. We explicitly require the Firebug Lite script to trigger
* functionality of the OpenLayers.Console methods.
*/
/**
* APIFunction: log
* Log an object in the console. The Firebug Lite console logs string
* representation of objects. Given multiple arguments, they will
* be cast to strings and logged with a space delimiter. If the first
* argument is a string with printf-like formatting, subsequent arguments
* will be used in string substitution. Any additional arguments (beyond
* the number substituted in a format string) will be appended in a space-
* delimited line.
*
* Parameters:
* object - {Object}
*/
log: function() {},
/**
* APIFunction: debug
* Writes a message to the console, including a hyperlink to the line
* where it was called.
*
* May be called with multiple arguments as with OpenLayers.Console.log().
*
* Parameters:
* object - {Object}
*/
debug: function() {},
/**
* APIFunction: info
* Writes a message to the console with the visual "info" icon and color
* coding and a hyperlink to the line where it was called.
*
* May be called with multiple arguments as with OpenLayers.Console.log().
*
* Parameters:
* object - {Object}
*/
info: function() {},
/**
* APIFunction: warn
* Writes a message to the console with the visual "warning" icon and
* color coding and a hyperlink to the line where it was called.
*
* May be called with multiple arguments as with OpenLayers.Console.log().
*
* Parameters:
* object - {Object}
*/
warn: function() {},
/**
* APIFunction: error
* Writes a message to the console with the visual "error" icon and color
* coding and a hyperlink to the line where it was called.
*
* May be called with multiple arguments as with OpenLayers.Console.log().
*
* Parameters:
* object - {Object}
*/
error: function() {},
/**
* APIFunction: userError
* A single interface for showing error messages to the user. The default
* behavior is a Javascript alert, though this can be overridden by
* reassigning OpenLayers.Console.userError to a different function.
*
* Expects a single error message
*
* Parameters:
* error - {Object}
*/
userError: function(error) {
alert(error);
},
/**
* APIFunction: assert
* Tests that an expression is true. If not, it will write a message to
* the console and throw an exception.
*
* May be called with multiple arguments as with OpenLayers.Console.log().
*
* Parameters:
* object - {Object}
*/
assert: function() {},
/**
* APIFunction: dir
* Prints an interactive listing of all properties of the object. This
* looks identical to the view that you would see in the DOM tab.
*
* Parameters:
* object - {Object}
*/
dir: function() {},
/**
* APIFunction: dirxml
* Prints the XML source tree of an HTML or XML element. This looks
* identical to the view that you would see in the HTML tab. You can click
* on any node to inspect it in the HTML tab.
*
* Parameters:
* object - {Object}
*/
dirxml: function() {},
/**
* APIFunction: trace
* Prints an interactive stack trace of JavaScript execution at the point
* where it is called. The stack trace details the functions on the stack,
* as well as the values that were passed as arguments to each function.
* You can click each function to take you to its source in the Script tab,
* and click each argument value to inspect it in the DOM or HTML tabs.
*
*/
trace: function() {},
/**
* APIFunction: group
* Writes a message to the console and opens a nested block to indent all
* future messages sent to the console. Call OpenLayers.Console.groupEnd()
* to close the block.
*
* May be called with multiple arguments as with OpenLayers.Console.log().
*
* Parameters:
* object - {Object}
*/
group: function() {},
/**
* APIFunction: groupEnd
* Closes the most recently opened block created by a call to
* OpenLayers.Console.group
*/
groupEnd: function() {},
/**
* APIFunction: time
* Creates a new timer under the given name. Call
* OpenLayers.Console.timeEnd(name)
* with the same name to stop the timer and print the time elapsed.
*
* Parameters:
* name - {String}
*/
time: function() {},
/**
* APIFunction: timeEnd
* Stops a timer created by a call to OpenLayers.Console.time(name) and
* writes the time elapsed.
*
* Parameters:
* name - {String}
*/
timeEnd: function() {},
/**
* APIFunction: profile
* Turns on the JavaScript profiler. The optional argument title would
* contain the text to be printed in the header of the profile report.
*
* This function is not currently implemented in Firebug Lite.
*
* Parameters:
* title - {String} Optional title for the profiler
*/
profile: function() {},
/**
* APIFunction: profileEnd
* Turns off the JavaScript profiler and prints its report.
*
* This function is not currently implemented in Firebug Lite.
*/
profileEnd: function() {},
/**
* APIFunction: count
* Writes the number of times that the line of code where count was called
* was executed. The optional argument title will print a message in
* addition to the number of the count.
*
* This function is not currently implemented in Firebug Lite.
*
* Parameters:
* title - {String} Optional title to be printed with count
*/
count: function() {},
CLASS_NAME: "OpenLayers.Console"
};
/**
* Execute an anonymous function to extend the OpenLayers.Console namespace
* if the firebug.js script is included. This closure is used so that the
* "scripts" and "i" variables don't pollute the global namespace.
*/
(function() {
/**
* If Firebug Lite is included (before this script), re-route all
* OpenLayers.Console calls to the console object.
*/
var scripts = document.getElementsByTagName("script");
for(var i=0, len=scripts.length; i<len; ++i) {
if(scripts[i].src.indexOf("firebug.js") != -1) {
if(console) {
OpenLayers.Util.extend(OpenLayers.Console, console);
break;
}
}
}
})();

View File

@ -0,0 +1,371 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/BaseTypes/Class.js
*/
/**
* Class: OpenLayers.Control
* Controls affect the display or behavior of the map. They allow everything
* from panning and zooming to displaying a scale indicator. Controls by
* default are added to the map they are contained within however it is
* possible to add a control to an external div by passing the div in the
* options parameter.
*
* Example:
* The following example shows how to add many of the common controls
* to a map.
*
* > var map = new OpenLayers.Map('map', { controls: [] });
* >
* > map.addControl(new OpenLayers.Control.PanZoomBar());
* > map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false}));
* > map.addControl(new OpenLayers.Control.Permalink());
* > map.addControl(new OpenLayers.Control.Permalink('permalink'));
* > map.addControl(new OpenLayers.Control.MousePosition());
* > map.addControl(new OpenLayers.Control.OverviewMap());
* > map.addControl(new OpenLayers.Control.KeyboardDefaults());
*
* The next code fragment is a quick example of how to intercept
* shift-mouse click to display the extent of the bounding box
* dragged out by the user. Usually controls are not created
* in exactly this manner. See the source for a more complete
* example:
*
* > var control = new OpenLayers.Control();
* > OpenLayers.Util.extend(control, {
* > draw: function () {
* > // this Handler.Box will intercept the shift-mousedown
* > // before Control.MouseDefault gets to see it
* > this.box = new OpenLayers.Handler.Box( control,
* > {"done": this.notice},
* > {keyMask: OpenLayers.Handler.MOD_SHIFT});
* > this.box.activate();
* > },
* >
* > notice: function (bounds) {
* > OpenLayers.Console.userError(bounds);
* > }
* > });
* > map.addControl(control);
*
*/
OpenLayers.Control = OpenLayers.Class({
/**
* Property: id
* {String}
*/
id: null,
/**
* Property: map
* {<OpenLayers.Map>} this gets set in the addControl() function in
* OpenLayers.Map
*/
map: null,
/**
* APIProperty: div
* {DOMElement} The element that contains the control, if not present the
* control is placed inside the map.
*/
div: null,
/**
* APIProperty: type
* {Number} Controls can have a 'type'. The type determines the type of
* interactions which are possible with them when they are placed in an
* <OpenLayers.Control.Panel>.
*/
type: null,
/**
* Property: allowSelection
* {Boolean} By default, controls do not allow selection, because
* it may interfere with map dragging. If this is true, OpenLayers
* will not prevent selection of the control.
* Default is false.
*/
allowSelection: false,
/**
* Property: displayClass
* {string} This property is used for CSS related to the drawing of the
* Control.
*/
displayClass: "",
/**
* APIProperty: title
* {string} This property is used for showing a tooltip over the
* Control.
*/
title: "",
/**
* APIProperty: autoActivate
* {Boolean} Activate the control when it is added to a map. Default is
* false.
*/
autoActivate: false,
/**
* APIProperty: active
* {Boolean} The control is active (read-only). Use <activate> and
* <deactivate> to change control state.
*/
active: null,
/**
* Property: handlerOptions
* {Object} Used to set non-default properties on the control's handler
*/
handlerOptions: null,
/**
* Property: handler
* {<OpenLayers.Handler>} null
*/
handler: null,
/**
* APIProperty: eventListeners
* {Object} If set as an option at construction, the eventListeners
* object will be registered with <OpenLayers.Events.on>. Object
* structure must be a listeners object as shown in the example for
* the events.on method.
*/
eventListeners: null,
/**
* APIProperty: events
* {<OpenLayers.Events>} Events instance for listeners and triggering
* control specific events.
*
* Register a listener for a particular event with the following syntax:
* (code)
* control.events.register(type, obj, listener);
* (end)
*
* Listeners will be called with a reference to an event object. The
* properties of this event depends on exactly what happened.
*
* All event objects have at least the following properties:
* object - {Object} A reference to control.events.object (a reference
* to the control).
* element - {DOMElement} A reference to control.events.element (which
* will be null unless documented otherwise).
*
* Supported map event types:
* activate - Triggered when activated.
* deactivate - Triggered when deactivated.
*/
events: null,
/**
* Constructor: OpenLayers.Control
* Create an OpenLayers Control. The options passed as a parameter
* directly extend the control. For example passing the following:
*
* > var control = new OpenLayers.Control({div: myDiv});
*
* Overrides the default div attribute value of null.
*
* Parameters:
* options - {Object}
*/
initialize: function (options) {
// We do this before the extend so that instances can override
// className in options.
this.displayClass =
this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, "");
OpenLayers.Util.extend(this, options);
this.events = new OpenLayers.Events(this);
if(this.eventListeners instanceof Object) {
this.events.on(this.eventListeners);
}
if (this.id == null) {
this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
}
},
/**
* Method: destroy
* The destroy method is used to perform any clean up before the control
* is dereferenced. Typically this is where event listeners are removed
* to prevent memory leaks.
*/
destroy: function () {
if(this.events) {
if(this.eventListeners) {
this.events.un(this.eventListeners);
}
this.events.destroy();
this.events = null;
}
this.eventListeners = null;
// eliminate circular references
if (this.handler) {
this.handler.destroy();
this.handler = null;
}
if(this.handlers) {
for(var key in this.handlers) {
if(this.handlers.hasOwnProperty(key) &&
typeof this.handlers[key].destroy == "function") {
this.handlers[key].destroy();
}
}
this.handlers = null;
}
if (this.map) {
this.map.removeControl(this);
this.map = null;
}
this.div = null;
},
/**
* Method: setMap
* Set the map property for the control. This is done through an accessor
* so that subclasses can override this and take special action once
* they have their map variable set.
*
* Parameters:
* map - {<OpenLayers.Map>}
*/
setMap: function(map) {
this.map = map;
if (this.handler) {
this.handler.setMap(map);
}
},
/**
* Method: draw
* The draw method is called when the control is ready to be displayed
* on the page. If a div has not been created one is created. Controls
* with a visual component will almost always want to override this method
* to customize the look of control.
*
* Parameters:
* px - {<OpenLayers.Pixel>} The top-left pixel position of the control
* or null.
*
* Returns:
* {DOMElement} A reference to the DIV DOMElement containing the control
*/
draw: function (px) {
if (this.div == null) {
this.div = OpenLayers.Util.createDiv(this.id);
this.div.className = this.displayClass;
if (!this.allowSelection) {
this.div.className += " olControlNoSelect";
this.div.setAttribute("unselectable", "on", 0);
this.div.onselectstart = OpenLayers.Function.False;
}
if (this.title != "") {
this.div.title = this.title;
}
}
if (px != null) {
this.position = px.clone();
}
this.moveTo(this.position);
return this.div;
},
/**
* Method: moveTo
* Sets the left and top style attributes to the passed in pixel
* coordinates.
*
* Parameters:
* px - {<OpenLayers.Pixel>}
*/
moveTo: function (px) {
if ((px != null) && (this.div != null)) {
this.div.style.left = px.x + "px";
this.div.style.top = px.y + "px";
}
},
/**
* APIMethod: activate
* Explicitly activates a control and it's associated
* handler if one has been set. Controls can be
* deactivated by calling the deactivate() method.
*
* Returns:
* {Boolean} True if the control was successfully activated or
* false if the control was already active.
*/
activate: function () {
if (this.active) {
return false;
}
if (this.handler) {
this.handler.activate();
}
this.active = true;
if(this.map) {
OpenLayers.Element.addClass(
this.map.viewPortDiv,
this.displayClass.replace(/ /g, "") + "Active"
);
}
this.events.triggerEvent("activate");
return true;
},
/**
* APIMethod: deactivate
* Deactivates a control and it's associated handler if any. The exact
* effect of this depends on the control itself.
*
* Returns:
* {Boolean} True if the control was effectively deactivated or false
* if the control was already inactive.
*/
deactivate: function () {
if (this.active) {
if (this.handler) {
this.handler.deactivate();
}
this.active = false;
if(this.map) {
OpenLayers.Element.removeClass(
this.map.viewPortDiv,
this.displayClass.replace(/ /g, "") + "Active"
);
}
this.events.triggerEvent("deactivate");
return true;
}
return false;
},
CLASS_NAME: "OpenLayers.Control"
});
/**
* Constant: OpenLayers.Control.TYPE_BUTTON
*/
OpenLayers.Control.TYPE_BUTTON = 1;
/**
* Constant: OpenLayers.Control.TYPE_TOGGLE
*/
OpenLayers.Control.TYPE_TOGGLE = 2;
/**
* Constant: OpenLayers.Control.TYPE_TOOL
*/
OpenLayers.Control.TYPE_TOOL = 3;

View File

@ -0,0 +1,182 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
*/
/**
* Class: OpenLayers.Control.ArgParser
* The ArgParser control adds location bar query string parsing functionality
* to an OpenLayers Map.
* When added to a Map control, on a page load/refresh, the Map will
* automatically take the href string and parse it for lon, lat, zoom, and
* layers information.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.ArgParser = OpenLayers.Class(OpenLayers.Control, {
/**
* Property: center
* {<OpenLayers.LonLat>}
*/
center: null,
/**
* Property: zoom
* {int}
*/
zoom: null,
/**
* Property: layers
* {String} Each character represents the state of the corresponding layer
* on the map.
*/
layers: null,
/**
* APIProperty: displayProjection
* {<OpenLayers.Projection>} Requires proj4js support.
* Projection used when reading the coordinates from the URL. This will
* reproject the map coordinates from the URL into the map's
* projection.
*
* If you are using this functionality, be aware that any permalink
* which is added to the map will determine the coordinate type which
* is read from the URL, which means you should not add permalinks with
* different displayProjections to the same map.
*/
displayProjection: null,
/**
* Constructor: OpenLayers.Control.ArgParser
*
* Parameters:
* options - {Object}
*/
/**
* Method: getParameters
*/
getParameters: function(url) {
url = url || window.location.href;
var parameters = OpenLayers.Util.getParameters(url);
// If we have an anchor in the url use it to split the url
var index = url.indexOf('#');
if (index > 0) {
// create an url to parse on the getParameters
url = '?' + url.substring(index + 1, url.length);
OpenLayers.Util.extend(parameters,
OpenLayers.Util.getParameters(url));
}
return parameters;
},
/**
* Method: setMap
* Set the map property for the control.
*
* Parameters:
* map - {<OpenLayers.Map>}
*/
setMap: function(map) {
OpenLayers.Control.prototype.setMap.apply(this, arguments);
//make sure we dont already have an arg parser attached
for(var i=0, len=this.map.controls.length; i<len; i++) {
var control = this.map.controls[i];
if ( (control != this) &&
(control.CLASS_NAME == "OpenLayers.Control.ArgParser") ) {
// If a second argparser is added to the map, then we
// override the displayProjection to be the one added to the
// map.
if (control.displayProjection != this.displayProjection) {
this.displayProjection = control.displayProjection;
}
break;
}
}
if (i == this.map.controls.length) {
var args = this.getParameters();
// Be careful to set layer first, to not trigger unnecessary layer loads
if (args.layers) {
this.layers = args.layers;
// when we add a new layer, set its visibility
this.map.events.register('addlayer', this,
this.configureLayers);
this.configureLayers();
}
if (args.lat && args.lon) {
this.center = new OpenLayers.LonLat(parseFloat(args.lon),
parseFloat(args.lat));
if (args.zoom) {
this.zoom = parseFloat(args.zoom);
}
// when we add a new baselayer to see when we can set the center
this.map.events.register('changebaselayer', this,
this.setCenter);
this.setCenter();
}
}
},
/**
* Method: setCenter
* As soon as a baseLayer has been loaded, we center and zoom
* ...and remove the handler.
*/
setCenter: function() {
if (this.map.baseLayer) {
//dont need to listen for this one anymore
this.map.events.unregister('changebaselayer', this,
this.setCenter);
if (this.displayProjection) {
this.center.transform(this.displayProjection,
this.map.getProjectionObject());
}
this.map.setCenter(this.center, this.zoom);
}
},
/**
* Method: configureLayers
* As soon as all the layers are loaded, cycle through them and
* hide or show them.
*/
configureLayers: function() {
if (this.layers.length == this.map.layers.length) {
this.map.events.unregister('addlayer', this, this.configureLayers);
for(var i=0, len=this.layers.length; i<len; i++) {
var layer = this.map.layers[i];
var c = this.layers.charAt(i);
if (c == "B") {
this.map.setBaseLayer(layer);
} else if ( (c == "T") || (c == "F") ) {
layer.setVisibility(c == "T");
}
}
}
},
CLASS_NAME: "OpenLayers.Control.ArgParser"
});

View File

@ -0,0 +1,104 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
*/
/**
* Class: OpenLayers.Control.Attribution
* The attribution control adds attribution from layers to the map display.
* It uses 'attribution' property of each layer.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.Attribution =
OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: separator
* {String} String used to separate layers.
*/
separator: ", ",
/**
* APIProperty: template
* {String} Template for the attribution. This has to include the substring
* "${layers}", which will be replaced by the layer specific
* attributions, separated by <separator>. The default is "${layers}".
*/
template: "${layers}",
/**
* Constructor: OpenLayers.Control.Attribution
*
* Parameters:
* options - {Object} Options for control.
*/
/**
* Method: destroy
* Destroy control.
*/
destroy: function() {
this.map.events.un({
"removelayer": this.updateAttribution,
"addlayer": this.updateAttribution,
"changelayer": this.updateAttribution,
"changebaselayer": this.updateAttribution,
scope: this
});
OpenLayers.Control.prototype.destroy.apply(this, arguments);
},
/**
* Method: draw
* Initialize control.
*
* Returns:
* {DOMElement} A reference to the DIV DOMElement containing the control
*/
draw: function() {
OpenLayers.Control.prototype.draw.apply(this, arguments);
this.map.events.on({
'changebaselayer': this.updateAttribution,
'changelayer': this.updateAttribution,
'addlayer': this.updateAttribution,
'removelayer': this.updateAttribution,
scope: this
});
this.updateAttribution();
return this.div;
},
/**
* Method: updateAttribution
* Update attribution string.
*/
updateAttribution: function() {
var attributions = [];
if (this.map && this.map.layers) {
for(var i=0, len=this.map.layers.length; i<len; i++) {
var layer = this.map.layers[i];
if (layer.attribution && layer.getVisibility()) {
// add attribution only if attribution text is unique
if (OpenLayers.Util.indexOf(
attributions, layer.attribution) === -1) {
attributions.push( layer.attribution );
}
}
}
this.div.innerHTML = OpenLayers.String.format(this.template, {
layers: attributions.join(this.separator)
});
}
},
CLASS_NAME: "OpenLayers.Control.Attribution"
});

View File

@ -0,0 +1,44 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
*/
/**
* Class: OpenLayers.Control.Button
* The Button control is a very simple push-button, for use with
* <OpenLayers.Control.Panel>.
* When clicked, the function trigger() is executed.
*
* Inherits from:
* - <OpenLayers.Control>
*
* Use:
* (code)
* var button = new OpenLayers.Control.Button({
* displayClass: "MyButton", trigger: myFunction
* });
* panel.addControls([button]);
* (end)
*
* Will create a button with CSS class MyButtonItemInactive, that
* will call the function MyFunction() when clicked.
*/
OpenLayers.Control.Button = OpenLayers.Class(OpenLayers.Control, {
/**
* Property: type
* {Integer} OpenLayers.Control.TYPE_BUTTON.
*/
type: OpenLayers.Control.TYPE_BUTTON,
/**
* Method: trigger
* Called by a control panel when the button is clicked.
*/
trigger: function() {},
CLASS_NAME: "OpenLayers.Control.Button"
});

View File

@ -0,0 +1,156 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
*/
/**
* Class: OpenLayers.Control.CacheRead
* A control for using image tiles cached with <OpenLayers.Control.CacheWrite>
* from the browser's local storage.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.CacheRead = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: fetchEvent
* {String} The layer event to listen to for replacing remote resource tile
* URLs with cached data URIs. Supported values are "tileerror" (try
* remote first, fall back to cached) and "tileloadstart" (try cache
* first, fall back to remote). Default is "tileloadstart".
*
* Note that "tileerror" will not work for CORS enabled images (see
* https://developer.mozilla.org/en/CORS_Enabled_Image), i.e. layers
* configured with a <OpenLayers.Tile.Image.crossOriginKeyword> in
* <OpenLayers.Layer.Grid.tileOptions>.
*/
fetchEvent: "tileloadstart",
/**
* APIProperty: layers
* {Array(<OpenLayers.Layer.Grid>)}. Optional. If provided, only these
* layers will receive tiles from the cache.
*/
layers: null,
/**
* APIProperty: autoActivate
* {Boolean} Activate the control when it is added to a map. Default is
* true.
*/
autoActivate: true,
/**
* Constructor: OpenLayers.Control.CacheRead
*
* Parameters:
* options - {Object} Object with API properties for this control
*/
/**
* Method: setMap
* Set the map property for the control.
*
* Parameters:
* map - {<OpenLayers.Map>}
*/
setMap: function(map) {
OpenLayers.Control.prototype.setMap.apply(this, arguments);
var i, layers = this.layers || map.layers;
for (i=layers.length-1; i>=0; --i) {
this.addLayer({layer: layers[i]});
}
if (!this.layers) {
map.events.on({
addlayer: this.addLayer,
removeLayer: this.removeLayer,
scope: this
});
}
},
/**
* Method: addLayer
* Adds a layer to the control. Once added, tiles requested for this layer
* will be cached.
*
* Parameters:
* evt - {Object} Object with a layer property referencing an
* <OpenLayers.Layer> instance
*/
addLayer: function(evt) {
evt.layer.events.register(this.fetchEvent, this, this.fetch);
},
/**
* Method: removeLayer
* Removes a layer from the control. Once removed, tiles requested for this
* layer will no longer be cached.
*
* Parameters:
* evt - {Object} Object with a layer property referencing an
* <OpenLayers.Layer> instance
*/
removeLayer: function(evt) {
evt.layer.events.unregister(this.fetchEvent, this, this.fetch);
},
/**
* Method: fetch
* Listener to the <fetchEvent> event. Replaces a tile's url with a data
* URI from the cache.
*
* Parameters:
* evt - {Object} Event object with a tile property.
*/
fetch: function(evt) {
if (this.active && window.localStorage &&
evt.tile instanceof OpenLayers.Tile.Image) {
var tile = evt.tile,
url = tile.url;
// deal with modified tile urls when both CacheWrite and CacheRead
// are active
if (!tile.layer.crossOriginKeyword && OpenLayers.ProxyHost &&
url.indexOf(OpenLayers.ProxyHost) === 0) {
url = OpenLayers.Control.CacheWrite.urlMap[url];
}
var dataURI = window.localStorage.getItem("olCache_" + url);
if (dataURI) {
tile.url = dataURI;
if (evt.type === "tileerror") {
tile.setImgSrc(dataURI);
}
}
}
},
/**
* Method: destroy
* The destroy method is used to perform any clean up before the control
* is dereferenced. Typically this is where event listeners are removed
* to prevent memory leaks.
*/
destroy: function() {
if (this.layers || this.map) {
var i, layers = this.layers || this.map.layers;
for (i=layers.length-1; i>=0; --i) {
this.removeLayer({layer: layers[i]});
}
}
if (this.map) {
this.map.events.un({
addlayer: this.addLayer,
removeLayer: this.removeLayer,
scope: this
});
}
OpenLayers.Control.prototype.destroy.apply(this, arguments);
},
CLASS_NAME: "OpenLayers.Control.CacheRead"
});

View File

@ -0,0 +1,257 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Request.js
* @requires OpenLayers/Console.js
*/
/**
* Class: OpenLayers.Control.CacheWrite
* A control for caching image tiles in the browser's local storage. The
* <OpenLayers.Control.CacheRead> control is used to fetch and use the cached
* tile images.
*
* Note: Before using this control on any layer that is not your own, make sure
* that the terms of service of the tile provider allow local storage of tiles.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.CacheWrite = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: events
* {<OpenLayers.Events>} Events instance for listeners and triggering
* control specific events.
*
* To register events in the constructor, configure <eventListeners>.
*
* Register a listener for a particular event with the following syntax:
* (code)
* control.events.register(type, obj, listener);
* (end)
*
* Supported event types (in addition to those from <OpenLayers.Control.events>):
* cachefull - Triggered when the cache is full. Listeners receive an
* object with a tile property as first argument. The tile references
* the tile that couldn't be cached.
*/
/**
* APIProperty: eventListeners
* {Object} Object with event listeners, keyed by event name. An optional
* scope property defines the scope that listeners will be executed in.
*/
/**
* APIProperty: layers
* {Array(<OpenLayers.Layer.Grid>)}. Optional. If provided, caching
* will be enabled for these layers only, otherwise for all cacheable
* layers.
*/
layers: null,
/**
* APIProperty: imageFormat
* {String} The image format used for caching. The default is "image/png".
* Supported formats depend on the user agent. If an unsupported
* <imageFormat> is provided, "image/png" will be used. For aerial
* imagery, "image/jpeg" is recommended.
*/
imageFormat: "image/png",
/**
* Property: quotaRegEx
* {RegExp}
*/
quotaRegEx: (/quota/i),
/**
* Constructor: OpenLayers.Control.CacheWrite
*
* Parameters:
* options - {Object} Object with API properties for this control.
*/
/**
* Method: setMap
* Set the map property for the control.
*
* Parameters:
* map - {<OpenLayers.Map>}
*/
setMap: function(map) {
OpenLayers.Control.prototype.setMap.apply(this, arguments);
var i, layers = this.layers || map.layers;
for (i=layers.length-1; i>=0; --i) {
this.addLayer({layer: layers[i]});
}
if (!this.layers) {
map.events.on({
addlayer: this.addLayer,
removeLayer: this.removeLayer,
scope: this
});
}
},
/**
* Method: addLayer
* Adds a layer to the control. Once added, tiles requested for this layer
* will be cached.
*
* Parameters:
* evt - {Object} Object with a layer property referencing an
* <OpenLayers.Layer> instance
*/
addLayer: function(evt) {
evt.layer.events.on({
tileloadstart: this.makeSameOrigin,
tileloaded: this.onTileLoaded,
scope: this
});
},
/**
* Method: removeLayer
* Removes a layer from the control. Once removed, tiles requested for this
* layer will no longer be cached.
*
* Parameters:
* evt - {Object} Object with a layer property referencing an
* <OpenLayers.Layer> instance
*/
removeLayer: function(evt) {
evt.layer.events.un({
tileloadstart: this.makeSameOrigin,
tileloaded: this.onTileLoaded,
scope: this
});
},
/**
* Method: makeSameOrigin
* If the tile does not have CORS image loading enabled and is from a
* different origin, use OpenLayers.ProxyHost to make it a same origin url.
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
makeSameOrigin: function(evt) {
if (this.active) {
var tile = evt.tile;
if (tile instanceof OpenLayers.Tile.Image &&
!tile.crossOriginKeyword &&
tile.url.substr(0, 5) !== "data:") {
var sameOriginUrl = OpenLayers.Request.makeSameOrigin(
tile.url, OpenLayers.ProxyHost
);
OpenLayers.Control.CacheWrite.urlMap[sameOriginUrl] = tile.url;
tile.url = sameOriginUrl;
}
}
},
/**
* Method: onTileLoaded
* Decides whether a tile can be cached and calls the cache method.
*
* Parameters:
* evt - {Event}
*/
onTileLoaded: function(evt) {
if (this.active && !evt.aborted &&
evt.tile instanceof OpenLayers.Tile.Image &&
evt.tile.url.substr(0, 5) !== 'data:') {
this.cache({tile: evt.tile});
delete OpenLayers.Control.CacheWrite.urlMap[evt.tile.url];
}
},
/**
* Method: cache
* Adds a tile to the cache. When the cache is full, the "cachefull" event
* is triggered.
*
* Parameters:
* obj - {Object} Object with a tile property, tile being the
* <OpenLayers.Tile.Image> with the data to add to the cache
*/
cache: function(obj) {
if (window.localStorage) {
var tile = obj.tile;
try {
var canvasContext = tile.getCanvasContext();
if (canvasContext) {
var urlMap = OpenLayers.Control.CacheWrite.urlMap;
var url = urlMap[tile.url] || tile.url;
window.localStorage.setItem(
"olCache_" + url,
canvasContext.canvas.toDataURL(this.imageFormat)
);
}
} catch(e) {
// local storage full or CORS violation
var reason = e.name || e.message;
if (reason && this.quotaRegEx.test(reason)) {
this.events.triggerEvent("cachefull", {tile: tile});
} else {
OpenLayers.Console.error(e.toString());
}
}
}
},
/**
* Method: destroy
* The destroy method is used to perform any clean up before the control
* is dereferenced. Typically this is where event listeners are removed
* to prevent memory leaks.
*/
destroy: function() {
if (this.layers || this.map) {
var i, layers = this.layers || this.map.layers;
for (i=layers.length-1; i>=0; --i) {
this.removeLayer({layer: layers[i]});
}
}
if (this.map) {
this.map.events.un({
addlayer: this.addLayer,
removeLayer: this.removeLayer,
scope: this
});
}
OpenLayers.Control.prototype.destroy.apply(this, arguments);
},
CLASS_NAME: "OpenLayers.Control.CacheWrite"
});
/**
* APIFunction: OpenLayers.Control.CacheWrite.clearCache
* Clears all tiles cached with <OpenLayers.Control.CacheWrite> from the cache.
*/
OpenLayers.Control.CacheWrite.clearCache = function() {
if (!window.localStorage) { return; }
var i, key;
for (i=window.localStorage.length-1; i>=0; --i) {
key = window.localStorage.key(i);
if (key.substr(0, 8) === "olCache_") {
window.localStorage.removeItem(key);
}
}
};
/**
* Property: OpenLayers.Control.CacheWrite.urlMap
* {Object} Mapping of same origin urls to cache url keys. Entries will be
* deleted as soon as a tile was cached.
*/
OpenLayers.Control.CacheWrite.urlMap = {};

View File

@ -0,0 +1,366 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Handler/Drag.js
* @requires OpenLayers/Handler/Feature.js
*/
/**
* Class: OpenLayers.Control.DragFeature
* The DragFeature control moves a feature with a drag of the mouse. Create a
* new control with the <OpenLayers.Control.DragFeature> constructor.
*
* Inherits From:
* - <OpenLayers.Control>
*/
OpenLayers.Control.DragFeature = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: geometryTypes
* {Array(String)} To restrict dragging to a limited set of geometry types,
* send a list of strings corresponding to the geometry class names.
*/
geometryTypes: null,
/**
* APIProperty: onStart
* {Function} Define this function if you want to know when a drag starts.
* The function should expect to receive two arguments: the feature
* that is about to be dragged and the pixel location of the mouse.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>} The feature that is about to be
* dragged.
* pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.
*/
onStart: function(feature, pixel) {},
/**
* APIProperty: onDrag
* {Function} Define this function if you want to know about each move of a
* feature. The function should expect to receive two arguments: the
* feature that is being dragged and the pixel location of the mouse.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>} The feature that was dragged.
* pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.
*/
onDrag: function(feature, pixel) {},
/**
* APIProperty: onComplete
* {Function} Define this function if you want to know when a feature is
* done dragging. The function should expect to receive two arguments:
* the feature that is being dragged and the pixel location of the
* mouse.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>} The feature that was dragged.
* pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.
*/
onComplete: function(feature, pixel) {},
/**
* APIProperty: onEnter
* {Function} Define this function if you want to know when the mouse
* goes over a feature and thereby makes this feature a candidate
* for dragging.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>} The feature that is ready
* to be dragged.
*/
onEnter: function(feature) {},
/**
* APIProperty: onLeave
* {Function} Define this function if you want to know when the mouse
* goes out of the feature that was dragged.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>} The feature that was dragged.
*/
onLeave: function(feature) {},
/**
* APIProperty: documentDrag
* {Boolean} If set to true, mouse dragging will continue even if the
* mouse cursor leaves the map viewport. Default is false.
*/
documentDrag: false,
/**
* Property: layer
* {<OpenLayers.Layer.Vector>}
*/
layer: null,
/**
* Property: feature
* {<OpenLayers.Feature.Vector>}
*/
feature: null,
/**
* Property: dragCallbacks
* {Object} The functions that are sent to the drag handler for callback.
*/
dragCallbacks: {},
/**
* Property: featureCallbacks
* {Object} The functions that are sent to the feature handler for callback.
*/
featureCallbacks: {},
/**
* Property: lastPixel
* {<OpenLayers.Pixel>}
*/
lastPixel: null,
/**
* Constructor: OpenLayers.Control.DragFeature
* Create a new control to drag features.
*
* Parameters:
* layer - {<OpenLayers.Layer.Vector>} The layer containing features to be
* dragged.
* options - {Object} Optional object whose properties will be set on the
* control.
*/
initialize: function(layer, options) {
OpenLayers.Control.prototype.initialize.apply(this, [options]);
this.layer = layer;
this.handlers = {
drag: new OpenLayers.Handler.Drag(
this, OpenLayers.Util.extend({
down: this.downFeature,
move: this.moveFeature,
up: this.upFeature,
out: this.cancel,
done: this.doneDragging
}, this.dragCallbacks), {
documentDrag: this.documentDrag
}
),
feature: new OpenLayers.Handler.Feature(
this, this.layer, OpenLayers.Util.extend({
// 'click' and 'clickout' callback are for the mobile
// support: no 'over' or 'out' in touch based browsers.
click: this.clickFeature,
clickout: this.clickoutFeature,
over: this.overFeature,
out: this.outFeature
}, this.featureCallbacks),
{geometryTypes: this.geometryTypes}
)
};
},
/**
* Method: clickFeature
* Called when the feature handler detects a click-in on a feature.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
*/
clickFeature: function(feature) {
if (this.handlers.feature.touch && !this.over && this.overFeature(feature)) {
this.handlers.drag.dragstart(this.handlers.feature.evt);
// to let the events propagate to the feature handler (click callback)
this.handlers.drag.stopDown = false;
}
},
/**
* Method: clickoutFeature
* Called when the feature handler detects a click-out on a feature.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
*/
clickoutFeature: function(feature) {
if (this.handlers.feature.touch && this.over) {
this.outFeature(feature);
this.handlers.drag.stopDown = true;
}
},
/**
* APIMethod: destroy
* Take care of things that are not handled in superclass
*/
destroy: function() {
this.layer = null;
OpenLayers.Control.prototype.destroy.apply(this, []);
},
/**
* APIMethod: activate
* Activate the control and the feature handler.
*
* Returns:
* {Boolean} Successfully activated the control and feature handler.
*/
activate: function() {
return (this.handlers.feature.activate() &&
OpenLayers.Control.prototype.activate.apply(this, arguments));
},
/**
* APIMethod: deactivate
* Deactivate the control and all handlers.
*
* Returns:
* {Boolean} Successfully deactivated the control.
*/
deactivate: function() {
// the return from the handlers is unimportant in this case
this.handlers.drag.deactivate();
this.handlers.feature.deactivate();
this.feature = null;
this.dragging = false;
this.lastPixel = null;
OpenLayers.Element.removeClass(
this.map.viewPortDiv, this.displayClass + "Over"
);
return OpenLayers.Control.prototype.deactivate.apply(this, arguments);
},
/**
* Method: overFeature
* Called when the feature handler detects a mouse-over on a feature.
* This activates the drag handler.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>} The selected feature.
*
* Returns:
* {Boolean} Successfully activated the drag handler.
*/
overFeature: function(feature) {
var activated = false;
if(!this.handlers.drag.dragging) {
this.feature = feature;
this.handlers.drag.activate();
activated = true;
this.over = true;
OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass + "Over");
this.onEnter(feature);
} else {
if(this.feature.id == feature.id) {
this.over = true;
} else {
this.over = false;
}
}
return activated;
},
/**
* Method: downFeature
* Called when the drag handler detects a mouse-down.
*
* Parameters:
* pixel - {<OpenLayers.Pixel>} Location of the mouse event.
*/
downFeature: function(pixel) {
this.lastPixel = pixel;
this.onStart(this.feature, pixel);
},
/**
* Method: moveFeature
* Called when the drag handler detects a mouse-move. Also calls the
* optional onDrag method.
*
* Parameters:
* pixel - {<OpenLayers.Pixel>} Location of the mouse event.
*/
moveFeature: function(pixel) {
var res = this.map.getResolution();
this.feature.geometry.move(res * (pixel.x - this.lastPixel.x),
res * (this.lastPixel.y - pixel.y));
this.layer.drawFeature(this.feature);
this.lastPixel = pixel;
this.onDrag(this.feature, pixel);
},
/**
* Method: upFeature
* Called when the drag handler detects a mouse-up.
*
* Parameters:
* pixel - {<OpenLayers.Pixel>} Location of the mouse event.
*/
upFeature: function(pixel) {
if(!this.over) {
this.handlers.drag.deactivate();
}
},
/**
* Method: doneDragging
* Called when the drag handler is done dragging.
*
* Parameters:
* pixel - {<OpenLayers.Pixel>} The last event pixel location. If this event
* came from a mouseout, this may not be in the map viewport.
*/
doneDragging: function(pixel) {
this.onComplete(this.feature, pixel);
},
/**
* Method: outFeature
* Called when the feature handler detects a mouse-out on a feature.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>} The feature that the mouse left.
*/
outFeature: function(feature) {
if(!this.handlers.drag.dragging) {
this.over = false;
this.handlers.drag.deactivate();
OpenLayers.Element.removeClass(
this.map.viewPortDiv, this.displayClass + "Over"
);
this.onLeave(feature);
this.feature = null;
} else {
if(this.feature.id == feature.id) {
this.over = false;
}
}
},
/**
* Method: cancel
* Called when the drag handler detects a mouse-out (from the map viewport).
*/
cancel: function() {
this.handlers.drag.deactivate();
this.over = false;
},
/**
* Method: setMap
* Set the map property for the control and all handlers.
*
* Parameters:
* map - {<OpenLayers.Map>} The control's map.
*/
setMap: function(map) {
this.handlers.drag.setMap(map);
this.handlers.feature.setMap(map);
OpenLayers.Control.prototype.setMap.apply(this, arguments);
},
CLASS_NAME: "OpenLayers.Control.DragFeature"
});

View File

@ -0,0 +1,156 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Handler/Drag.js
*/
/**
* Class: OpenLayers.Control.DragPan
* The DragPan control pans the map with a drag of the mouse.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {
/**
* Property: type
* {OpenLayers.Control.TYPES}
*/
type: OpenLayers.Control.TYPE_TOOL,
/**
* Property: panned
* {Boolean} The map moved.
*/
panned: false,
/**
* Property: interval
* {Integer} The number of milliseconds that should ellapse before
* panning the map again. Defaults to 0 milliseconds, which means that
* no separate cycle is used for panning. In most cases you won't want
* to change this value. For slow machines/devices larger values can be
* tried out.
*/
interval: 0,
/**
* APIProperty: documentDrag
* {Boolean} If set to true, mouse dragging will continue even if the
* mouse cursor leaves the map viewport. Default is false.
*/
documentDrag: false,
/**
* Property: kinetic
* {<OpenLayers.Kinetic>} The OpenLayers.Kinetic object.
*/
kinetic: null,
/**
* APIProperty: enableKinetic
* {Boolean} Set this option to enable "kinetic dragging". Can be
* set to true or to an object. If set to an object this
* object will be passed to the {<OpenLayers.Kinetic>}
* constructor. Defaults to true.
* To get kinetic dragging, ensure that OpenLayers/Kinetic.js is
* included in your build config.
*/
enableKinetic: true,
/**
* APIProperty: kineticInterval
* {Integer} Interval in milliseconds between 2 steps in the "kinetic
* scrolling". Applies only if enableKinetic is set. Defaults
* to 10 milliseconds.
*/
kineticInterval: 10,
/**
* Method: draw
* Creates a Drag handler, using <panMap> and
* <panMapDone> as callbacks.
*/
draw: function() {
if (this.enableKinetic && OpenLayers.Kinetic) {
var config = {interval: this.kineticInterval};
if(typeof this.enableKinetic === "object") {
config = OpenLayers.Util.extend(config, this.enableKinetic);
}
this.kinetic = new OpenLayers.Kinetic(config);
}
this.handler = new OpenLayers.Handler.Drag(this, {
"move": this.panMap,
"done": this.panMapDone,
"down": this.panMapStart
}, {
interval: this.interval,
documentDrag: this.documentDrag
}
);
},
/**
* Method: panMapStart
*/
panMapStart: function() {
if(this.kinetic) {
this.kinetic.begin();
}
},
/**
* Method: panMap
*
* Parameters:
* xy - {<OpenLayers.Pixel>} Pixel of the mouse position
*/
panMap: function(xy) {
if(this.kinetic) {
this.kinetic.update(xy);
}
this.panned = true;
this.map.pan(
this.handler.last.x - xy.x,
this.handler.last.y - xy.y,
{dragging: true, animate: false}
);
},
/**
* Method: panMapDone
* Finish the panning operation. Only call setCenter (through <panMap>)
* if the map has actually been moved.
*
* Parameters:
* xy - {<OpenLayers.Pixel>} Pixel of the mouse position
*/
panMapDone: function(xy) {
if(this.panned) {
var res = null;
if (this.kinetic) {
res = this.kinetic.end(xy);
}
this.map.pan(
this.handler.last.x - xy.x,
this.handler.last.y - xy.y,
{dragging: !!res, animate: false}
);
if (res) {
var self = this;
this.kinetic.move(res, function(x, y, end) {
self.map.pan(x, y, {dragging: !end, animate: false});
});
}
this.panned = false;
}
},
CLASS_NAME: "OpenLayers.Control.DragPan"
});

View File

@ -0,0 +1,229 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Feature/Vector.js
*/
/**
* Class: OpenLayers.Control.DrawFeature
* The DrawFeature control draws point, line or polygon features on a vector
* layer when active.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, {
/**
* Property: layer
* {<OpenLayers.Layer.Vector>}
*/
layer: null,
/**
* Property: callbacks
* {Object} The functions that are sent to the handler for callback
*/
callbacks: null,
/**
* APIProperty: events
* {<OpenLayers.Events>} Events instance for listeners and triggering
* control specific events.
*
* Register a listener for a particular event with the following syntax:
* (code)
* control.events.register(type, obj, listener);
* (end)
*
* Supported event types (in addition to those from <OpenLayers.Control.events>):
* featureadded - Triggered when a feature is added
*/
/**
* APIProperty: multi
* {Boolean} Cast features to multi-part geometries before passing to the
* layer. Default is false.
*/
multi: false,
/**
* APIProperty: featureAdded
* {Function} Called after each feature is added
*/
featureAdded: function() {},
/**
* APIProperty: handlerOptions
* {Object} Used to set non-default properties on the control's handler
*/
/**
* Constructor: OpenLayers.Control.DrawFeature
*
* Parameters:
* layer - {<OpenLayers.Layer.Vector>}
* handler - {<OpenLayers.Handler>}
* options - {Object}
*/
initialize: function(layer, handler, options) {
OpenLayers.Control.prototype.initialize.apply(this, [options]);
this.callbacks = OpenLayers.Util.extend(
{
done: this.drawFeature,
modify: function(vertex, feature) {
this.layer.events.triggerEvent(
"sketchmodified", {vertex: vertex, feature: feature}
);
},
create: function(vertex, feature) {
this.layer.events.triggerEvent(
"sketchstarted", {vertex: vertex, feature: feature}
);
}
},
this.callbacks
);
this.layer = layer;
this.handlerOptions = this.handlerOptions || {};
this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(
this.handlerOptions.layerOptions, {
renderers: layer.renderers, rendererOptions: layer.rendererOptions
}
);
if (!("multi" in this.handlerOptions)) {
this.handlerOptions.multi = this.multi;
}
var sketchStyle = this.layer.styleMap && this.layer.styleMap.styles.temporary;
if(sketchStyle) {
this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(
this.handlerOptions.layerOptions,
{styleMap: new OpenLayers.StyleMap({"default": sketchStyle})}
);
}
this.handler = new handler(this, this.callbacks, this.handlerOptions);
},
/**
* Method: drawFeature
*/
drawFeature: function(geometry) {
var feature = new OpenLayers.Feature.Vector(geometry);
var proceed = this.layer.events.triggerEvent(
"sketchcomplete", {feature: feature}
);
if(proceed !== false) {
feature.state = OpenLayers.State.INSERT;
this.layer.addFeatures([feature]);
this.featureAdded(feature);
this.events.triggerEvent("featureadded",{feature : feature});
}
},
/**
* APIMethod: insertXY
* Insert a point in the current sketch given x & y coordinates.
*
* Parameters:
* x - {Number} The x-coordinate of the point.
* y - {Number} The y-coordinate of the point.
*/
insertXY: function(x, y) {
if (this.handler && this.handler.line) {
this.handler.insertXY(x, y);
}
},
/**
* APIMethod: insertDeltaXY
* Insert a point given offsets from the previously inserted point.
*
* Parameters:
* dx - {Number} The x-coordinate offset of the point.
* dy - {Number} The y-coordinate offset of the point.
*/
insertDeltaXY: function(dx, dy) {
if (this.handler && this.handler.line) {
this.handler.insertDeltaXY(dx, dy);
}
},
/**
* APIMethod: insertDirectionLength
* Insert a point in the current sketch given a direction and a length.
*
* Parameters:
* direction - {Number} Degrees clockwise from the positive x-axis.
* length - {Number} Distance from the previously drawn point.
*/
insertDirectionLength: function(direction, length) {
if (this.handler && this.handler.line) {
this.handler.insertDirectionLength(direction, length);
}
},
/**
* APIMethod: insertDeflectionLength
* Insert a point in the current sketch given a deflection and a length.
* The deflection should be degrees clockwise from the previously
* digitized segment.
*
* Parameters:
* deflection - {Number} Degrees clockwise from the previous segment.
* length - {Number} Distance from the previously drawn point.
*/
insertDeflectionLength: function(deflection, length) {
if (this.handler && this.handler.line) {
this.handler.insertDeflectionLength(deflection, length);
}
},
/**
* APIMethod: undo
* Remove the most recently added point in the current sketch geometry.
*
* Returns:
* {Boolean} An edit was undone.
*/
undo: function() {
return this.handler.undo && this.handler.undo();
},
/**
* APIMethod: redo
* Reinsert the most recently removed point resulting from an <undo> call.
* The undo stack is deleted whenever a point is added by other means.
*
* Returns:
* {Boolean} An edit was redone.
*/
redo: function() {
return this.handler.redo && this.handler.redo();
},
/**
* APIMethod: finishSketch
* Finishes the sketch without including the currently drawn point.
* This method can be called to terminate drawing programmatically
* instead of waiting for the user to end the sketch.
*/
finishSketch: function() {
this.handler.finishGeometry();
},
/**
* APIMethod: cancel
* Cancel the current sketch. This removes the current sketch and keeps
* the drawing control active.
*/
cancel: function() {
this.handler.cancel();
},
CLASS_NAME: "OpenLayers.Control.DrawFeature"
});

View File

@ -0,0 +1,81 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control/Panel.js
* @requires OpenLayers/Control/Navigation.js
* @requires OpenLayers/Control/DrawFeature.js
* @requires OpenLayers/Handler/Point.js
* @requires OpenLayers/Handler/Path.js
* @requires OpenLayers/Handler/Polygon.js
*/
/**
* Class: OpenLayers.Control.EditingToolbar
* The EditingToolbar is a panel of 4 controls to draw polygons, lines,
* points, or to navigate the map by panning. By default it appears in the
* upper right corner of the map.
*
* Inherits from:
* - <OpenLayers.Control.Panel>
*/
OpenLayers.Control.EditingToolbar = OpenLayers.Class(
OpenLayers.Control.Panel, {
/**
* APIProperty: citeCompliant
* {Boolean} If set to true, coordinates of features drawn in a map extent
* crossing the date line won't exceed the world bounds. Default is false.
*/
citeCompliant: false,
/**
* Constructor: OpenLayers.Control.EditingToolbar
* Create an editing toolbar for a given layer.
*
* Parameters:
* layer - {<OpenLayers.Layer.Vector>}
* options - {Object}
*/
initialize: function(layer, options) {
OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);
this.addControls(
[ new OpenLayers.Control.Navigation() ]
);
var controls = [
new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, {
displayClass: 'olControlDrawFeaturePoint',
handlerOptions: {citeCompliant: this.citeCompliant}
}),
new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, {
displayClass: 'olControlDrawFeaturePath',
handlerOptions: {citeCompliant: this.citeCompliant}
}),
new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Polygon, {
displayClass: 'olControlDrawFeaturePolygon',
handlerOptions: {citeCompliant: this.citeCompliant}
})
];
this.addControls(controls);
},
/**
* Method: draw
* calls the default draw, and then activates mouse defaults.
*
* Returns:
* {DOMElement}
*/
draw: function() {
var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);
if (this.defaultControl === null) {
this.defaultControl = this.controls[0];
}
return div;
},
CLASS_NAME: "OpenLayers.Control.EditingToolbar"
});

View File

@ -0,0 +1,192 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Geometry/Point.js
* @requires OpenLayers/Projection.js
*/
/**
* Class: OpenLayers.Control.Geolocate
* The Geolocate control wraps w3c geolocation API into control that can be
* bound to a map, and generate events on location update
*
* To use this control requires to load the proj4js library if the projection
* of the map is not EPSG:4326 or EPSG:900913.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.Geolocate = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: events
* {<OpenLayers.Events>} Events instance for listeners and triggering
* control specific events.
*
* Register a listener for a particular event with the following syntax:
* (code)
* control.events.register(type, obj, listener);
* (end)
*
* Supported event types (in addition to those from <OpenLayers.Control.events>):
* locationupdated - Triggered when browser return a new position. Listeners will
* receive an object with a 'position' property which is the browser.geolocation.position
* native object, as well as a 'point' property which is the location transformed in the
* current map projection.
* locationfailed - Triggered when geolocation has failed
* locationuncapable - Triggered when control is activated on a browser
* which doesn't support geolocation
*/
/**
* Property: geolocation
* {Object} The geolocation engine, as a property to be possibly mocked.
* This is set lazily to avoid a memory leak in IE9.
*/
geolocation: null,
/**
* Property: available
* {Boolean} The navigator.geolocation object is available.
*/
available: ('geolocation' in navigator),
/**
* APIProperty: bind
* {Boolean} If true, map center will be set on location update.
*/
bind: true,
/**
* APIProperty: watch
* {Boolean} If true, position will be update regularly.
*/
watch: false,
/**
* APIProperty: geolocationOptions
* {Object} Options to pass to the navigator's geolocation API. See
* <http://dev.w3.org/geo/api/spec-source.html>. No specific
* option is passed to the geolocation API by default.
*/
geolocationOptions: null,
/**
* Constructor: OpenLayers.Control.Geolocate
* Create a new control to deal with browser geolocation API
*
*/
/**
* Method: destroy
*/
destroy: function() {
this.deactivate();
OpenLayers.Control.prototype.destroy.apply(this, arguments);
},
/**
* Method: activate
* Activates the control.
*
* Returns:
* {Boolean} The control was effectively activated.
*/
activate: function () {
if (this.available && !this.geolocation) {
// set lazily to avoid IE9 memory leak
this.geolocation = navigator.geolocation;
}
if (!this.geolocation) {
this.events.triggerEvent("locationuncapable");
return false;
}
if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {
if (this.watch) {
this.watchId = this.geolocation.watchPosition(
OpenLayers.Function.bind(this.geolocate, this),
OpenLayers.Function.bind(this.failure, this),
this.geolocationOptions
);
} else {
this.getCurrentLocation();
}
return true;
}
return false;
},
/**
* Method: deactivate
* Deactivates the control.
*
* Returns:
* {Boolean} The control was effectively deactivated.
*/
deactivate: function () {
if (this.active && this.watchId !== null) {
this.geolocation.clearWatch(this.watchId);
}
return OpenLayers.Control.prototype.deactivate.apply(
this, arguments
);
},
/**
* Method: geolocate
* Activates the control.
*
*/
geolocate: function (position) {
var center = new OpenLayers.LonLat(
position.coords.longitude,
position.coords.latitude
).transform(
new OpenLayers.Projection("EPSG:4326"),
this.map.getProjectionObject()
);
if (this.bind) {
this.map.setCenter(center);
}
this.events.triggerEvent("locationupdated", {
position: position,
point: new OpenLayers.Geometry.Point(
center.lon, center.lat
)
});
},
/**
* APIMethod: getCurrentLocation
*
* Returns:
* {Boolean} Returns true if a event will be fired (successfull
* registration)
*/
getCurrentLocation: function() {
if (!this.active || this.watch) {
return false;
}
this.geolocation.getCurrentPosition(
OpenLayers.Function.bind(this.geolocate, this),
OpenLayers.Function.bind(this.failure, this),
this.geolocationOptions
);
return true;
},
/**
* Method: failure
* method called on browser's geolocation failure
*
*/
failure: function (error) {
this.events.triggerEvent("locationfailed", {error: error});
},
CLASS_NAME: "OpenLayers.Control.Geolocate"
});

View File

@ -0,0 +1,597 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Handler/Click.js
* @requires OpenLayers/Handler/Box.js
* @requires OpenLayers/Handler/Hover.js
* @requires OpenLayers/Filter/Spatial.js
*/
/**
* Class: OpenLayers.Control.GetFeature
* Gets vector features for locations underneath the mouse cursor. Can be
* configured to act on click, hover or dragged boxes. Uses an
* <OpenLayers.Protocol> that supports spatial filters to retrieve
* features from a server and fires events that notify applications of the
* selected features.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.GetFeature = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: protocol
* {<OpenLayers.Protocol>} Required. The protocol used for fetching
* features.
*/
protocol: null,
/**
* APIProperty: multipleKey
* {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
* the <multiple> property to true. Default is null.
*/
multipleKey: null,
/**
* APIProperty: toggleKey
* {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
* the <toggle> property to true. Default is null.
*/
toggleKey: null,
/**
* Property: modifiers
* {Object} The event modifiers to use, according to the current event
* being handled by this control's handlers
*/
modifiers: null,
/**
* APIProperty: multiple
* {Boolean} Allow selection of multiple geometries. Default is false.
*/
multiple: false,
/**
* APIProperty: click
* {Boolean} Use a click handler for selecting/unselecting features. If
* both <click> and <box> are set to true, the click handler takes
* precedence over the box handler if a box with zero extent was
* selected. Default is true.
*/
click: true,
/**
* APIProperty: single
* {Boolean} Tells whether select by click should select a single
* feature. If set to false, all matching features are selected.
* If set to true, only the best matching feature is selected.
* This option has an effect only of the <click> option is set
* to true. Default is true.
*/
single: true,
/**
* APIProperty: clickout
* {Boolean} Unselect features when clicking outside any feature.
* Applies only if <click> is true. Default is true.
*/
clickout: true,
/**
* APIProperty: toggle
* {Boolean} Unselect a selected feature on click. Applies only if
* <click> is true. Default is false.
*/
toggle: false,
/**
* APIProperty: clickTolerance
* {Integer} Tolerance for the filter query in pixels. This has the
* same effect as the tolerance parameter on WMS GetFeatureInfo
* requests. Will be ignored for box selections. Applies only if
* <click> or <hover> is true. Default is 5. Note that this not
* only affects requests on click, but also on hover.
*/
clickTolerance: 5,
/**
* APIProperty: hover
* {Boolean} Send feature requests on mouse moves. Default is false.
*/
hover: false,
/**
* APIProperty: box
* {Boolean} Allow feature selection by drawing a box. If set to
* true set <click> to false to disable the click handler and
* rely on the box handler only, even for "zero extent" boxes.
* See the description of the <click> option for additional
* information. Default is false.
*/
box: false,
/**
* APIProperty: maxFeatures
* {Integer} Maximum number of features to return from a query in single mode
* if supported by the <protocol>. This set of features is then used to
* determine the best match client-side. Default is 10.
*/
maxFeatures: 10,
/**
* Property: features
* {Object} Hash of {<OpenLayers.Feature.Vector>}, keyed by fid, holding
* the currently selected features
*/
features: null,
/**
* Proeprty: hoverFeature
* {<OpenLayers.Feature.Vector>} The feature currently selected by the
* hover handler
*/
hoverFeature: null,
/**
* APIProperty: handlerOptions
* {Object} Additional options for the handlers used by this control. This
* is a hash with the keys "click", "box" and "hover".
*/
/**
* Property: handlers
* {Object} Object with references to multiple <OpenLayers.Handler>
* instances.
*/
handlers: null,
/**
* Property: hoverResponse
* {<OpenLayers.Protocol.Response>} The response object associated with
* the currently running hover request (if any).
*/
hoverResponse: null,
/**
* Property: filterType
* {<String>} The type of filter to use when sending off a request.
* Possible values:
* OpenLayers.Filter.Spatial.<BBOX|INTERSECTS|WITHIN|CONTAINS>
* Defaults to: OpenLayers.Filter.Spatial.BBOX
*/
filterType: OpenLayers.Filter.Spatial.BBOX,
/**
* APIProperty: events
* {<OpenLayers.Events>} Events instance for listeners and triggering
* control specific events.
*
* Register a listener for a particular event with the following syntax:
* (code)
* control.events.register(type, obj, listener);
* (end)
*
* Supported event types (in addition to those from <OpenLayers.Control.events>):
* beforefeatureselected - Triggered when <click> is true before a
* feature is selected. The event object has a feature property with
* the feature about to select
* featureselected - Triggered when <click> is true and a feature is
* selected. The event object has a feature property with the
* selected feature
* beforefeaturesselected - Triggered when <click> is true before a
* set of features is selected. The event object is an array of
* feature properties with the features about to be selected.
* Return false after receiving this event to discontinue processing
* of all featureselected events and the featuresselected event.
* featuresselected - Triggered when <click> is true and a set of
* features is selected. The event object is an array of feature
* properties of the selected features
* featureunselected - Triggered when <click> is true and a feature is
* unselected. The event object has a feature property with the
* unselected feature
* clickout - Triggered when when <click> is true and no feature was
* selected.
* hoverfeature - Triggered when <hover> is true and the mouse has
* stopped over a feature
* outfeature - Triggered when <hover> is true and the mouse moves
* moved away from a hover-selected feature
*/
/**
* Constructor: OpenLayers.Control.GetFeature
* Create a new control for fetching remote features.
*
* Parameters:
* options - {Object} A configuration object which at least has to contain
* a <protocol> property (if not, it has to be set before a request is
* made)
*/
initialize: function(options) {
options.handlerOptions = options.handlerOptions || {};
OpenLayers.Control.prototype.initialize.apply(this, [options]);
this.features = {};
this.handlers = {};
if(this.click) {
this.handlers.click = new OpenLayers.Handler.Click(this,
{click: this.selectClick}, this.handlerOptions.click || {});
}
if(this.box) {
this.handlers.box = new OpenLayers.Handler.Box(
this, {done: this.selectBox},
OpenLayers.Util.extend(this.handlerOptions.box, {
boxDivClassName: "olHandlerBoxSelectFeature"
})
);
}
if(this.hover) {
this.handlers.hover = new OpenLayers.Handler.Hover(
this, {'move': this.cancelHover, 'pause': this.selectHover},
OpenLayers.Util.extend(this.handlerOptions.hover, {
'delay': 250,
'pixelTolerance': 2
})
);
}
},
/**
* Method: activate
* Activates the control.
*
* Returns:
* {Boolean} The control was effectively activated.
*/
activate: function () {
if (!this.active) {
for(var i in this.handlers) {
this.handlers[i].activate();
}
}
return OpenLayers.Control.prototype.activate.apply(
this, arguments
);
},
/**
* Method: deactivate
* Deactivates the control.
*
* Returns:
* {Boolean} The control was effectively deactivated.
*/
deactivate: function () {
if (this.active) {
for(var i in this.handlers) {
this.handlers[i].deactivate();
}
}
return OpenLayers.Control.prototype.deactivate.apply(
this, arguments
);
},
/**
* Method: selectClick
* Called on click
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
selectClick: function(evt) {
var bounds = this.pixelToBounds(evt.xy);
this.setModifiers(evt);
this.request(bounds, {single: this.single});
},
/**
* Method: selectBox
* Callback from the handlers.box set up when <box> selection is on
*
* Parameters:
* position - {<OpenLayers.Bounds>|Object} An OpenLayers.Bounds or
* an object with a 'left', 'bottom', 'right' and 'top' properties.
*/
selectBox: function(position) {
var bounds;
if (position instanceof OpenLayers.Bounds) {
var minXY = this.map.getLonLatFromPixel({
x: position.left,
y: position.bottom
});
var maxXY = this.map.getLonLatFromPixel({
x: position.right,
y: position.top
});
bounds = new OpenLayers.Bounds(
minXY.lon, minXY.lat, maxXY.lon, maxXY.lat
);
} else {
if(this.click) {
// box without extent - let the click handler take care of it
return;
}
bounds = this.pixelToBounds(position);
}
this.setModifiers(this.handlers.box.dragHandler.evt);
this.request(bounds);
},
/**
* Method: selectHover
* Callback from the handlers.hover set up when <hover> selection is on
*
* Parameters:
* evt - {Object} event object with an xy property
*/
selectHover: function(evt) {
var bounds = this.pixelToBounds(evt.xy);
this.request(bounds, {single: true, hover: true});
},
/**
* Method: cancelHover
* Callback from the handlers.hover set up when <hover> selection is on
*/
cancelHover: function() {
if (this.hoverResponse) {
this.protocol.abort(this.hoverResponse);
this.hoverResponse = null;
OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
}
},
/**
* Method: request
* Sends a GetFeature request to the WFS
*
* Parameters:
* bounds - {<OpenLayers.Bounds>} bounds for the request's BBOX filter
* options - {Object} additional options for this method.
*
* Supported options include:
* single - {Boolean} A single feature should be returned.
* Note that this will be ignored if the protocol does not
* return the geometries of the features.
* hover - {Boolean} Do the request for the hover handler.
*/
request: function(bounds, options) {
options = options || {};
var filter = new OpenLayers.Filter.Spatial({
type: this.filterType,
value: bounds
});
// Set the cursor to "wait" to tell the user we're working.
OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait");
var response = this.protocol.read({
maxFeatures: options.single == true ? this.maxFeatures : undefined,
filter: filter,
callback: function(result) {
if(result.success()) {
if(result.features.length) {
if(options.single == true) {
this.selectBestFeature(result.features,
bounds.getCenterLonLat(), options);
} else {
this.select(result.features);
}
} else if(options.hover) {
this.hoverSelect();
} else {
this.events.triggerEvent("clickout");
if(this.clickout) {
this.unselectAll();
}
}
}
// Reset the cursor.
OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
},
scope: this
});
if(options.hover == true) {
this.hoverResponse = response;
}
},
/**
* Method: selectBestFeature
* Selects the feature from an array of features that is the best match
* for the click position.
*
* Parameters:
* features - {Array(<OpenLayers.Feature.Vector>)}
* clickPosition - {<OpenLayers.LonLat>}
* options - {Object} additional options for this method
*
* Supported options include:
* hover - {Boolean} Do the selection for the hover handler.
*/
selectBestFeature: function(features, clickPosition, options) {
options = options || {};
if(features.length) {
var point = new OpenLayers.Geometry.Point(clickPosition.lon,
clickPosition.lat);
var feature, resultFeature, dist;
var minDist = Number.MAX_VALUE;
for(var i=0; i<features.length; ++i) {
feature = features[i];
if(feature.geometry) {
dist = point.distanceTo(feature.geometry, {edge: false});
if(dist < minDist) {
minDist = dist;
resultFeature = feature;
if(minDist == 0) {
break;
}
}
}
}
if(options.hover == true) {
this.hoverSelect(resultFeature);
} else {
this.select(resultFeature || features);
}
}
},
/**
* Method: setModifiers
* Sets the multiple and toggle modifiers according to the current event
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
setModifiers: function(evt) {
this.modifiers = {
multiple: this.multiple || (this.multipleKey && evt[this.multipleKey]),
toggle: this.toggle || (this.toggleKey && evt[this.toggleKey])
};
},
/**
* Method: select
* Add feature to the hash of selected features and trigger the
* featureselected and featuresselected events.
*
* Parameters:
* features - {<OpenLayers.Feature.Vector>} or an array of features
*/
select: function(features) {
if(!this.modifiers.multiple && !this.modifiers.toggle) {
this.unselectAll();
}
if(!(OpenLayers.Util.isArray(features))) {
features = [features];
}
var cont = this.events.triggerEvent("beforefeaturesselected", {
features: features
});
if(cont !== false) {
var selectedFeatures = [];
var feature;
for(var i=0, len=features.length; i<len; ++i) {
feature = features[i];
if(this.features[feature.fid || feature.id]) {
if(this.modifiers.toggle) {
this.unselect(this.features[feature.fid || feature.id]);
}
} else {
cont = this.events.triggerEvent("beforefeatureselected", {
feature: feature
});
if(cont !== false) {
this.features[feature.fid || feature.id] = feature;
selectedFeatures.push(feature);
this.events.triggerEvent("featureselected",
{feature: feature});
}
}
}
this.events.triggerEvent("featuresselected", {
features: selectedFeatures
});
}
},
/**
* Method: hoverSelect
* Sets/unsets the <hoverFeature>
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>} the feature to hover-select.
* If none is provided, the current <hoverFeature> will be nulled and
* the outfeature event will be triggered.
*/
hoverSelect: function(feature) {
var fid = feature ? feature.fid || feature.id : null;
var hfid = this.hoverFeature ?
this.hoverFeature.fid || this.hoverFeature.id : null;
if(hfid && hfid != fid) {
this.events.triggerEvent("outfeature",
{feature: this.hoverFeature});
this.hoverFeature = null;
}
if(fid && fid != hfid) {
this.events.triggerEvent("hoverfeature", {feature: feature});
this.hoverFeature = feature;
}
},
/**
* Method: unselect
* Remove feature from the hash of selected features and trigger the
* featureunselected event.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
*/
unselect: function(feature) {
delete this.features[feature.fid || feature.id];
this.events.triggerEvent("featureunselected", {feature: feature});
},
/**
* Method: unselectAll
* Unselect all selected features.
*/
unselectAll: function() {
// we'll want an option to supress notification here
for(var fid in this.features) {
this.unselect(this.features[fid]);
}
},
/**
* Method: setMap
* Set the map property for the control.
*
* Parameters:
* map - {<OpenLayers.Map>}
*/
setMap: function(map) {
for(var i in this.handlers) {
this.handlers[i].setMap(map);
}
OpenLayers.Control.prototype.setMap.apply(this, arguments);
},
/**
* Method: pixelToBounds
* Takes a pixel as argument and creates bounds after adding the
* <clickTolerance>.
*
* Parameters:
* pixel - {<OpenLayers.Pixel>}
*/
pixelToBounds: function(pixel) {
var llPx = pixel.add(-this.clickTolerance/2, this.clickTolerance/2);
var urPx = pixel.add(this.clickTolerance/2, -this.clickTolerance/2);
var ll = this.map.getLonLatFromPixel(llPx);
var ur = this.map.getLonLatFromPixel(urPx);
return new OpenLayers.Bounds(ll.lon, ll.lat, ur.lon, ur.lat);
},
CLASS_NAME: "OpenLayers.Control.GetFeature"
});

View File

@ -0,0 +1,377 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Lang.js
* @requires OpenLayers/Rule.js
* @requires OpenLayers/StyleMap.js
* @requires OpenLayers/Layer/Vector.js
*/
/**
* Class: OpenLayers.Control.Graticule
* The Graticule displays a grid of latitude/longitude lines reprojected on
* the map.
*
* Inherits from:
* - <OpenLayers.Control>
*
*/
OpenLayers.Control.Graticule = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: autoActivate
* {Boolean} Activate the control when it is added to a map. Default is
* true.
*/
autoActivate: true,
/**
* APIProperty: intervals
* {Array(Float)} A list of possible graticule widths in degrees.
*/
intervals: [ 45, 30, 20, 10, 5, 2, 1,
0.5, 0.2, 0.1, 0.05, 0.01,
0.005, 0.002, 0.001 ],
/**
* APIProperty: displayInLayerSwitcher
* {Boolean} Allows the Graticule control to be switched on and off by
* LayerSwitcher control. Defaults is true.
*/
displayInLayerSwitcher: true,
/**
* APIProperty: visible
* {Boolean} should the graticule be initially visible (default=true)
*/
visible: true,
/**
* APIProperty: numPoints
* {Integer} The number of points to use in each graticule line. Higher
* numbers result in a smoother curve for projected maps
*/
numPoints: 50,
/**
* APIProperty: targetSize
* {Integer} The maximum size of the grid in pixels on the map
*/
targetSize: 200,
/**
* APIProperty: layerName
* {String} The name to be displayed in the layer switcher, default is set
* by {<OpenLayers.Lang>}.
*/
layerName: null,
/**
* APIProperty: labelled
* {Boolean} Should the graticule lines be labelled?. default=true
*/
labelled: true,
/**
* APIProperty: labelFormat
* {String} the format of the labels, default = 'dm'. See
* <OpenLayers.Util.getFormattedLonLat> for other options.
*/
labelFormat: 'dm',
/**
* APIProperty: lineSymbolizer
* {symbolizer} the symbolizer used to render lines
*/
lineSymbolizer: {
strokeColor: "#333",
strokeWidth: 1,
strokeOpacity: 0.5
},
/**
* APIProperty: labelSymbolizer
* {symbolizer} the symbolizer used to render labels
*/
labelSymbolizer: {},
/**
* Property: gratLayer
* {<OpenLayers.Layer.Vector>} vector layer used to draw the graticule on
*/
gratLayer: null,
/**
* Constructor: OpenLayers.Control.Graticule
* Create a new graticule control to display a grid of latitude longitude
* lines.
*
* Parameters:
* options - {Object} An optional object whose properties will be used
* to extend the control.
*/
initialize: function(options) {
options = options || {};
options.layerName = options.layerName || OpenLayers.i18n("Graticule");
OpenLayers.Control.prototype.initialize.apply(this, [options]);
this.labelSymbolizer.stroke = false;
this.labelSymbolizer.fill = false;
this.labelSymbolizer.label = "${label}";
this.labelSymbolizer.labelAlign = "${labelAlign}";
this.labelSymbolizer.labelXOffset = "${xOffset}";
this.labelSymbolizer.labelYOffset = "${yOffset}";
},
/**
* APIMethod: destroy
*/
destroy: function() {
this.deactivate();
OpenLayers.Control.prototype.destroy.apply(this, arguments);
if (this.gratLayer) {
this.gratLayer.destroy();
this.gratLayer = null;
}
},
/**
* Method: draw
*
* initializes the graticule layer and does the initial update
*
* Returns:
* {DOMElement}
*/
draw: function() {
OpenLayers.Control.prototype.draw.apply(this, arguments);
if (!this.gratLayer) {
var gratStyle = new OpenLayers.Style({},{
rules: [new OpenLayers.Rule({'symbolizer':
{"Point":this.labelSymbolizer,
"Line":this.lineSymbolizer}
})]
});
this.gratLayer = new OpenLayers.Layer.Vector(this.layerName, {
styleMap: new OpenLayers.StyleMap({'default':gratStyle}),
visibility: this.visible,
displayInLayerSwitcher: this.displayInLayerSwitcher
});
}
return this.div;
},
/**
* APIMethod: activate
*/
activate: function() {
if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {
this.map.addLayer(this.gratLayer);
this.map.events.register('moveend', this, this.update);
this.update();
return true;
} else {
return false;
}
},
/**
* APIMethod: deactivate
*/
deactivate: function() {
if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
this.map.events.unregister('moveend', this, this.update);
this.map.removeLayer(this.gratLayer);
return true;
} else {
return false;
}
},
/**
* Method: update
*
* calculates the grid to be displayed and actually draws it
*
* Returns:
* {DOMElement}
*/
update: function() {
//wait for the map to be initialized before proceeding
var mapBounds = this.map.getExtent();
if (!mapBounds) {
return;
}
//clear out the old grid
this.gratLayer.destroyFeatures();
//get the projection objects required
var llProj = new OpenLayers.Projection("EPSG:4326");
var mapProj = this.map.getProjectionObject();
var mapRes = this.map.getResolution();
//if the map is in lon/lat, then the lines are straight and only one
//point is required
if (mapProj.proj && mapProj.proj.projName == "longlat") {
this.numPoints = 1;
}
//get the map center in EPSG:4326
var mapCenter = this.map.getCenter(); //lon and lat here are really map x and y
var mapCenterLL = new OpenLayers.Pixel(mapCenter.lon, mapCenter.lat);
OpenLayers.Projection.transform(mapCenterLL, mapProj, llProj);
/* This block of code determines the lon/lat interval to use for the
* grid by calculating the diagonal size of one grid cell at the map
* center. Iterates through the intervals array until the diagonal
* length is less than the targetSize option.
*/
//find lat/lon interval that results in a grid of less than the target size
var testSq = this.targetSize*mapRes;
testSq *= testSq; //compare squares rather than doing a square root to save time
var llInterval;
for (var i=0; i<this.intervals.length; ++i) {
llInterval = this.intervals[i]; //could do this for both x and y??
var delta = llInterval/2;
var p1 = mapCenterLL.offset({x: -delta, y: -delta}); //test coords in EPSG:4326 space
var p2 = mapCenterLL.offset({x: delta, y: delta});
OpenLayers.Projection.transform(p1, llProj, mapProj); // convert them back to map projection
OpenLayers.Projection.transform(p2, llProj, mapProj);
var distSq = (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y);
if (distSq <= testSq) {
break;
}
}
//alert(llInterval);
//round the LL center to an even number based on the interval
mapCenterLL.x = Math.floor(mapCenterLL.x/llInterval)*llInterval;
mapCenterLL.y = Math.floor(mapCenterLL.y/llInterval)*llInterval;
//TODO adjust for minutses/seconds?
/* The following 2 blocks calculate the nodes of the grid along a
* line of constant longitude (then latitiude) running through the
* center of the map until it reaches the map edge. The calculation
* goes from the center in both directions to the edge.
*/
//get the central longitude line, increment the latitude
var iter = 0;
var centerLonPoints = [mapCenterLL.clone()];
var newPoint = mapCenterLL.clone();
var mapXY;
do {
newPoint = newPoint.offset({x: 0, y: llInterval});
mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);
centerLonPoints.unshift(newPoint);
} while (mapBounds.containsPixel(mapXY) && ++iter<1000);
newPoint = mapCenterLL.clone();
do {
newPoint = newPoint.offset({x: 0, y: -llInterval});
mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);
centerLonPoints.push(newPoint);
} while (mapBounds.containsPixel(mapXY) && ++iter<1000);
//get the central latitude line, increment the longitude
iter = 0;
var centerLatPoints = [mapCenterLL.clone()];
newPoint = mapCenterLL.clone();
do {
newPoint = newPoint.offset({x: -llInterval, y: 0});
mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);
centerLatPoints.unshift(newPoint);
} while (mapBounds.containsPixel(mapXY) && ++iter<1000);
newPoint = mapCenterLL.clone();
do {
newPoint = newPoint.offset({x: llInterval, y: 0});
mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);
centerLatPoints.push(newPoint);
} while (mapBounds.containsPixel(mapXY) && ++iter<1000);
//now generate a line for each node in the central lat and lon lines
//first loop over constant longitude
var lines = [];
for(var i=0; i < centerLatPoints.length; ++i) {
var lon = centerLatPoints[i].x;
var pointList = [];
var labelPoint = null;
var latEnd = Math.min(centerLonPoints[0].y, 90);
var latStart = Math.max(centerLonPoints[centerLonPoints.length - 1].y, -90);
var latDelta = (latEnd - latStart)/this.numPoints;
var lat = latStart;
for(var j=0; j<= this.numPoints; ++j) {
var gridPoint = new OpenLayers.Geometry.Point(lon,lat);
gridPoint.transform(llProj, mapProj);
pointList.push(gridPoint);
lat += latDelta;
if (gridPoint.y >= mapBounds.bottom && !labelPoint) {
labelPoint = gridPoint;
}
}
if (this.labelled) {
//keep track of when this grid line crosses the map bounds to set
//the label position
//labels along the bottom, add 10 pixel offset up into the map
//TODO add option for labels on top
var labelPos = new OpenLayers.Geometry.Point(labelPoint.x,mapBounds.bottom);
var labelAttrs = {
value: lon,
label: this.labelled?OpenLayers.Util.getFormattedLonLat(lon, "lon", this.labelFormat):"",
labelAlign: "cb",
xOffset: 0,
yOffset: 2
};
this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos,labelAttrs));
}
var geom = new OpenLayers.Geometry.LineString(pointList);
lines.push(new OpenLayers.Feature.Vector(geom));
}
//now draw the lines of constant latitude
for (var j=0; j < centerLonPoints.length; ++j) {
lat = centerLonPoints[j].y;
if (lat<-90 || lat>90) { //latitudes only valid between -90 and 90
continue;
}
var pointList = [];
var lonStart = centerLatPoints[0].x;
var lonEnd = centerLatPoints[centerLatPoints.length - 1].x;
var lonDelta = (lonEnd - lonStart)/this.numPoints;
var lon = lonStart;
var labelPoint = null;
for(var i=0; i <= this.numPoints ; ++i) {
var gridPoint = new OpenLayers.Geometry.Point(lon,lat);
gridPoint.transform(llProj, mapProj);
pointList.push(gridPoint);
lon += lonDelta;
if (gridPoint.x < mapBounds.right) {
labelPoint = gridPoint;
}
}
if (this.labelled) {
//keep track of when this grid line crosses the map bounds to set
//the label position
//labels along the right, 30 pixel offset left into the map
//TODO add option for labels on left
var labelPos = new OpenLayers.Geometry.Point(mapBounds.right, labelPoint.y);
var labelAttrs = {
value: lat,
label: this.labelled?OpenLayers.Util.getFormattedLonLat(lat, "lat", this.labelFormat):"",
labelAlign: "rb",
xOffset: -2,
yOffset: 2
};
this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos,labelAttrs));
}
var geom = new OpenLayers.Geometry.LineString(pointList);
lines.push(new OpenLayers.Feature.Vector(geom));
}
this.gratLayer.addFeatures(lines);
},
CLASS_NAME: "OpenLayers.Control.Graticule"
});

View File

@ -0,0 +1,142 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Handler/Keyboard.js
* @requires OpenLayers/Events.js
*/
/**
* Class: OpenLayers.Control.KeyboardDefaults
* The KeyboardDefaults control adds panning and zooming functions, controlled
* with the keyboard. By default arrow keys pan, +/- keys zoom & Page Up/Page
* Down/Home/End scroll by three quarters of a page.
*
* This control has no visible appearance.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.KeyboardDefaults = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: autoActivate
* {Boolean} Activate the control when it is added to a map. Default is
* true.
*/
autoActivate: true,
/**
* APIProperty: slideFactor
* Pixels to slide by.
*/
slideFactor: 75,
/**
* APIProperty: observeElement
* {DOMelement|String} The DOM element to handle keys for. You
* can use the map div here, to have the navigation keys
* work when the map div has the focus. If undefined the
* document is used.
*/
observeElement: null,
/**
* Constructor: OpenLayers.Control.KeyboardDefaults
*/
/**
* Method: draw
* Create handler.
*/
draw: function() {
var observeElement = this.observeElement || document;
this.handler = new OpenLayers.Handler.Keyboard( this,
{"keydown": this.defaultKeyPress},
{observeElement: observeElement}
);
},
/**
* Method: defaultKeyPress
* When handling the key event, we only use evt.keyCode. This holds
* some drawbacks, though we get around them below. When interpretting
* the keycodes below (including the comments associated with them),
* consult the URL below. For instance, the Safari browser returns
* "IE keycodes", and so is supported by any keycode labeled "IE".
*
* Very informative URL:
* http://unixpapa.com/js/key.html
*
* Parameters:
* evt - {Event}
*/
defaultKeyPress: function (evt) {
var size, handled = true;
var target = OpenLayers.Event.element(evt);
if (target &&
(target.tagName == 'INPUT' ||
target.tagName == 'TEXTAREA' ||
target.tagName == 'SELECT')) {
return;
}
switch (evt.keyCode) {
case OpenLayers.Event.KEY_LEFT:
this.map.pan(-this.slideFactor, 0);
break;
case OpenLayers.Event.KEY_RIGHT:
this.map.pan(this.slideFactor, 0);
break;
case OpenLayers.Event.KEY_UP:
this.map.pan(0, -this.slideFactor);
break;
case OpenLayers.Event.KEY_DOWN:
this.map.pan(0, this.slideFactor);
break;
case 33: // Page Up. Same in all browsers.
size = this.map.getSize();
this.map.pan(0, -0.75*size.h);
break;
case 34: // Page Down. Same in all browsers.
size = this.map.getSize();
this.map.pan(0, 0.75*size.h);
break;
case 35: // End. Same in all browsers.
size = this.map.getSize();
this.map.pan(0.75*size.w, 0);
break;
case 36: // Home. Same in all browsers.
size = this.map.getSize();
this.map.pan(-0.75*size.w, 0);
break;
case 43: // +/= (ASCII), keypad + (ASCII, Opera)
case 61: // +/= (Mozilla, Opera, some ASCII)
case 187: // +/= (IE)
case 107: // keypad + (IE, Mozilla)
this.map.zoomIn();
break;
case 45: // -/_ (ASCII, Opera), keypad - (ASCII, Opera)
case 109: // -/_ (Mozilla), keypad - (Mozilla, IE)
case 189: // -/_ (IE)
case 95: // -/_ (some ASCII)
this.map.zoomOut();
break;
default:
handled = false;
}
if (handled) {
// prevent browser default not to move the page
// when moving the page with the keyboard
OpenLayers.Event.stop(evt);
}
},
CLASS_NAME: "OpenLayers.Control.KeyboardDefaults"
});

View File

@ -0,0 +1,521 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Lang.js
* @requires OpenLayers/Util.js
* @requires OpenLayers/Events/buttonclick.js
*/
/**
* Class: OpenLayers.Control.LayerSwitcher
* The LayerSwitcher control displays a table of contents for the map. This
* allows the user interface to switch between BaseLasyers and to show or hide
* Overlays. By default the switcher is shown minimized on the right edge of
* the map, the user may expand it by clicking on the handle.
*
* To create the LayerSwitcher outside of the map, pass the Id of a html div
* as the first argument to the constructor.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, {
/**
* Property: layerStates
* {Array(Object)} Basically a copy of the "state" of the map's layers
* the last time the control was drawn. We have this in order to avoid
* unnecessarily redrawing the control.
*/
layerStates: null,
// DOM Elements
/**
* Property: layersDiv
* {DOMElement}
*/
layersDiv: null,
/**
* Property: baseLayersDiv
* {DOMElement}
*/
baseLayersDiv: null,
/**
* Property: baseLayers
* {Array(Object)}
*/
baseLayers: null,
/**
* Property: dataLbl
* {DOMElement}
*/
dataLbl: null,
/**
* Property: dataLayersDiv
* {DOMElement}
*/
dataLayersDiv: null,
/**
* Property: dataLayers
* {Array(Object)}
*/
dataLayers: null,
/**
* Property: minimizeDiv
* {DOMElement}
*/
minimizeDiv: null,
/**
* Property: maximizeDiv
* {DOMElement}
*/
maximizeDiv: null,
/**
* APIProperty: ascending
* {Boolean}
*/
ascending: true,
/**
* Constructor: OpenLayers.Control.LayerSwitcher
*
* Parameters:
* options - {Object}
*/
initialize: function(options) {
OpenLayers.Control.prototype.initialize.apply(this, arguments);
this.layerStates = [];
},
/**
* APIMethod: destroy
*/
destroy: function() {
//clear out layers info and unregister their events
this.clearLayersArray("base");
this.clearLayersArray("data");
this.map.events.un({
buttonclick: this.onButtonClick,
addlayer: this.redraw,
changelayer: this.redraw,
removelayer: this.redraw,
changebaselayer: this.redraw,
scope: this
});
this.events.unregister("buttonclick", this, this.onButtonClick);
OpenLayers.Control.prototype.destroy.apply(this, arguments);
},
/**
* Method: setMap
*
* Properties:
* map - {<OpenLayers.Map>}
*/
setMap: function(map) {
OpenLayers.Control.prototype.setMap.apply(this, arguments);
this.map.events.on({
addlayer: this.redraw,
changelayer: this.redraw,
removelayer: this.redraw,
changebaselayer: this.redraw,
scope: this
});
if (this.outsideViewport) {
this.events.attachToElement(this.div);
this.events.register("buttonclick", this, this.onButtonClick);
} else {
this.map.events.register("buttonclick", this, this.onButtonClick);
}
},
/**
* Method: draw
*
* Returns:
* {DOMElement} A reference to the DIV DOMElement containing the
* switcher tabs.
*/
draw: function() {
OpenLayers.Control.prototype.draw.apply(this);
// create layout divs
this.loadContents();
// set mode to minimize
if(!this.outsideViewport) {
this.minimizeControl();
}
// populate div with current info
this.redraw();
return this.div;
},
/**
* Method: onButtonClick
*
* Parameters:
* evt - {Event}
*/
onButtonClick: function(evt) {
var button = evt.buttonElement;
if (button === this.minimizeDiv) {
this.minimizeControl();
} else if (button === this.maximizeDiv) {
this.maximizeControl();
} else if (button._layerSwitcher === this.id) {
if (button["for"]) {
button = document.getElementById(button["for"]);
}
if (!button.disabled) {
if (button.type == "radio") {
button.checked = true;
this.map.setBaseLayer(this.map.getLayer(button._layer));
} else {
button.checked = !button.checked;
this.updateMap();
}
}
}
},
/**
* Method: clearLayersArray
* User specifies either "base" or "data". we then clear all the
* corresponding listeners, the div, and reinitialize a new array.
*
* Parameters:
* layersType - {String}
*/
clearLayersArray: function(layersType) {
this[layersType + "LayersDiv"].innerHTML = "";
this[layersType + "Layers"] = [];
},
/**
* Method: checkRedraw
* Checks if the layer state has changed since the last redraw() call.
*
* Returns:
* {Boolean} The layer state changed since the last redraw() call.
*/
checkRedraw: function() {
if ( !this.layerStates.length ||
(this.map.layers.length != this.layerStates.length) ) {
return true;
}
for (var i = 0, len = this.layerStates.length; i < len; i++) {
var layerState = this.layerStates[i];
var layer = this.map.layers[i];
if ( (layerState.name != layer.name) ||
(layerState.inRange != layer.inRange) ||
(layerState.id != layer.id) ||
(layerState.visibility != layer.visibility) ) {
return true;
}
}
return false;
},
/**
* Method: redraw
* Goes through and takes the current state of the Map and rebuilds the
* control to display that state. Groups base layers into a
* radio-button group and lists each data layer with a checkbox.
*
* Returns:
* {DOMElement} A reference to the DIV DOMElement containing the control
*/
redraw: function() {
//if the state hasn't changed since last redraw, no need
// to do anything. Just return the existing div.
if (!this.checkRedraw()) {
return this.div;
}
//clear out previous layers
this.clearLayersArray("base");
this.clearLayersArray("data");
var containsOverlays = false;
var containsBaseLayers = false;
// Save state -- for checking layer if the map state changed.
// We save this before redrawing, because in the process of redrawing
// we will trigger more visibility changes, and we want to not redraw
// and enter an infinite loop.
var len = this.map.layers.length;
this.layerStates = new Array(len);
for (var i=0; i <len; i++) {
var layer = this.map.layers[i];
this.layerStates[i] = {
'name': layer.name,
'visibility': layer.visibility,
'inRange': layer.inRange,
'id': layer.id
};
}
var layers = this.map.layers.slice();
if (!this.ascending) { layers.reverse(); }
for(var i=0, len=layers.length; i<len; i++) {
var layer = layers[i];
var baseLayer = layer.isBaseLayer;
if (layer.displayInLayerSwitcher) {
if (baseLayer) {
containsBaseLayers = true;
} else {
containsOverlays = true;
}
// only check a baselayer if it is *the* baselayer, check data
// layers if they are visible
var checked = (baseLayer) ? (layer == this.map.baseLayer)
: layer.getVisibility();
// create input element
var inputElem = document.createElement("input"),
// The input shall have an id attribute so we can use
// labels to interact with them.
inputId = OpenLayers.Util.createUniqueID(
this.id + "_input_"
);
inputElem.id = inputId;
inputElem.name = (baseLayer) ? this.id + "_baseLayers" : layer.name;
inputElem.type = (baseLayer) ? "radio" : "checkbox";
inputElem.value = layer.name;
inputElem.checked = checked;
inputElem.defaultChecked = checked;
inputElem.className = "olButton";
inputElem._layer = layer.id;
inputElem._layerSwitcher = this.id;
if (!baseLayer && !layer.inRange) {
inputElem.disabled = true;
}
// create span
var labelSpan = document.createElement("label");
// this isn't the DOM attribute 'for', but an arbitrary name we
// use to find the appropriate input element in <onButtonClick>
labelSpan["for"] = inputElem.id;
OpenLayers.Element.addClass(labelSpan, "labelSpan olButton");
labelSpan._layer = layer.id;
labelSpan._layerSwitcher = this.id;
if (!baseLayer && !layer.inRange) {
labelSpan.style.color = "gray";
}
labelSpan.innerHTML = layer.name;
labelSpan.style.verticalAlign = (baseLayer) ? "bottom"
: "baseline";
// create line break
var br = document.createElement("br");
var groupArray = (baseLayer) ? this.baseLayers
: this.dataLayers;
groupArray.push({
'layer': layer,
'inputElem': inputElem,
'labelSpan': labelSpan
});
var groupDiv = (baseLayer) ? this.baseLayersDiv
: this.dataLayersDiv;
groupDiv.appendChild(inputElem);
groupDiv.appendChild(labelSpan);
groupDiv.appendChild(br);
}
}
// if no overlays, dont display the overlay label
this.dataLbl.style.display = (containsOverlays) ? "" : "none";
// if no baselayers, dont display the baselayer label
this.baseLbl.style.display = (containsBaseLayers) ? "" : "none";
return this.div;
},
/**
* Method: updateMap
* Cycles through the loaded data and base layer input arrays and makes
* the necessary calls to the Map object such that that the map's
* visual state corresponds to what the user has selected in
* the control.
*/
updateMap: function() {
// set the newly selected base layer
for(var i=0, len=this.baseLayers.length; i<len; i++) {
var layerEntry = this.baseLayers[i];
if (layerEntry.inputElem.checked) {
this.map.setBaseLayer(layerEntry.layer, false);
}
}
// set the correct visibilities for the overlays
for(var i=0, len=this.dataLayers.length; i<len; i++) {
var layerEntry = this.dataLayers[i];
layerEntry.layer.setVisibility(layerEntry.inputElem.checked);
}
},
/**
* Method: maximizeControl
* Set up the labels and divs for the control
*
* Parameters:
* e - {Event}
*/
maximizeControl: function(e) {
// set the div's width and height to empty values, so
// the div dimensions can be controlled by CSS
this.div.style.width = "";
this.div.style.height = "";
this.showControls(false);
if (e != null) {
OpenLayers.Event.stop(e);
}
},
/**
* Method: minimizeControl
* Hide all the contents of the control, shrink the size,
* add the maximize icon
*
* Parameters:
* e - {Event}
*/
minimizeControl: function(e) {
// to minimize the control we set its div's width
// and height to 0px, we cannot just set "display"
// to "none" because it would hide the maximize
// div
this.div.style.width = "0px";
this.div.style.height = "0px";
this.showControls(true);
if (e != null) {
OpenLayers.Event.stop(e);
}
},
/**
* Method: showControls
* Hide/Show all LayerSwitcher controls depending on whether we are
* minimized or not
*
* Parameters:
* minimize - {Boolean}
*/
showControls: function(minimize) {
this.maximizeDiv.style.display = minimize ? "" : "none";
this.minimizeDiv.style.display = minimize ? "none" : "";
this.layersDiv.style.display = minimize ? "none" : "";
},
/**
* Method: loadContents
* Set up the labels and divs for the control
*/
loadContents: function() {
// layers list div
this.layersDiv = document.createElement("div");
this.layersDiv.id = this.id + "_layersDiv";
OpenLayers.Element.addClass(this.layersDiv, "layersDiv");
this.baseLbl = document.createElement("div");
this.baseLbl.innerHTML = OpenLayers.i18n("Base Layer");
OpenLayers.Element.addClass(this.baseLbl, "baseLbl");
this.baseLayersDiv = document.createElement("div");
OpenLayers.Element.addClass(this.baseLayersDiv, "baseLayersDiv");
this.dataLbl = document.createElement("div");
this.dataLbl.innerHTML = OpenLayers.i18n("Overlays");
OpenLayers.Element.addClass(this.dataLbl, "dataLbl");
this.dataLayersDiv = document.createElement("div");
OpenLayers.Element.addClass(this.dataLayersDiv, "dataLayersDiv");
if (this.ascending) {
this.layersDiv.appendChild(this.baseLbl);
this.layersDiv.appendChild(this.baseLayersDiv);
this.layersDiv.appendChild(this.dataLbl);
this.layersDiv.appendChild(this.dataLayersDiv);
} else {
this.layersDiv.appendChild(this.dataLbl);
this.layersDiv.appendChild(this.dataLayersDiv);
this.layersDiv.appendChild(this.baseLbl);
this.layersDiv.appendChild(this.baseLayersDiv);
}
this.div.appendChild(this.layersDiv);
// maximize button div
var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png');
this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
"OpenLayers_Control_MaximizeDiv",
null,
null,
img,
"absolute");
OpenLayers.Element.addClass(this.maximizeDiv, "maximizeDiv olButton");
this.maximizeDiv.style.display = "none";
this.div.appendChild(this.maximizeDiv);
// minimize button div
var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png');
this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
"OpenLayers_Control_MinimizeDiv",
null,
null,
img,
"absolute");
OpenLayers.Element.addClass(this.minimizeDiv, "minimizeDiv olButton");
this.minimizeDiv.style.display = "none";
this.div.appendChild(this.minimizeDiv);
},
CLASS_NAME: "OpenLayers.Control.LayerSwitcher"
});

View File

@ -0,0 +1,379 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Feature/Vector.js
*/
/**
* Class: OpenLayers.Control.Measure
* Allows for drawing of features for measurements.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.Measure = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: events
* {<OpenLayers.Events>} Events instance for listeners and triggering
* control specific events.
*
* Register a listener for a particular event with the following syntax:
* (code)
* control.events.register(type, obj, listener);
* (end)
*
* Supported event types (in addition to those from <OpenLayers.Control.events>):
* measure - Triggered when a measurement sketch is complete. Listeners
* will receive an event with measure, units, order, and geometry
* properties.
* measurepartial - Triggered when a new point is added to the
* measurement sketch or if the <immediate> property is true and the
* measurement sketch is modified. Listeners receive an event with measure,
* units, order, and geometry.
*/
/**
* APIProperty: handlerOptions
* {Object} Used to set non-default properties on the control's handler
*/
/**
* Property: callbacks
* {Object} The functions that are sent to the handler for callback
*/
callbacks: null,
/**
* APIProperty: displaySystem
* {String} Display system for output measurements. Supported values
* are 'english', 'metric', and 'geographic'. Default is 'metric'.
*/
displaySystem: 'metric',
/**
* APIProperty: geodesic
* {Boolean} Calculate geodesic metrics instead of planar metrics. This
* requires that geometries can be transformed into Geographic/WGS84
* (if that is not already the map projection). Default is false.
*/
geodesic: false,
/**
* Property: displaySystemUnits
* {Object} Units for various measurement systems. Values are arrays
* of unit abbreviations (from OpenLayers.INCHES_PER_UNIT) in decreasing
* order of length.
*/
displaySystemUnits: {
geographic: ['dd'],
english: ['mi', 'ft', 'in'],
metric: ['km', 'm']
},
/**
* Property: delay
* {Number} Number of milliseconds between clicks before the event is
* considered a double-click. The "measurepartial" event will not
* be triggered if the sketch is completed within this time. This
* is required for IE where creating a browser reflow (if a listener
* is modifying the DOM by displaying the measurement values) messes
* with the dblclick listener in the sketch handler.
*/
partialDelay: 300,
/**
* Property: delayedTrigger
* {Number} Timeout id of trigger for measurepartial.
*/
delayedTrigger: null,
/**
* APIProperty: persist
* {Boolean} Keep the temporary measurement sketch drawn after the
* measurement is complete. The geometry will persist until a new
* measurement is started, the control is deactivated, or <cancel> is
* called.
*/
persist: false,
/**
* APIProperty: immediate
* {Boolean} Activates the immediate measurement so that the "measurepartial"
* event is also fired once the measurement sketch is modified.
* Default is false.
*/
immediate : false,
/**
* Constructor: OpenLayers.Control.Measure
*
* Parameters:
* handler - {<OpenLayers.Handler>}
* options - {Object}
*/
initialize: function(handler, options) {
OpenLayers.Control.prototype.initialize.apply(this, [options]);
var callbacks = {done: this.measureComplete,
point: this.measurePartial};
if (this.immediate){
callbacks.modify = this.measureImmediate;
}
this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);
// let the handler options override, so old code that passes 'persist'
// directly to the handler does not need an update
this.handlerOptions = OpenLayers.Util.extend(
{persist: this.persist}, this.handlerOptions
);
this.handler = new handler(this, this.callbacks, this.handlerOptions);
},
/**
* APIMethod: deactivate
*/
deactivate: function() {
this.cancelDelay();
return OpenLayers.Control.prototype.deactivate.apply(this, arguments);
},
/**
* APIMethod: cancel
* Stop the control from measuring. If <persist> is true, the temporary
* sketch will be erased.
*/
cancel: function() {
this.cancelDelay();
this.handler.cancel();
},
/**
* APIMethod: setImmediate
* Sets the <immediate> property. Changes the activity of immediate
* measurement.
*/
setImmediate: function(immediate) {
this.immediate = immediate;
if (this.immediate){
this.callbacks.modify = this.measureImmediate;
} else {
delete this.callbacks.modify;
}
},
/**
* Method: updateHandler
*
* Parameters:
* handler - {Function} One of the sketch handler constructors.
* options - {Object} Options for the handler.
*/
updateHandler: function(handler, options) {
var active = this.active;
if(active) {
this.deactivate();
}
this.handler = new handler(this, this.callbacks, options);
if(active) {
this.activate();
}
},
/**
* Method: measureComplete
* Called when the measurement sketch is done.
*
* Parameters:
* geometry - {<OpenLayers.Geometry>}
*/
measureComplete: function(geometry) {
this.cancelDelay();
this.measure(geometry, "measure");
},
/**
* Method: measurePartial
* Called each time a new point is added to the measurement sketch.
*
* Parameters:
* point - {<OpenLayers.Geometry.Point>} The last point added.
* geometry - {<OpenLayers.Geometry>} The sketch geometry.
*/
measurePartial: function(point, geometry) {
this.cancelDelay();
geometry = geometry.clone();
// when we're wating for a dblclick, we have to trigger measurepartial
// after some delay to deal with reflow issues in IE
if (this.handler.freehandMode(this.handler.evt)) {
// no dblclick in freehand mode
this.measure(geometry, "measurepartial");
} else {
this.delayedTrigger = window.setTimeout(
OpenLayers.Function.bind(function() {
this.delayedTrigger = null;
this.measure(geometry, "measurepartial");
}, this),
this.partialDelay
);
}
},
/**
* Method: measureImmediate
* Called each time the measurement sketch is modified.
*
* Parameters:
* point - {<OpenLayers.Geometry.Point>} The point at the mouse position.
* feature - {<OpenLayers.Feature.Vector>} The sketch feature.
* drawing - {Boolean} Indicates whether we're currently drawing.
*/
measureImmediate : function(point, feature, drawing) {
if (drawing && !this.handler.freehandMode(this.handler.evt)) {
this.cancelDelay();
this.measure(feature.geometry, "measurepartial");
}
},
/**
* Method: cancelDelay
* Cancels the delay measurement that measurePartial began.
*/
cancelDelay: function() {
if (this.delayedTrigger !== null) {
window.clearTimeout(this.delayedTrigger);
this.delayedTrigger = null;
}
},
/**
* Method: measure
*
* Parameters:
* geometry - {<OpenLayers.Geometry>}
* eventType - {String}
*/
measure: function(geometry, eventType) {
var stat, order;
if(geometry.CLASS_NAME.indexOf('LineString') > -1) {
stat = this.getBestLength(geometry);
order = 1;
} else {
stat = this.getBestArea(geometry);
order = 2;
}
this.events.triggerEvent(eventType, {
measure: stat[0],
units: stat[1],
order: order,
geometry: geometry
});
},
/**
* Method: getBestArea
* Based on the <displaySystem> returns the area of a geometry.
*
* Parameters:
* geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {Array([Float, String])} Returns a two item array containing the
* area and the units abbreviation.
*/
getBestArea: function(geometry) {
var units = this.displaySystemUnits[this.displaySystem];
var unit, area;
for(var i=0, len=units.length; i<len; ++i) {
unit = units[i];
area = this.getArea(geometry, unit);
if(area > 1) {
break;
}
}
return [area, unit];
},
/**
* Method: getArea
*
* Parameters:
* geometry - {<OpenLayers.Geometry>}
* units - {String} Unit abbreviation
*
* Returns:
* {Float} The geometry area in the given units.
*/
getArea: function(geometry, units) {
var area, geomUnits;
if(this.geodesic) {
area = geometry.getGeodesicArea(this.map.getProjectionObject());
geomUnits = "m";
} else {
area = geometry.getArea();
geomUnits = this.map.getUnits();
}
var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
if(inPerDisplayUnit) {
var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];
area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2);
}
return area;
},
/**
* Method: getBestLength
* Based on the <displaySystem> returns the length of a geometry.
*
* Parameters:
* geometry - {<OpenLayers.Geometry>}
*
* Returns:
* {Array([Float, String])} Returns a two item array containing the
* length and the units abbreviation.
*/
getBestLength: function(geometry) {
var units = this.displaySystemUnits[this.displaySystem];
var unit, length;
for(var i=0, len=units.length; i<len; ++i) {
unit = units[i];
length = this.getLength(geometry, unit);
if(length > 1) {
break;
}
}
return [length, unit];
},
/**
* Method: getLength
*
* Parameters:
* geometry - {<OpenLayers.Geometry>}
* units - {String} Unit abbreviation
*
* Returns:
* {Float} The geometry length in the given units.
*/
getLength: function(geometry, units) {
var length, geomUnits;
if(this.geodesic) {
length = geometry.getGeodesicLength(this.map.getProjectionObject());
geomUnits = "m";
} else {
length = geometry.getLength();
geomUnits = this.map.getUnits();
}
var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
if(inPerDisplayUnit) {
var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];
length *= (inPerMapUnit / inPerDisplayUnit);
}
return length;
},
CLASS_NAME: "OpenLayers.Control.Measure"
});

View File

@ -0,0 +1,835 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Handler/Drag.js
* @requires OpenLayers/Handler/Keyboard.js
*/
/**
* Class: OpenLayers.Control.ModifyFeature
* Control to modify features. When activated, a click renders the vertices
* of a feature - these vertices can then be dragged. By default, the
* delete key will delete the vertex under the mouse. New features are
* added by dragging "virtual vertices" between vertices. Create a new
* control with the <OpenLayers.Control.ModifyFeature> constructor.
*
* Inherits From:
* - <OpenLayers.Control>
*/
OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: documentDrag
* {Boolean} If set to true, dragging vertices will continue even if the
* mouse cursor leaves the map viewport. Default is false.
*/
documentDrag: false,
/**
* APIProperty: geometryTypes
* {Array(String)} To restrict modification to a limited set of geometry
* types, send a list of strings corresponding to the geometry class
* names.
*/
geometryTypes: null,
/**
* APIProperty: clickout
* {Boolean} Unselect features when clicking outside any feature.
* Default is true.
*/
clickout: true,
/**
* APIProperty: toggle
* {Boolean} Unselect a selected feature on click.
* Default is true.
*/
toggle: true,
/**
* APIProperty: standalone
* {Boolean} Set to true to create a control without SelectFeature
* capabilities. Default is false. If standalone is true, to modify
* a feature, call the <selectFeature> method with the target feature.
* Note that you must call the <unselectFeature> method to finish
* feature modification in standalone mode (before starting to modify
* another feature).
*/
standalone: false,
/**
* Property: layer
* {<OpenLayers.Layer.Vector>}
*/
layer: null,
/**
* Property: feature
* {<OpenLayers.Feature.Vector>} Feature currently available for modification.
*/
feature: null,
/**
* Property: vertex
* {<OpenLayers.Feature.Vector>} Vertex currently being modified.
*/
vertex: null,
/**
* Property: vertices
* {Array(<OpenLayers.Feature.Vector>)} Verticies currently available
* for dragging.
*/
vertices: null,
/**
* Property: virtualVertices
* {Array(<OpenLayers.Feature.Vector>)} Virtual vertices in the middle
* of each edge.
*/
virtualVertices: null,
/**
* Property: handlers
* {Object}
*/
handlers: null,
/**
* APIProperty: deleteCodes
* {Array(Integer)} Keycodes for deleting verticies. Set to null to disable
* vertex deltion by keypress. If non-null, keypresses with codes
* in this array will delete vertices under the mouse. Default
* is 46 and 68, the 'delete' and lowercase 'd' keys.
*/
deleteCodes: null,
/**
* APIProperty: virtualStyle
* {Object} A symbolizer to be used for virtual vertices.
*/
virtualStyle: null,
/**
* APIProperty: vertexRenderIntent
* {String} The renderIntent to use for vertices. If no <virtualStyle> is
* provided, this renderIntent will also be used for virtual vertices, with
* a fillOpacity and strokeOpacity of 0.3. Default is null, which means
* that the layer's default style will be used for vertices.
*/
vertexRenderIntent: null,
/**
* APIProperty: mode
* {Integer} Bitfields specifying the modification mode. Defaults to
* OpenLayers.Control.ModifyFeature.RESHAPE. To set the mode to a
* combination of options, use the | operator. For example, to allow
* the control to both resize and rotate features, use the following
* syntax
* (code)
* control.mode = OpenLayers.Control.ModifyFeature.RESIZE |
* OpenLayers.Control.ModifyFeature.ROTATE;
* (end)
*/
mode: null,
/**
* APIProperty: createVertices
* {Boolean} Create new vertices by dragging the virtual vertices
* in the middle of each edge. Default is true.
*/
createVertices: true,
/**
* Property: modified
* {Boolean} The currently selected feature has been modified.
*/
modified: false,
/**
* Property: radiusHandle
* {<OpenLayers.Feature.Vector>} A handle for rotating/resizing a feature.
*/
radiusHandle: null,
/**
* Property: dragHandle
* {<OpenLayers.Feature.Vector>} A handle for dragging a feature.
*/
dragHandle: null,
/**
* APIProperty: onModificationStart
* {Function} *Deprecated*. Register for "beforefeaturemodified" instead.
* The "beforefeaturemodified" event is triggered on the layer before
* any modification begins.
*
* Optional function to be called when a feature is selected
* to be modified. The function should expect to be called with a
* feature. This could be used for example to allow to lock the
* feature on server-side.
*/
onModificationStart: function() {},
/**
* APIProperty: onModification
* {Function} *Deprecated*. Register for "featuremodified" instead.
* The "featuremodified" event is triggered on the layer with each
* feature modification.
*
* Optional function to be called when a feature has been
* modified. The function should expect to be called with a feature.
*/
onModification: function() {},
/**
* APIProperty: onModificationEnd
* {Function} *Deprecated*. Register for "afterfeaturemodified" instead.
* The "afterfeaturemodified" event is triggered on the layer after
* a feature has been modified.
*
* Optional function to be called when a feature is finished
* being modified. The function should expect to be called with a
* feature.
*/
onModificationEnd: function() {},
/**
* Constructor: OpenLayers.Control.ModifyFeature
* Create a new modify feature control.
*
* Parameters:
* layer - {<OpenLayers.Layer.Vector>} Layer that contains features that
* will be modified.
* options - {Object} Optional object whose properties will be set on the
* control.
*/
initialize: function(layer, options) {
options = options || {};
this.layer = layer;
this.vertices = [];
this.virtualVertices = [];
this.virtualStyle = OpenLayers.Util.extend({},
this.layer.style ||
this.layer.styleMap.createSymbolizer(null, options.vertexRenderIntent)
);
this.virtualStyle.fillOpacity = 0.3;
this.virtualStyle.strokeOpacity = 0.3;
this.deleteCodes = [46, 68];
this.mode = OpenLayers.Control.ModifyFeature.RESHAPE;
OpenLayers.Control.prototype.initialize.apply(this, [options]);
if(!(OpenLayers.Util.isArray(this.deleteCodes))) {
this.deleteCodes = [this.deleteCodes];
}
// configure the drag handler
var dragCallbacks = {
down: function(pixel) {
this.vertex = null;
var feature = this.layer.getFeatureFromEvent(
this.handlers.drag.evt);
if (feature) {
this.dragStart(feature);
} else if (this.clickout) {
this._unselect = this.feature;
}
},
move: function(pixel) {
delete this._unselect;
if (this.vertex) {
this.dragVertex(this.vertex, pixel);
}
},
up: function() {
this.handlers.drag.stopDown = false;
if (this._unselect) {
this.unselectFeature(this._unselect);
delete this._unselect;
}
},
done: function(pixel) {
if (this.vertex) {
this.dragComplete(this.vertex);
}
}
};
var dragOptions = {
documentDrag: this.documentDrag,
stopDown: false
};
// configure the keyboard handler
var keyboardOptions = {
keydown: this.handleKeypress
};
this.handlers = {
keyboard: new OpenLayers.Handler.Keyboard(this, keyboardOptions),
drag: new OpenLayers.Handler.Drag(this, dragCallbacks, dragOptions)
};
},
/**
* APIMethod: destroy
* Take care of things that are not handled in superclass.
*/
destroy: function() {
if (this.map) {
this.map.events.un({
"removelayer": this.handleMapEvents,
"changelayer": this.handleMapEvents,
scope: this
});
}
this.layer = null;
OpenLayers.Control.prototype.destroy.apply(this, []);
},
/**
* APIMethod: activate
* Activate the control.
*
* Returns:
* {Boolean} Successfully activated the control.
*/
activate: function() {
this.moveLayerToTop();
this.map.events.on({
"removelayer": this.handleMapEvents,
"changelayer": this.handleMapEvents,
scope: this
});
return (this.handlers.keyboard.activate() &&
this.handlers.drag.activate() &&
OpenLayers.Control.prototype.activate.apply(this, arguments));
},
/**
* APIMethod: deactivate
* Deactivate the control.
*
* Returns:
* {Boolean} Successfully deactivated the control.
*/
deactivate: function() {
var deactivated = false;
// the return from the controls is unimportant in this case
if(OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
this.moveLayerBack();
this.map.events.un({
"removelayer": this.handleMapEvents,
"changelayer": this.handleMapEvents,
scope: this
});
this.layer.removeFeatures(this.vertices, {silent: true});
this.layer.removeFeatures(this.virtualVertices, {silent: true});
this.vertices = [];
this.handlers.drag.deactivate();
this.handlers.keyboard.deactivate();
var feature = this.feature;
if (feature && feature.geometry && feature.layer) {
this.unselectFeature(feature);
}
deactivated = true;
}
return deactivated;
},
/**
* Method: beforeSelectFeature
* Called before a feature is selected.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>} The feature about to be selected.
*/
beforeSelectFeature: function(feature) {
return this.layer.events.triggerEvent(
"beforefeaturemodified", {feature: feature}
);
},
/**
* APIMethod: selectFeature
* Select a feature for modification in standalone mode. In non-standalone
* mode, this method is called when a feature is selected by clicking.
* Register a listener to the beforefeaturemodified event and return false
* to prevent feature modification.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>} the selected feature.
*/
selectFeature: function(feature) {
if (this.feature === feature ||
(this.geometryTypes && OpenLayers.Util.indexOf(this.geometryTypes,
feature.geometry.CLASS_NAME) == -1)) {
return;
}
if (this.beforeSelectFeature(feature) !== false) {
if (this.feature) {
this.unselectFeature(this.feature);
}
this.feature = feature;
this.layer.selectedFeatures.push(feature);
this.layer.drawFeature(feature, 'select');
this.modified = false;
this.resetVertices();
this.onModificationStart(this.feature);
}
// keep track of geometry modifications
var modified = feature.modified;
if (feature.geometry && !(modified && modified.geometry)) {
this._originalGeometry = feature.geometry.clone();
}
},
/**
* APIMethod: unselectFeature
* Called when the select feature control unselects a feature.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>} The unselected feature.
*/
unselectFeature: function(feature) {
this.layer.removeFeatures(this.vertices, {silent: true});
this.vertices = [];
this.layer.destroyFeatures(this.virtualVertices, {silent: true});
this.virtualVertices = [];
if(this.dragHandle) {
this.layer.destroyFeatures([this.dragHandle], {silent: true});
delete this.dragHandle;
}
if(this.radiusHandle) {
this.layer.destroyFeatures([this.radiusHandle], {silent: true});
delete this.radiusHandle;
}
this.layer.drawFeature(this.feature, 'default');
this.feature = null;
OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature);
this.onModificationEnd(feature);
this.layer.events.triggerEvent("afterfeaturemodified", {
feature: feature,
modified: this.modified
});
this.modified = false;
},
/**
* Method: dragStart
* Called by the drag handler before a feature is dragged. This method is
* used to differentiate between points and vertices
* of higher order geometries.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>} The point or vertex about to be
* dragged.
*/
dragStart: function(feature) {
var isPoint = feature.geometry.CLASS_NAME ==
'OpenLayers.Geometry.Point';
if (!this.standalone &&
((!feature._sketch && isPoint) || !feature._sketch)) {
if (this.toggle && this.feature === feature) {
// mark feature for unselection
this._unselect = feature;
}
this.selectFeature(feature);
}
if (feature._sketch || isPoint) {
// feature is a drag or virtual handle or point
this.vertex = feature;
this.handlers.drag.stopDown = true;
}
},
/**
* Method: dragVertex
* Called by the drag handler with each drag move of a vertex.
*
* Parameters:
* vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.
* pixel - {<OpenLayers.Pixel>} Pixel location of the mouse event.
*/
dragVertex: function(vertex, pixel) {
var pos = this.map.getLonLatFromViewPortPx(pixel);
var geom = vertex.geometry;
geom.move(pos.lon - geom.x, pos.lat - geom.y);
this.modified = true;
/**
* Five cases:
* 1) dragging a simple point
* 2) dragging a virtual vertex
* 3) dragging a drag handle
* 4) dragging a real vertex
* 5) dragging a radius handle
*/
if(this.feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
// dragging a simple point
this.layer.events.triggerEvent("vertexmodified", {
vertex: vertex.geometry,
feature: this.feature,
pixel: pixel
});
} else {
if(vertex._index) {
// dragging a virtual vertex
vertex.geometry.parent.addComponent(vertex.geometry,
vertex._index);
// move from virtual to real vertex
delete vertex._index;
OpenLayers.Util.removeItem(this.virtualVertices, vertex);
this.vertices.push(vertex);
} else if(vertex == this.dragHandle) {
// dragging a drag handle
this.layer.removeFeatures(this.vertices, {silent: true});
this.vertices = [];
if(this.radiusHandle) {
this.layer.destroyFeatures([this.radiusHandle], {silent: true});
this.radiusHandle = null;
}
} else if(vertex !== this.radiusHandle) {
// dragging a real vertex
this.layer.events.triggerEvent("vertexmodified", {
vertex: vertex.geometry,
feature: this.feature,
pixel: pixel
});
}
// dragging a radius handle - no special treatment
if(this.virtualVertices.length > 0) {
this.layer.destroyFeatures(this.virtualVertices, {silent: true});
this.virtualVertices = [];
}
this.layer.drawFeature(this.feature, this.standalone ? undefined :
'select');
}
// keep the vertex on top so it gets the mouseout after dragging
// this should be removed in favor of an option to draw under or
// maintain node z-index
this.layer.drawFeature(vertex);
},
/**
* Method: dragComplete
* Called by the drag handler when the feature dragging is complete.
*
* Parameters:
* vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.
*/
dragComplete: function(vertex) {
this.resetVertices();
this.setFeatureState();
this.onModification(this.feature);
this.layer.events.triggerEvent("featuremodified",
{feature: this.feature});
},
/**
* Method: setFeatureState
* Called when the feature is modified. If the current state is not
* INSERT or DELETE, the state is set to UPDATE.
*/
setFeatureState: function() {
if(this.feature.state != OpenLayers.State.INSERT &&
this.feature.state != OpenLayers.State.DELETE) {
this.feature.state = OpenLayers.State.UPDATE;
if (this.modified && this._originalGeometry) {
var feature = this.feature;
feature.modified = OpenLayers.Util.extend(feature.modified, {
geometry: this._originalGeometry
});
delete this._originalGeometry;
}
}
},
/**
* Method: resetVertices
*/
resetVertices: function() {
if(this.vertices.length > 0) {
this.layer.removeFeatures(this.vertices, {silent: true});
this.vertices = [];
}
if(this.virtualVertices.length > 0) {
this.layer.removeFeatures(this.virtualVertices, {silent: true});
this.virtualVertices = [];
}
if(this.dragHandle) {
this.layer.destroyFeatures([this.dragHandle], {silent: true});
this.dragHandle = null;
}
if(this.radiusHandle) {
this.layer.destroyFeatures([this.radiusHandle], {silent: true});
this.radiusHandle = null;
}
if(this.feature &&
this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") {
if((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) {
this.collectDragHandle();
}
if((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE |
OpenLayers.Control.ModifyFeature.RESIZE))) {
this.collectRadiusHandle();
}
if(this.mode & OpenLayers.Control.ModifyFeature.RESHAPE){
// Don't collect vertices when we're resizing
if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)){
this.collectVertices();
}
}
}
},
/**
* Method: handleKeypress
* Called by the feature handler on keypress. This is used to delete
* vertices. If the <deleteCode> property is set, vertices will
* be deleted when a feature is selected for modification and
* the mouse is over a vertex.
*
* Parameters:
* evt - {Event} Keypress event.
*/
handleKeypress: function(evt) {
var code = evt.keyCode;
// check for delete key
if(this.feature &&
OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) {
var vertex = this.layer.getFeatureFromEvent(this.handlers.drag.evt);
if (vertex &&
OpenLayers.Util.indexOf(this.vertices, vertex) != -1 &&
!this.handlers.drag.dragging && vertex.geometry.parent) {
// remove the vertex
vertex.geometry.parent.removeComponent(vertex.geometry);
this.layer.events.triggerEvent("vertexremoved", {
vertex: vertex.geometry,
feature: this.feature,
pixel: evt.xy
});
this.layer.drawFeature(this.feature, this.standalone ?
undefined : 'select');
this.modified = true;
this.resetVertices();
this.setFeatureState();
this.onModification(this.feature);
this.layer.events.triggerEvent("featuremodified",
{feature: this.feature});
}
}
},
/**
* Method: collectVertices
* Collect the vertices from the modifiable feature's geometry and push
* them on to the control's vertices array.
*/
collectVertices: function() {
this.vertices = [];
this.virtualVertices = [];
var control = this;
function collectComponentVertices(geometry) {
var i, vertex, component, len;
if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
vertex = new OpenLayers.Feature.Vector(geometry);
vertex._sketch = true;
vertex.renderIntent = control.vertexRenderIntent;
control.vertices.push(vertex);
} else {
var numVert = geometry.components.length;
if(geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
numVert -= 1;
}
for(i=0; i<numVert; ++i) {
component = geometry.components[i];
if(component.CLASS_NAME == "OpenLayers.Geometry.Point") {
vertex = new OpenLayers.Feature.Vector(component);
vertex._sketch = true;
vertex.renderIntent = control.vertexRenderIntent;
control.vertices.push(vertex);
} else {
collectComponentVertices(component);
}
}
// add virtual vertices in the middle of each edge
if (control.createVertices && geometry.CLASS_NAME != "OpenLayers.Geometry.MultiPoint") {
for(i=0, len=geometry.components.length; i<len-1; ++i) {
var prevVertex = geometry.components[i];
var nextVertex = geometry.components[i + 1];
if(prevVertex.CLASS_NAME == "OpenLayers.Geometry.Point" &&
nextVertex.CLASS_NAME == "OpenLayers.Geometry.Point") {
var x = (prevVertex.x + nextVertex.x) / 2;
var y = (prevVertex.y + nextVertex.y) / 2;
var point = new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.Point(x, y),
null, control.virtualStyle
);
// set the virtual parent and intended index
point.geometry.parent = geometry;
point._index = i + 1;
point._sketch = true;
control.virtualVertices.push(point);
}
}
}
}
}
collectComponentVertices.call(this, this.feature.geometry);
this.layer.addFeatures(this.virtualVertices, {silent: true});
this.layer.addFeatures(this.vertices, {silent: true});
},
/**
* Method: collectDragHandle
* Collect the drag handle for the selected geometry.
*/
collectDragHandle: function() {
var geometry = this.feature.geometry;
var center = geometry.getBounds().getCenterLonLat();
var originGeometry = new OpenLayers.Geometry.Point(
center.lon, center.lat
);
var origin = new OpenLayers.Feature.Vector(originGeometry);
originGeometry.move = function(x, y) {
OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
geometry.move(x, y);
};
origin._sketch = true;
this.dragHandle = origin;
this.dragHandle.renderIntent = this.vertexRenderIntent;
this.layer.addFeatures([this.dragHandle], {silent: true});
},
/**
* Method: collectRadiusHandle
* Collect the radius handle for the selected geometry.
*/
collectRadiusHandle: function() {
var geometry = this.feature.geometry;
var bounds = geometry.getBounds();
var center = bounds.getCenterLonLat();
var originGeometry = new OpenLayers.Geometry.Point(
center.lon, center.lat
);
var radiusGeometry = new OpenLayers.Geometry.Point(
bounds.right, bounds.bottom
);
var radius = new OpenLayers.Feature.Vector(radiusGeometry);
var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE);
var reshape = (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE);
var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE);
radiusGeometry.move = function(x, y) {
OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
var dx1 = this.x - originGeometry.x;
var dy1 = this.y - originGeometry.y;
var dx0 = dx1 - x;
var dy0 = dy1 - y;
if(rotate) {
var a0 = Math.atan2(dy0, dx0);
var a1 = Math.atan2(dy1, dx1);
var angle = a1 - a0;
angle *= 180 / Math.PI;
geometry.rotate(angle, originGeometry);
}
if(resize) {
var scale, ratio;
// 'resize' together with 'reshape' implies that the aspect
// ratio of the geometry will not be preserved whilst resizing
if (reshape) {
scale = dy1 / dy0;
ratio = (dx1 / dx0) / scale;
} else {
var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0));
var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1));
scale = l1 / l0;
}
geometry.resize(scale, originGeometry, ratio);
}
};
radius._sketch = true;
this.radiusHandle = radius;
this.radiusHandle.renderIntent = this.vertexRenderIntent;
this.layer.addFeatures([this.radiusHandle], {silent: true});
},
/**
* Method: setMap
* Set the map property for the control and all handlers.
*
* Parameters:
* map - {<OpenLayers.Map>} The control's map.
*/
setMap: function(map) {
this.handlers.drag.setMap(map);
OpenLayers.Control.prototype.setMap.apply(this, arguments);
},
/**
* Method: handleMapEvents
*
* Parameters:
* evt - {Object}
*/
handleMapEvents: function(evt) {
if (evt.type == "removelayer" || evt.property == "order") {
this.moveLayerToTop();
}
},
/**
* Method: moveLayerToTop
* Moves the layer for this handler to the top, so mouse events can reach
* it.
*/
moveLayerToTop: function() {
var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,
this.layer.getZIndex()) + 1;
this.layer.setZIndex(index);
},
/**
* Method: moveLayerBack
* Moves the layer back to the position determined by the map's layers
* array.
*/
moveLayerBack: function() {
var index = this.layer.getZIndex() - 1;
if (index >= this.map.Z_INDEX_BASE['Feature']) {
this.layer.setZIndex(index);
} else {
this.map.setLayerZIndex(this.layer,
this.map.getLayerIndex(this.layer));
}
},
CLASS_NAME: "OpenLayers.Control.ModifyFeature"
});
/**
* Constant: RESHAPE
* {Integer} Constant used to make the control work in reshape mode
*/
OpenLayers.Control.ModifyFeature.RESHAPE = 1;
/**
* Constant: RESIZE
* {Integer} Constant used to make the control work in resize mode
*/
OpenLayers.Control.ModifyFeature.RESIZE = 2;
/**
* Constant: ROTATE
* {Integer} Constant used to make the control work in rotate mode
*/
OpenLayers.Control.ModifyFeature.ROTATE = 4;
/**
* Constant: DRAG
* {Integer} Constant used to make the control work in drag mode
*/
OpenLayers.Control.ModifyFeature.DRAG = 8;

View File

@ -0,0 +1,227 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
*/
/**
* Class: OpenLayers.Control.MousePosition
* The MousePosition control displays geographic coordinates of the mouse
* pointer, as it is moved about the map.
*
* You can use the <prefix>- or <suffix>-properties to provide more information
* about the displayed coordinates to the user:
*
* (code)
* var mousePositionCtrl = new OpenLayers.Control.MousePosition({
* prefix: '<a target="_blank" ' +
* 'href="http://spatialreference.org/ref/epsg/4326/">' +
* 'EPSG:4326</a> coordinates: '
* }
* );
* (end code)
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.MousePosition = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: autoActivate
* {Boolean} Activate the control when it is added to a map. Default is
* true.
*/
autoActivate: true,
/**
* Property: element
* {DOMElement}
*/
element: null,
/**
* APIProperty: prefix
* {String} A string to be prepended to the current pointers coordinates
* when it is rendered. Defaults to the empty string ''.
*/
prefix: '',
/**
* APIProperty: separator
* {String} A string to be used to seperate the two coordinates from each
* other. Defaults to the string ', ', which will result in a
* rendered coordinate of e.g. '42.12, 21.22'.
*/
separator: ', ',
/**
* APIProperty: suffix
* {String} A string to be appended to the current pointers coordinates
* when it is rendered. Defaults to the empty string ''.
*/
suffix: '',
/**
* APIProperty: numDigits
* {Integer} The number of digits each coordinate shall have when being
* rendered, Defaults to 5.
*/
numDigits: 5,
/**
* APIProperty: granularity
* {Integer}
*/
granularity: 10,
/**
* APIProperty: emptyString
* {String} Set this to some value to set when the mouse is outside the
* map.
*/
emptyString: null,
/**
* Property: lastXy
* {<OpenLayers.Pixel>}
*/
lastXy: null,
/**
* APIProperty: displayProjection
* {<OpenLayers.Projection>} The projection in which the mouse position is
* displayed.
*/
displayProjection: null,
/**
* Constructor: OpenLayers.Control.MousePosition
*
* Parameters:
* options - {Object} Options for control.
*/
/**
* Method: destroy
*/
destroy: function() {
this.deactivate();
OpenLayers.Control.prototype.destroy.apply(this, arguments);
},
/**
* APIMethod: activate
*/
activate: function() {
if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {
this.map.events.register('mousemove', this, this.redraw);
this.map.events.register('mouseout', this, this.reset);
this.redraw();
return true;
} else {
return false;
}
},
/**
* APIMethod: deactivate
*/
deactivate: function() {
if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
this.map.events.unregister('mousemove', this, this.redraw);
this.map.events.unregister('mouseout', this, this.reset);
this.element.innerHTML = "";
return true;
} else {
return false;
}
},
/**
* Method: draw
* {DOMElement}
*/
draw: function() {
OpenLayers.Control.prototype.draw.apply(this, arguments);
if (!this.element) {
this.div.left = "";
this.div.top = "";
this.element = this.div;
}
return this.div;
},
/**
* Method: redraw
*/
redraw: function(evt) {
var lonLat;
if (evt == null) {
this.reset();
return;
} else {
if (this.lastXy == null ||
Math.abs(evt.xy.x - this.lastXy.x) > this.granularity ||
Math.abs(evt.xy.y - this.lastXy.y) > this.granularity)
{
this.lastXy = evt.xy;
return;
}
lonLat = this.map.getLonLatFromPixel(evt.xy);
if (!lonLat) {
// map has not yet been properly initialized
return;
}
if (this.displayProjection) {
lonLat.transform(this.map.getProjectionObject(),
this.displayProjection );
}
this.lastXy = evt.xy;
}
var newHtml = this.formatOutput(lonLat);
if (newHtml != this.element.innerHTML) {
this.element.innerHTML = newHtml;
}
},
/**
* Method: reset
*/
reset: function(evt) {
if (this.emptyString != null) {
this.element.innerHTML = this.emptyString;
}
},
/**
* Method: formatOutput
* Override to provide custom display output
*
* Parameters:
* lonLat - {<OpenLayers.LonLat>} Location to display
*/
formatOutput: function(lonLat) {
var digits = parseInt(this.numDigits);
var newHtml =
this.prefix +
lonLat.lon.toFixed(digits) +
this.separator +
lonLat.lat.toFixed(digits) +
this.suffix;
return newHtml;
},
CLASS_NAME: "OpenLayers.Control.MousePosition"
});

View File

@ -0,0 +1,57 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control/Panel.js
* @requires OpenLayers/Control/Navigation.js
* @requires OpenLayers/Control/ZoomBox.js
*/
/**
* Class: OpenLayers.Control.NavToolbar
* This Toolbar is an alternative to the Navigation control that displays
* the state of the control, and provides a UI for changing state to
* use the zoomBox via a Panel control.
*
* If you wish to change the properties of the Navigation control used
* in the NavToolbar, see:
* http://trac.openlayers.org/wiki/Toolbars#SubclassingNavToolbar
*
*
* Inherits from:
* - <OpenLayers.Control.Panel>
*/
OpenLayers.Control.NavToolbar = OpenLayers.Class(OpenLayers.Control.Panel, {
/**
* Constructor: OpenLayers.Control.NavToolbar
* Add our two mousedefaults controls.
*
* Parameters:
* options - {Object} An optional object whose properties will be used
* to extend the control.
*/
initialize: function(options) {
OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);
this.addControls([
new OpenLayers.Control.Navigation(),
new OpenLayers.Control.ZoomBox()
]);
},
/**
* Method: draw
* calls the default draw, and then activates mouse defaults.
*/
draw: function() {
var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);
if (this.defaultControl === null) {
this.defaultControl = this.controls[0];
}
return div;
},
CLASS_NAME: "OpenLayers.Control.NavToolbar"
});

View File

@ -0,0 +1,345 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control/ZoomBox.js
* @requires OpenLayers/Control/DragPan.js
* @requires OpenLayers/Handler/MouseWheel.js
* @requires OpenLayers/Handler/Click.js
*/
/**
* Class: OpenLayers.Control.Navigation
* The navigation control handles map browsing with mouse events (dragging,
* double-clicking, and scrolling the wheel). Create a new navigation
* control with the <OpenLayers.Control.Navigation> control.
*
* Note that this control is added to the map by default (if no controls
* array is sent in the options object to the <OpenLayers.Map>
* constructor).
*
* Inherits:
* - <OpenLayers.Control>
*/
OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {
/**
* Property: dragPan
* {<OpenLayers.Control.DragPan>}
*/
dragPan: null,
/**
* APIProperty: dragPanOptions
* {Object} Options passed to the DragPan control.
*/
dragPanOptions: null,
/**
* Property: pinchZoom
* {<OpenLayers.Control.PinchZoom>}
*/
pinchZoom: null,
/**
* APIProperty: pinchZoomOptions
* {Object} Options passed to the PinchZoom control.
*/
pinchZoomOptions: null,
/**
* APIProperty: documentDrag
* {Boolean} Allow panning of the map by dragging outside map viewport.
* Default is false.
*/
documentDrag: false,
/**
* Property: zoomBox
* {<OpenLayers.Control.ZoomBox>}
*/
zoomBox: null,
/**
* APIProperty: zoomBoxEnabled
* {Boolean} Whether the user can draw a box to zoom
*/
zoomBoxEnabled: true,
/**
* APIProperty: zoomWheelEnabled
* {Boolean} Whether the mousewheel should zoom the map
*/
zoomWheelEnabled: true,
/**
* Property: mouseWheelOptions
* {Object} Options passed to the MouseWheel control (only useful if
* <zoomWheelEnabled> is set to true). Default is no options for maps
* with fractionalZoom set to true, otherwise
* {cumulative: false, interval: 50, maxDelta: 6}
*/
mouseWheelOptions: null,
/**
* APIProperty: handleRightClicks
* {Boolean} Whether or not to handle right clicks. Default is false.
*/
handleRightClicks: false,
/**
* APIProperty: zoomBoxKeyMask
* {Integer} <OpenLayers.Handler> key code of the key, which has to be
* pressed, while drawing the zoom box with the mouse on the screen.
* You should probably set handleRightClicks to true if you use this
* with MOD_CTRL, to disable the context menu for machines which use
* CTRL-Click as a right click.
* Default: <OpenLayers.Handler.MOD_SHIFT>
*/
zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,
/**
* APIProperty: autoActivate
* {Boolean} Activate the control when it is added to a map. Default is
* true.
*/
autoActivate: true,
/**
* Constructor: OpenLayers.Control.Navigation
* Create a new navigation control
*
* Parameters:
* options - {Object} An optional object whose properties will be set on
* the control
*/
initialize: function(options) {
this.handlers = {};
OpenLayers.Control.prototype.initialize.apply(this, arguments);
},
/**
* Method: destroy
* The destroy method is used to perform any clean up before the control
* is dereferenced. Typically this is where event listeners are removed
* to prevent memory leaks.
*/
destroy: function() {
this.deactivate();
if (this.dragPan) {
this.dragPan.destroy();
}
this.dragPan = null;
if (this.zoomBox) {
this.zoomBox.destroy();
}
this.zoomBox = null;
if (this.pinchZoom) {
this.pinchZoom.destroy();
}
this.pinchZoom = null;
OpenLayers.Control.prototype.destroy.apply(this,arguments);
},
/**
* Method: activate
*/
activate: function() {
this.dragPan.activate();
if (this.zoomWheelEnabled) {
this.handlers.wheel.activate();
}
this.handlers.click.activate();
if (this.zoomBoxEnabled) {
this.zoomBox.activate();
}
if (this.pinchZoom) {
this.pinchZoom.activate();
}
return OpenLayers.Control.prototype.activate.apply(this,arguments);
},
/**
* Method: deactivate
*/
deactivate: function() {
if (this.pinchZoom) {
this.pinchZoom.deactivate();
}
this.zoomBox.deactivate();
this.dragPan.deactivate();
this.handlers.click.deactivate();
this.handlers.wheel.deactivate();
return OpenLayers.Control.prototype.deactivate.apply(this,arguments);
},
/**
* Method: draw
*/
draw: function() {
// disable right mouse context menu for support of right click events
if (this.handleRightClicks) {
this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False;
}
var clickCallbacks = {
'click': this.defaultClick,
'dblclick': this.defaultDblClick,
'dblrightclick': this.defaultDblRightClick
};
var clickOptions = {
'double': true,
'stopDouble': true
};
this.handlers.click = new OpenLayers.Handler.Click(
this, clickCallbacks, clickOptions
);
this.dragPan = new OpenLayers.Control.DragPan(
OpenLayers.Util.extend({
map: this.map,
documentDrag: this.documentDrag
}, this.dragPanOptions)
);
this.zoomBox = new OpenLayers.Control.ZoomBox(
{map: this.map, keyMask: this.zoomBoxKeyMask});
this.dragPan.draw();
this.zoomBox.draw();
var wheelOptions = this.map.fractionalZoom ? {} : {
cumulative: false,
interval: 50,
maxDelta: 6
};
this.handlers.wheel = new OpenLayers.Handler.MouseWheel(
this, {up : this.wheelUp, down: this.wheelDown},
OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions)
);
if (OpenLayers.Control.PinchZoom) {
this.pinchZoom = new OpenLayers.Control.PinchZoom(
OpenLayers.Util.extend(
{map: this.map}, this.pinchZoomOptions));
}
},
/**
* Method: defaultClick
*
* Parameters:
* evt - {Event}
*/
defaultClick: function (evt) {
if (evt.lastTouches && evt.lastTouches.length == 2) {
this.map.zoomOut();
}
},
/**
* Method: defaultDblClick
*
* Parameters:
* evt - {Event}
*/
defaultDblClick: function (evt) {
this.map.zoomTo(this.map.zoom + 1, evt.xy);
},
/**
* Method: defaultDblRightClick
*
* Parameters:
* evt - {Event}
*/
defaultDblRightClick: function (evt) {
this.map.zoomTo(this.map.zoom - 1, evt.xy);
},
/**
* Method: wheelChange
*
* Parameters:
* evt - {Event}
* deltaZ - {Integer}
*/
wheelChange: function(evt, deltaZ) {
if (!this.map.fractionalZoom) {
deltaZ = Math.round(deltaZ);
}
var currentZoom = this.map.getZoom(),
newZoom = currentZoom + deltaZ;
newZoom = Math.max(newZoom, 0);
newZoom = Math.min(newZoom, this.map.getNumZoomLevels());
if (newZoom === currentZoom) {
return;
}
this.map.zoomTo(newZoom, evt.xy);
},
/**
* Method: wheelUp
* User spun scroll wheel up
*
* Parameters:
* evt - {Event}
* delta - {Integer}
*/
wheelUp: function(evt, delta) {
this.wheelChange(evt, delta || 1);
},
/**
* Method: wheelDown
* User spun scroll wheel down
*
* Parameters:
* evt - {Event}
* delta - {Integer}
*/
wheelDown: function(evt, delta) {
this.wheelChange(evt, delta || -1);
},
/**
* Method: disableZoomBox
*/
disableZoomBox : function() {
this.zoomBoxEnabled = false;
this.zoomBox.deactivate();
},
/**
* Method: enableZoomBox
*/
enableZoomBox : function() {
this.zoomBoxEnabled = true;
if (this.active) {
this.zoomBox.activate();
}
},
/**
* Method: disableZoomWheel
*/
disableZoomWheel : function() {
this.zoomWheelEnabled = false;
this.handlers.wheel.deactivate();
},
/**
* Method: enableZoomWheel
*/
enableZoomWheel : function() {
this.zoomWheelEnabled = true;
if (this.active) {
this.handlers.wheel.activate();
}
},
CLASS_NAME: "OpenLayers.Control.Navigation"
});

View File

@ -0,0 +1,423 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Control/Button.js
*/
/**
* Class: OpenLayers.Control.NavigationHistory
* A navigation history control. This is a meta-control, that creates two
* dependent controls: <previous> and <next>. Call the trigger method
* on the <previous> and <next> controls to restore previous and next
* history states. The previous and next controls will become active
* when there are available states to restore and will become deactive
* when there are no states to restore.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.NavigationHistory = OpenLayers.Class(OpenLayers.Control, {
/**
* Property: type
* {String} Note that this control is not intended to be added directly
* to a control panel. Instead, add the sub-controls previous and
* next. These sub-controls are button type controls that activate
* and deactivate themselves. If this parent control is added to
* a panel, it will act as a toggle.
*/
type: OpenLayers.Control.TYPE_TOGGLE,
/**
* APIProperty: previous
* {<OpenLayers.Control>} A button type control whose trigger method restores
* the previous state managed by this control.
*/
previous: null,
/**
* APIProperty: previousOptions
* {Object} Set this property on the options argument of the constructor
* to set optional properties on the <previous> control.
*/
previousOptions: null,
/**
* APIProperty: next
* {<OpenLayers.Control>} A button type control whose trigger method restores
* the next state managed by this control.
*/
next: null,
/**
* APIProperty: nextOptions
* {Object} Set this property on the options argument of the constructor
* to set optional properties on the <next> control.
*/
nextOptions: null,
/**
* APIProperty: limit
* {Integer} Optional limit on the number of history items to retain. If
* null, there is no limit. Default is 50.
*/
limit: 50,
/**
* APIProperty: autoActivate
* {Boolean} Activate the control when it is added to a map. Default is
* true.
*/
autoActivate: true,
/**
* Property: clearOnDeactivate
* {Boolean} Clear the history when the control is deactivated. Default
* is false.
*/
clearOnDeactivate: false,
/**
* Property: registry
* {Object} An object with keys corresponding to event types. Values
* are functions that return an object representing the current state.
*/
registry: null,
/**
* Property: nextStack
* {Array} Array of items in the history.
*/
nextStack: null,
/**
* Property: previousStack
* {Array} List of items in the history. First item represents the current
* state.
*/
previousStack: null,
/**
* Property: listeners
* {Object} An object containing properties corresponding to event types.
* This object is used to configure the control and is modified on
* construction.
*/
listeners: null,
/**
* Property: restoring
* {Boolean} Currently restoring a history state. This is set to true
* before calling restore and set to false after restore returns.
*/
restoring: false,
/**
* Constructor: OpenLayers.Control.NavigationHistory
*
* Parameters:
* options - {Object} An optional object whose properties will be used
* to extend the control.
*/
initialize: function(options) {
OpenLayers.Control.prototype.initialize.apply(this, [options]);
this.registry = OpenLayers.Util.extend({
"moveend": this.getState
}, this.registry);
var previousOptions = {
trigger: OpenLayers.Function.bind(this.previousTrigger, this),
displayClass: this.displayClass + " " + this.displayClass + "Previous"
};
OpenLayers.Util.extend(previousOptions, this.previousOptions);
this.previous = new OpenLayers.Control.Button(previousOptions);
var nextOptions = {
trigger: OpenLayers.Function.bind(this.nextTrigger, this),
displayClass: this.displayClass + " " + this.displayClass + "Next"
};
OpenLayers.Util.extend(nextOptions, this.nextOptions);
this.next = new OpenLayers.Control.Button(nextOptions);
this.clear();
},
/**
* Method: onPreviousChange
* Called when the previous history stack changes.
*
* Parameters:
* state - {Object} An object representing the state to be restored
* if previous is triggered again or null if no previous states remain.
* length - {Integer} The number of remaining previous states that can
* be restored.
*/
onPreviousChange: function(state, length) {
if(state && !this.previous.active) {
this.previous.activate();
} else if(!state && this.previous.active) {
this.previous.deactivate();
}
},
/**
* Method: onNextChange
* Called when the next history stack changes.
*
* Parameters:
* state - {Object} An object representing the state to be restored
* if next is triggered again or null if no next states remain.
* length - {Integer} The number of remaining next states that can
* be restored.
*/
onNextChange: function(state, length) {
if(state && !this.next.active) {
this.next.activate();
} else if(!state && this.next.active) {
this.next.deactivate();
}
},
/**
* APIMethod: destroy
* Destroy the control.
*/
destroy: function() {
OpenLayers.Control.prototype.destroy.apply(this);
this.previous.destroy();
this.next.destroy();
this.deactivate();
for(var prop in this) {
this[prop] = null;
}
},
/**
* Method: setMap
* Set the map property for the control and <previous> and <next> child
* controls.
*
* Parameters:
* map - {<OpenLayers.Map>}
*/
setMap: function(map) {
this.map = map;
this.next.setMap(map);
this.previous.setMap(map);
},
/**
* Method: draw
* Called when the control is added to the map.
*/
draw: function() {
OpenLayers.Control.prototype.draw.apply(this, arguments);
this.next.draw();
this.previous.draw();
},
/**
* Method: previousTrigger
* Restore the previous state. If no items are in the previous history
* stack, this has no effect.
*
* Returns:
* {Object} Item representing state that was restored. Undefined if no
* items are in the previous history stack.
*/
previousTrigger: function() {
var current = this.previousStack.shift();
var state = this.previousStack.shift();
if(state != undefined) {
this.nextStack.unshift(current);
this.previousStack.unshift(state);
this.restoring = true;
this.restore(state);
this.restoring = false;
this.onNextChange(this.nextStack[0], this.nextStack.length);
this.onPreviousChange(
this.previousStack[1], this.previousStack.length - 1
);
} else {
this.previousStack.unshift(current);
}
return state;
},
/**
* APIMethod: nextTrigger
* Restore the next state. If no items are in the next history
* stack, this has no effect. The next history stack is populated
* as states are restored from the previous history stack.
*
* Returns:
* {Object} Item representing state that was restored. Undefined if no
* items are in the next history stack.
*/
nextTrigger: function() {
var state = this.nextStack.shift();
if(state != undefined) {
this.previousStack.unshift(state);
this.restoring = true;
this.restore(state);
this.restoring = false;
this.onNextChange(this.nextStack[0], this.nextStack.length);
this.onPreviousChange(
this.previousStack[1], this.previousStack.length - 1
);
}
return state;
},
/**
* APIMethod: clear
* Clear history.
*/
clear: function() {
this.previousStack = [];
this.previous.deactivate();
this.nextStack = [];
this.next.deactivate();
},
/**
* Method: getState
* Get the current state and return it.
*
* Returns:
* {Object} An object representing the current state.
*/
getState: function() {
return {
center: this.map.getCenter(),
resolution: this.map.getResolution(),
projection: this.map.getProjectionObject(),
units: this.map.getProjectionObject().getUnits() ||
this.map.units || this.map.baseLayer.units
};
},
/**
* Method: restore
* Update the state with the given object.
*
* Parameters:
* state - {Object} An object representing the state to restore.
*/
restore: function(state) {
var center, zoom;
if (this.map.getProjectionObject() == state.projection) {
zoom = this.map.getZoomForResolution(state.resolution);
center = state.center;
} else {
center = state.center.clone();
center.transform(state.projection, this.map.getProjectionObject());
var sourceUnits = state.units;
var targetUnits = this.map.getProjectionObject().getUnits() ||
this.map.units || this.map.baseLayer.units;
var resolutionFactor = sourceUnits && targetUnits ?
OpenLayers.INCHES_PER_UNIT[sourceUnits] / OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;
zoom = this.map.getZoomForResolution(resolutionFactor*state.resolution);
}
this.map.setCenter(center, zoom);
},
/**
* Method: setListeners
* Sets functions to be registered in the listeners object.
*/
setListeners: function() {
this.listeners = {};
for(var type in this.registry) {
this.listeners[type] = OpenLayers.Function.bind(function() {
if(!this.restoring) {
var state = this.registry[type].apply(this, arguments);
this.previousStack.unshift(state);
if(this.previousStack.length > 1) {
this.onPreviousChange(
this.previousStack[1], this.previousStack.length - 1
);
}
if(this.previousStack.length > (this.limit + 1)) {
this.previousStack.pop();
}
if(this.nextStack.length > 0) {
this.nextStack = [];
this.onNextChange(null, 0);
}
}
return true;
}, this);
}
},
/**
* APIMethod: activate
* Activate the control. This registers any listeners.
*
* Returns:
* {Boolean} Control successfully activated.
*/
activate: function() {
var activated = false;
if(this.map) {
if(OpenLayers.Control.prototype.activate.apply(this)) {
if(this.listeners == null) {
this.setListeners();
}
for(var type in this.listeners) {
this.map.events.register(type, this, this.listeners[type]);
}
activated = true;
if(this.previousStack.length == 0) {
this.initStack();
}
}
}
return activated;
},
/**
* Method: initStack
* Called after the control is activated if the previous history stack is
* empty.
*/
initStack: function() {
if(this.map.getCenter()) {
this.listeners.moveend();
}
},
/**
* APIMethod: deactivate
* Deactivate the control. This unregisters any listeners.
*
* Returns:
* {Boolean} Control successfully deactivated.
*/
deactivate: function() {
var deactivated = false;
if(this.map) {
if(OpenLayers.Control.prototype.deactivate.apply(this)) {
for(var type in this.listeners) {
this.map.events.unregister(
type, this, this.listeners[type]
);
}
if(this.clearOnDeactivate) {
this.clear();
}
deactivated = true;
}
}
return deactivated;
},
CLASS_NAME: "OpenLayers.Control.NavigationHistory"
});

View File

@ -0,0 +1,750 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/BaseTypes.js
* @requires OpenLayers/Events/buttonclick.js
* @requires OpenLayers/Map.js
* @requires OpenLayers/Handler/Click.js
* @requires OpenLayers/Handler/Drag.js
*/
/**
* Class: OpenLayers.Control.OverviewMap
* The OverMap control creates a small overview map, useful to display the
* extent of a zoomed map and your main map and provide additional
* navigation options to the User. By default the overview map is drawn in
* the lower right corner of the main map. Create a new overview map with the
* <OpenLayers.Control.OverviewMap> constructor.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control, {
/**
* Property: element
* {DOMElement} The DOM element that contains the overview map
*/
element: null,
/**
* APIProperty: ovmap
* {<OpenLayers.Map>} A reference to the overview map itself.
*/
ovmap: null,
/**
* APIProperty: size
* {<OpenLayers.Size>} The overvew map size in pixels. Note that this is
* the size of the map itself - the element that contains the map (default
* class name olControlOverviewMapElement) may have padding or other style
* attributes added via CSS.
*/
size: {w: 180, h: 90},
/**
* APIProperty: layers
* {Array(<OpenLayers.Layer>)} Ordered list of layers in the overview map.
* If none are sent at construction, the base layer for the main map is used.
*/
layers: null,
/**
* APIProperty: minRectSize
* {Integer} The minimum width or height (in pixels) of the extent
* rectangle on the overview map. When the extent rectangle reaches
* this size, it will be replaced depending on the value of the
* <minRectDisplayClass> property. Default is 15 pixels.
*/
minRectSize: 15,
/**
* APIProperty: minRectDisplayClass
* {String} Replacement style class name for the extent rectangle when
* <minRectSize> is reached. This string will be suffixed on to the
* displayClass. Default is "RectReplacement".
*
* Example CSS declaration:
* (code)
* .olControlOverviewMapRectReplacement {
* overflow: hidden;
* cursor: move;
* background-image: url("img/overview_replacement.gif");
* background-repeat: no-repeat;
* background-position: center;
* }
* (end)
*/
minRectDisplayClass: "RectReplacement",
/**
* APIProperty: minRatio
* {Float} The ratio of the overview map resolution to the main map
* resolution at which to zoom farther out on the overview map.
*/
minRatio: 8,
/**
* APIProperty: maxRatio
* {Float} The ratio of the overview map resolution to the main map
* resolution at which to zoom farther in on the overview map.
*/
maxRatio: 32,
/**
* APIProperty: mapOptions
* {Object} An object containing any non-default properties to be sent to
* the overview map's map constructor. These should include any
* non-default options that the main map was constructed with.
*/
mapOptions: null,
/**
* APIProperty: autoPan
* {Boolean} Always pan the overview map, so the extent marker remains in
* the center. Default is false. If true, when you drag the extent
* marker, the overview map will update itself so the marker returns
* to the center.
*/
autoPan: false,
/**
* Property: handlers
* {Object}
*/
handlers: null,
/**
* Property: resolutionFactor
* {Object}
*/
resolutionFactor: 1,
/**
* APIProperty: maximized
* {Boolean} Start as maximized (visible). Defaults to false.
*/
maximized: false,
/**
* APIProperty: maximizeTitle
* {String} This property is used for showing a tooltip over the
* maximize div. Defaults to "" (no title).
*/
maximizeTitle: "",
/**
* APIProperty: minimizeTitle
* {String} This property is used for showing a tooltip over the
* minimize div. Defaults to "" (no title).
*/
minimizeTitle: "",
/**
* Constructor: OpenLayers.Control.OverviewMap
* Create a new overview map
*
* Parameters:
* options - {Object} Properties of this object will be set on the overview
* map object. Note, to set options on the map object contained in this
* control, set <mapOptions> as one of the options properties.
*/
initialize: function(options) {
this.layers = [];
this.handlers = {};
OpenLayers.Control.prototype.initialize.apply(this, [options]);
},
/**
* APIMethod: destroy
* Deconstruct the control
*/
destroy: function() {
if (!this.mapDiv) { // we've already been destroyed
return;
}
if (this.handlers.click) {
this.handlers.click.destroy();
}
if (this.handlers.drag) {
this.handlers.drag.destroy();
}
this.ovmap && this.ovmap.viewPortDiv.removeChild(this.extentRectangle);
this.extentRectangle = null;
if (this.rectEvents) {
this.rectEvents.destroy();
this.rectEvents = null;
}
if (this.ovmap) {
this.ovmap.destroy();
this.ovmap = null;
}
this.element.removeChild(this.mapDiv);
this.mapDiv = null;
this.div.removeChild(this.element);
this.element = null;
if (this.maximizeDiv) {
this.div.removeChild(this.maximizeDiv);
this.maximizeDiv = null;
}
if (this.minimizeDiv) {
this.div.removeChild(this.minimizeDiv);
this.minimizeDiv = null;
}
this.map.events.un({
buttonclick: this.onButtonClick,
moveend: this.update,
changebaselayer: this.baseLayerDraw,
scope: this
});
OpenLayers.Control.prototype.destroy.apply(this, arguments);
},
/**
* Method: draw
* Render the control in the browser.
*/
draw: function() {
OpenLayers.Control.prototype.draw.apply(this, arguments);
if (this.layers.length === 0) {
if (this.map.baseLayer) {
var layer = this.map.baseLayer.clone();
this.layers = [layer];
} else {
this.map.events.register("changebaselayer", this, this.baseLayerDraw);
return this.div;
}
}
// create overview map DOM elements
this.element = document.createElement('div');
this.element.className = this.displayClass + 'Element';
this.element.style.display = 'none';
this.mapDiv = document.createElement('div');
this.mapDiv.style.width = this.size.w + 'px';
this.mapDiv.style.height = this.size.h + 'px';
this.mapDiv.style.position = 'relative';
this.mapDiv.style.overflow = 'hidden';
this.mapDiv.id = OpenLayers.Util.createUniqueID('overviewMap');
this.extentRectangle = document.createElement('div');
this.extentRectangle.style.position = 'absolute';
this.extentRectangle.style.zIndex = 1000; //HACK
this.extentRectangle.className = this.displayClass+'ExtentRectangle';
this.element.appendChild(this.mapDiv);
this.div.appendChild(this.element);
// Optionally add min/max buttons if the control will go in the
// map viewport.
if(!this.outsideViewport) {
this.div.className += " " + this.displayClass + 'Container';
// maximize button div
var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png');
this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
this.displayClass + 'MaximizeButton',
null,
null,
img,
'absolute');
this.maximizeDiv.style.display = 'none';
this.maximizeDiv.className = this.displayClass + 'MaximizeButton olButton';
if (this.maximizeTitle) {
this.maximizeDiv.title = this.maximizeTitle;
}
this.div.appendChild(this.maximizeDiv);
// minimize button div
var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png');
this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
'OpenLayers_Control_minimizeDiv',
null,
null,
img,
'absolute');
this.minimizeDiv.style.display = 'none';
this.minimizeDiv.className = this.displayClass + 'MinimizeButton olButton';
if (this.minimizeTitle) {
this.minimizeDiv.title = this.minimizeTitle;
}
this.div.appendChild(this.minimizeDiv);
this.minimizeControl();
} else {
// show the overview map
this.element.style.display = '';
}
if(this.map.getExtent()) {
this.update();
}
this.map.events.on({
buttonclick: this.onButtonClick,
moveend: this.update,
scope: this
});
if (this.maximized) {
this.maximizeControl();
}
return this.div;
},
/**
* Method: baseLayerDraw
* Draw the base layer - called if unable to complete in the initial draw
*/
baseLayerDraw: function() {
this.draw();
this.map.events.unregister("changebaselayer", this, this.baseLayerDraw);
},
/**
* Method: rectDrag
* Handle extent rectangle drag
*
* Parameters:
* px - {<OpenLayers.Pixel>} The pixel location of the drag.
*/
rectDrag: function(px) {
var deltaX = this.handlers.drag.last.x - px.x;
var deltaY = this.handlers.drag.last.y - px.y;
if(deltaX != 0 || deltaY != 0) {
var rectTop = this.rectPxBounds.top;
var rectLeft = this.rectPxBounds.left;
var rectHeight = Math.abs(this.rectPxBounds.getHeight());
var rectWidth = this.rectPxBounds.getWidth();
// don't allow dragging off of parent element
var newTop = Math.max(0, (rectTop - deltaY));
newTop = Math.min(newTop,
this.ovmap.size.h - this.hComp - rectHeight);
var newLeft = Math.max(0, (rectLeft - deltaX));
newLeft = Math.min(newLeft,
this.ovmap.size.w - this.wComp - rectWidth);
this.setRectPxBounds(new OpenLayers.Bounds(newLeft,
newTop + rectHeight,
newLeft + rectWidth,
newTop));
}
},
/**
* Method: mapDivClick
* Handle browser events
*
* Parameters:
* evt - {<OpenLayers.Event>} evt
*/
mapDivClick: function(evt) {
var pxCenter = this.rectPxBounds.getCenterPixel();
var deltaX = evt.xy.x - pxCenter.x;
var deltaY = evt.xy.y - pxCenter.y;
var top = this.rectPxBounds.top;
var left = this.rectPxBounds.left;
var height = Math.abs(this.rectPxBounds.getHeight());
var width = this.rectPxBounds.getWidth();
var newTop = Math.max(0, (top + deltaY));
newTop = Math.min(newTop, this.ovmap.size.h - height);
var newLeft = Math.max(0, (left + deltaX));
newLeft = Math.min(newLeft, this.ovmap.size.w - width);
this.setRectPxBounds(new OpenLayers.Bounds(newLeft,
newTop + height,
newLeft + width,
newTop));
this.updateMapToRect();
},
/**
* Method: onButtonClick
*
* Parameters:
* evt - {Event}
*/
onButtonClick: function(evt) {
if (evt.buttonElement === this.minimizeDiv) {
this.minimizeControl();
} else if (evt.buttonElement === this.maximizeDiv) {
this.maximizeControl();
}
},
/**
* Method: maximizeControl
* Unhide the control. Called when the control is in the map viewport.
*
* Parameters:
* e - {<OpenLayers.Event>}
*/
maximizeControl: function(e) {
this.element.style.display = '';
this.showToggle(false);
if (e != null) {
OpenLayers.Event.stop(e);
}
},
/**
* Method: minimizeControl
* Hide all the contents of the control, shrink the size,
* add the maximize icon
*
* Parameters:
* e - {<OpenLayers.Event>}
*/
minimizeControl: function(e) {
this.element.style.display = 'none';
this.showToggle(true);
if (e != null) {
OpenLayers.Event.stop(e);
}
},
/**
* Method: showToggle
* Hide/Show the toggle depending on whether the control is minimized
*
* Parameters:
* minimize - {Boolean}
*/
showToggle: function(minimize) {
if (this.maximizeDiv) {
this.maximizeDiv.style.display = minimize ? '' : 'none';
}
if (this.minimizeDiv) {
this.minimizeDiv.style.display = minimize ? 'none' : '';
}
},
/**
* Method: update
* Update the overview map after layers move.
*/
update: function() {
if(this.ovmap == null) {
this.createMap();
}
if(this.autoPan || !this.isSuitableOverview()) {
this.updateOverview();
}
// update extent rectangle
this.updateRectToMap();
},
/**
* Method: isSuitableOverview
* Determines if the overview map is suitable given the extent and
* resolution of the main map.
*/
isSuitableOverview: function() {
var mapExtent = this.map.getExtent();
var maxExtent = this.map.getMaxExtent();
var testExtent = new OpenLayers.Bounds(
Math.max(mapExtent.left, maxExtent.left),
Math.max(mapExtent.bottom, maxExtent.bottom),
Math.min(mapExtent.right, maxExtent.right),
Math.min(mapExtent.top, maxExtent.top));
if (this.ovmap.getProjection() != this.map.getProjection()) {
testExtent = testExtent.transform(
this.map.getProjectionObject(),
this.ovmap.getProjectionObject() );
}
var resRatio = this.ovmap.getResolution() / this.map.getResolution();
return ((resRatio > this.minRatio) &&
(resRatio <= this.maxRatio) &&
(this.ovmap.getExtent().containsBounds(testExtent)));
},
/**
* Method updateOverview
* Called by <update> if <isSuitableOverview> returns true
*/
updateOverview: function() {
var mapRes = this.map.getResolution();
var targetRes = this.ovmap.getResolution();
var resRatio = targetRes / mapRes;
if(resRatio > this.maxRatio) {
// zoom in overview map
targetRes = this.minRatio * mapRes;
} else if(resRatio <= this.minRatio) {
// zoom out overview map
targetRes = this.maxRatio * mapRes;
}
var center;
if (this.ovmap.getProjection() != this.map.getProjection()) {
center = this.map.center.clone();
center.transform(this.map.getProjectionObject(),
this.ovmap.getProjectionObject() );
} else {
center = this.map.center;
}
this.ovmap.setCenter(center, this.ovmap.getZoomForResolution(
targetRes * this.resolutionFactor));
this.updateRectToMap();
},
/**
* Method: createMap
* Construct the map that this control contains
*/
createMap: function() {
// create the overview map
var options = OpenLayers.Util.extend(
{controls: [], maxResolution: 'auto',
fallThrough: false}, this.mapOptions);
this.ovmap = new OpenLayers.Map(this.mapDiv, options);
this.ovmap.viewPortDiv.appendChild(this.extentRectangle);
// prevent ovmap from being destroyed when the page unloads, because
// the OverviewMap control has to do this (and does it).
OpenLayers.Event.stopObserving(window, 'unload', this.ovmap.unloadDestroy);
this.ovmap.addLayers(this.layers);
this.ovmap.zoomToMaxExtent();
// check extent rectangle border width
this.wComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
'border-left-width')) +
parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
'border-right-width'));
this.wComp = (this.wComp) ? this.wComp : 2;
this.hComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
'border-top-width')) +
parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
'border-bottom-width'));
this.hComp = (this.hComp) ? this.hComp : 2;
this.handlers.drag = new OpenLayers.Handler.Drag(
this, {move: this.rectDrag, done: this.updateMapToRect},
{map: this.ovmap}
);
this.handlers.click = new OpenLayers.Handler.Click(
this, {
"click": this.mapDivClick
},{
"single": true, "double": false,
"stopSingle": true, "stopDouble": true,
"pixelTolerance": 1,
map: this.ovmap
}
);
this.handlers.click.activate();
this.rectEvents = new OpenLayers.Events(this, this.extentRectangle,
null, true);
this.rectEvents.register("mouseover", this, function(e) {
if(!this.handlers.drag.active && !this.map.dragging) {
this.handlers.drag.activate();
}
});
this.rectEvents.register("mouseout", this, function(e) {
if(!this.handlers.drag.dragging) {
this.handlers.drag.deactivate();
}
});
if (this.ovmap.getProjection() != this.map.getProjection()) {
var sourceUnits = this.map.getProjectionObject().getUnits() ||
this.map.units || this.map.baseLayer.units;
var targetUnits = this.ovmap.getProjectionObject().getUnits() ||
this.ovmap.units || this.ovmap.baseLayer.units;
this.resolutionFactor = sourceUnits && targetUnits ?
OpenLayers.INCHES_PER_UNIT[sourceUnits] /
OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;
}
},
/**
* Method: updateRectToMap
* Updates the extent rectangle position and size to match the map extent
*/
updateRectToMap: function() {
// If the projections differ we need to reproject
var bounds;
if (this.ovmap.getProjection() != this.map.getProjection()) {
bounds = this.map.getExtent().transform(
this.map.getProjectionObject(),
this.ovmap.getProjectionObject() );
} else {
bounds = this.map.getExtent();
}
var pxBounds = this.getRectBoundsFromMapBounds(bounds);
if (pxBounds) {
this.setRectPxBounds(pxBounds);
}
},
/**
* Method: updateMapToRect
* Updates the map extent to match the extent rectangle position and size
*/
updateMapToRect: function() {
var lonLatBounds = this.getMapBoundsFromRectBounds(this.rectPxBounds);
if (this.ovmap.getProjection() != this.map.getProjection()) {
lonLatBounds = lonLatBounds.transform(
this.ovmap.getProjectionObject(),
this.map.getProjectionObject() );
}
this.map.panTo(lonLatBounds.getCenterLonLat());
},
/**
* Method: setRectPxBounds
* Set extent rectangle pixel bounds.
*
* Parameters:
* pxBounds - {<OpenLayers.Bounds>}
*/
setRectPxBounds: function(pxBounds) {
var top = Math.max(pxBounds.top, 0);
var left = Math.max(pxBounds.left, 0);
var bottom = Math.min(pxBounds.top + Math.abs(pxBounds.getHeight()),
this.ovmap.size.h - this.hComp);
var right = Math.min(pxBounds.left + pxBounds.getWidth(),
this.ovmap.size.w - this.wComp);
var width = Math.max(right - left, 0);
var height = Math.max(bottom - top, 0);
if(width < this.minRectSize || height < this.minRectSize) {
this.extentRectangle.className = this.displayClass +
this.minRectDisplayClass;
var rLeft = left + (width / 2) - (this.minRectSize / 2);
var rTop = top + (height / 2) - (this.minRectSize / 2);
this.extentRectangle.style.top = Math.round(rTop) + 'px';
this.extentRectangle.style.left = Math.round(rLeft) + 'px';
this.extentRectangle.style.height = this.minRectSize + 'px';
this.extentRectangle.style.width = this.minRectSize + 'px';
} else {
this.extentRectangle.className = this.displayClass +
'ExtentRectangle';
this.extentRectangle.style.top = Math.round(top) + 'px';
this.extentRectangle.style.left = Math.round(left) + 'px';
this.extentRectangle.style.height = Math.round(height) + 'px';
this.extentRectangle.style.width = Math.round(width) + 'px';
}
this.rectPxBounds = new OpenLayers.Bounds(
Math.round(left), Math.round(bottom),
Math.round(right), Math.round(top)
);
},
/**
* Method: getRectBoundsFromMapBounds
* Get the rect bounds from the map bounds.
*
* Parameters:
* lonLatBounds - {<OpenLayers.Bounds>}
*
* Returns:
* {<OpenLayers.Bounds>}A bounds which is the passed-in map lon/lat extent
* translated into pixel bounds for the overview map
*/
getRectBoundsFromMapBounds: function(lonLatBounds) {
var leftBottomPx = this.getOverviewPxFromLonLat({
lon: lonLatBounds.left,
lat: lonLatBounds.bottom
});
var rightTopPx = this.getOverviewPxFromLonLat({
lon: lonLatBounds.right,
lat: lonLatBounds.top
});
var bounds = null;
if (leftBottomPx && rightTopPx) {
bounds = new OpenLayers.Bounds(leftBottomPx.x, leftBottomPx.y,
rightTopPx.x, rightTopPx.y);
}
return bounds;
},
/**
* Method: getMapBoundsFromRectBounds
* Get the map bounds from the rect bounds.
*
* Parameters:
* pxBounds - {<OpenLayers.Bounds>}
*
* Returns:
* {<OpenLayers.Bounds>} Bounds which is the passed-in overview rect bounds
* translated into lon/lat bounds for the overview map
*/
getMapBoundsFromRectBounds: function(pxBounds) {
var leftBottomLonLat = this.getLonLatFromOverviewPx({
x: pxBounds.left,
y: pxBounds.bottom
});
var rightTopLonLat = this.getLonLatFromOverviewPx({
x: pxBounds.right,
y: pxBounds.top
});
return new OpenLayers.Bounds(leftBottomLonLat.lon, leftBottomLonLat.lat,
rightTopLonLat.lon, rightTopLonLat.lat);
},
/**
* Method: getLonLatFromOverviewPx
* Get a map location from a pixel location
*
* Parameters:
* overviewMapPx - {<OpenLayers.Pixel>|Object} OpenLayers.Pixel or
* an object with a
* 'x' and 'y' properties.
*
* Returns:
* {Object} Location which is the passed-in overview map
* OpenLayers.Pixel, translated into lon/lat by the overview
* map. An object with a 'lon' and 'lat' properties.
*/
getLonLatFromOverviewPx: function(overviewMapPx) {
var size = this.ovmap.size;
var res = this.ovmap.getResolution();
var center = this.ovmap.getExtent().getCenterLonLat();
var deltaX = overviewMapPx.x - (size.w / 2);
var deltaY = overviewMapPx.y - (size.h / 2);
return {
lon: center.lon + deltaX * res,
lat: center.lat - deltaY * res
};
},
/**
* Method: getOverviewPxFromLonLat
* Get a pixel location from a map location
*
* Parameters:
* lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
* object with a 'lon' and 'lat' properties.
*
* Returns:
* {Object} Location which is the passed-in OpenLayers.LonLat,
* translated into overview map pixels
*/
getOverviewPxFromLonLat: function(lonlat) {
var res = this.ovmap.getResolution();
var extent = this.ovmap.getExtent();
if (extent) {
return {
x: Math.round(1/res * (lonlat.lon - extent.left)),
y: Math.round(1/res * (extent.top - lonlat.lat))
};
}
},
CLASS_NAME: 'OpenLayers.Control.OverviewMap'
});

View File

@ -0,0 +1,95 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control/Button.js
*/
/**
* Class: OpenLayers.Control.Pan
* The Pan control is a single button to pan the map in one direction. For
* a more complete control see <OpenLayers.Control.PanPanel>.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.Pan = OpenLayers.Class(OpenLayers.Control.Button, {
/**
* APIProperty: slideFactor
* {Integer} Number of pixels by which we'll pan the map in any direction
* on clicking the arrow buttons, defaults to 50. If you want to pan
* by some ratio of the map dimensions, use <slideRatio> instead.
*/
slideFactor: 50,
/**
* APIProperty: slideRatio
* {Number} The fraction of map width/height by which we'll pan the map
* on clicking the arrow buttons. Default is null. If set, will
* override <slideFactor>. E.g. if slideRatio is .5, then Pan Up will
* pan up half the map height.
*/
slideRatio: null,
/**
* Property: direction
* {String} in {'North', 'South', 'East', 'West'}
*/
direction: null,
/**
* Constructor: OpenLayers.Control.Pan
* Control which handles the panning (in any of the cardinal directions)
* of the map by a set px distance.
*
* Parameters:
* direction - {String} The direction this button should pan.
* options - {Object} An optional object whose properties will be used
* to extend the control.
*/
initialize: function(direction, options) {
this.direction = direction;
this.CLASS_NAME += this.direction;
OpenLayers.Control.prototype.initialize.apply(this, [options]);
},
/**
* Method: trigger
*/
trigger: function(){
if (this.map) {
var getSlideFactor = OpenLayers.Function.bind(function (dim) {
return this.slideRatio ?
this.map.getSize()[dim] * this.slideRatio :
this.slideFactor;
}, this);
switch (this.direction) {
case OpenLayers.Control.Pan.NORTH:
this.map.pan(0, -getSlideFactor("h"));
break;
case OpenLayers.Control.Pan.SOUTH:
this.map.pan(0, getSlideFactor("h"));
break;
case OpenLayers.Control.Pan.WEST:
this.map.pan(-getSlideFactor("w"), 0);
break;
case OpenLayers.Control.Pan.EAST:
this.map.pan(getSlideFactor("w"), 0);
break;
}
}
},
CLASS_NAME: "OpenLayers.Control.Pan"
});
OpenLayers.Control.Pan.NORTH = "North";
OpenLayers.Control.Pan.SOUTH = "South";
OpenLayers.Control.Pan.EAST = "East";
OpenLayers.Control.Pan.WEST = "West";

View File

@ -0,0 +1,73 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control/Panel.js
* @requires OpenLayers/Control/Pan.js
*/
/**
* Class: OpenLayers.Control.PanPanel
* The PanPanel is visible control for panning the map North, South, East or
* West in small steps. By default it is drawn in the top left corner of the
* map.
*
* Note:
* If you wish to use this class with the default images and you want
* it to look nice in ie6, you should add the following, conditionally
* added css stylesheet to your HTML file:
*
* (code)
* <!--[if lte IE 6]>
* <link rel="stylesheet" href="../theme/default/ie6-style.css" type="text/css" />
* <![endif]-->
* (end)
*
* Inherits from:
* - <OpenLayers.Control.Panel>
*/
OpenLayers.Control.PanPanel = OpenLayers.Class(OpenLayers.Control.Panel, {
/**
* APIProperty: slideFactor
* {Integer} Number of pixels by which we'll pan the map in any direction
* on clicking the arrow buttons, defaults to 50. If you want to pan
* by some ratio of the map dimensions, use <slideRatio> instead.
*/
slideFactor: 50,
/**
* APIProperty: slideRatio
* {Number} The fraction of map width/height by which we'll pan the map
* on clicking the arrow buttons. Default is null. If set, will
* override <slideFactor>. E.g. if slideRatio is .5, then Pan Up will
* pan up half the map height.
*/
slideRatio: null,
/**
* Constructor: OpenLayers.Control.PanPanel
* Add the four directional pan buttons.
*
* Parameters:
* options - {Object} An optional object whose properties will be used
* to extend the control.
*/
initialize: function(options) {
OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);
var options = {
slideFactor: this.slideFactor,
slideRatio: this.slideRatio
};
this.addControls([
new OpenLayers.Control.Pan(OpenLayers.Control.Pan.NORTH, options),
new OpenLayers.Control.Pan(OpenLayers.Control.Pan.SOUTH, options),
new OpenLayers.Control.Pan(OpenLayers.Control.Pan.EAST, options),
new OpenLayers.Control.Pan(OpenLayers.Control.Pan.WEST, options)
]);
},
CLASS_NAME: "OpenLayers.Control.PanPanel"
});

View File

@ -0,0 +1,233 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Events/buttonclick.js
*/
/**
* Class: OpenLayers.Control.PanZoom
* The PanZoom is a visible control, composed of a
* <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomPanel>. By
* default it is drawn in the upper left corner of the map.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: slideFactor
* {Integer} Number of pixels by which we'll pan the map in any direction
* on clicking the arrow buttons. If you want to pan by some ratio
* of the map dimensions, use <slideRatio> instead.
*/
slideFactor: 50,
/**
* APIProperty: slideRatio
* {Number} The fraction of map width/height by which we'll pan the map
* on clicking the arrow buttons. Default is null. If set, will
* override <slideFactor>. E.g. if slideRatio is .5, then the Pan Up
* button will pan up half the map height.
*/
slideRatio: null,
/**
* Property: buttons
* {Array(DOMElement)} Array of Button Divs
*/
buttons: null,
/**
* Property: position
* {<OpenLayers.Pixel>}
*/
position: null,
/**
* Constructor: OpenLayers.Control.PanZoom
*
* Parameters:
* options - {Object}
*/
initialize: function(options) {
this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,
OpenLayers.Control.PanZoom.Y);
OpenLayers.Control.prototype.initialize.apply(this, arguments);
},
/**
* APIMethod: destroy
*/
destroy: function() {
if (this.map) {
this.map.events.unregister("buttonclick", this, this.onButtonClick);
}
this.removeButtons();
this.buttons = null;
this.position = null;
OpenLayers.Control.prototype.destroy.apply(this, arguments);
},
/**
* Method: setMap
*
* Properties:
* map - {<OpenLayers.Map>}
*/
setMap: function(map) {
OpenLayers.Control.prototype.setMap.apply(this, arguments);
this.map.events.register("buttonclick", this, this.onButtonClick);
},
/**
* Method: draw
*
* Parameters:
* px - {<OpenLayers.Pixel>}
*
* Returns:
* {DOMElement} A reference to the container div for the PanZoom control.
*/
draw: function(px) {
// initialize our internal div
OpenLayers.Control.prototype.draw.apply(this, arguments);
px = this.position;
// place the controls
this.buttons = [];
var sz = {w: 18, h: 18};
var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
this._addButton("panup", "north-mini.png", centered, sz);
px.y = centered.y+sz.h;
this._addButton("panleft", "west-mini.png", px, sz);
this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz);
this._addButton("pandown", "south-mini.png",
centered.add(0, sz.h*2), sz);
this._addButton("zoomin", "zoom-plus-mini.png",
centered.add(0, sz.h*3+5), sz);
this._addButton("zoomworld", "zoom-world-mini.png",
centered.add(0, sz.h*4+5), sz);
this._addButton("zoomout", "zoom-minus-mini.png",
centered.add(0, sz.h*5+5), sz);
return this.div;
},
/**
* Method: _addButton
*
* Parameters:
* id - {String}
* img - {String}
* xy - {<OpenLayers.Pixel>}
* sz - {<OpenLayers.Size>}
*
* Returns:
* {DOMElement} A Div (an alphaImageDiv, to be precise) that contains the
* image of the button, and has all the proper event handlers set.
*/
_addButton:function(id, img, xy, sz) {
var imgLocation = OpenLayers.Util.getImageLocation(img);
var btn = OpenLayers.Util.createAlphaImageDiv(
this.id + "_" + id,
xy, sz, imgLocation, "absolute");
btn.style.cursor = "pointer";
//we want to add the outer div
this.div.appendChild(btn);
btn.action = id;
btn.className = "olButton";
//we want to remember/reference the outer div
this.buttons.push(btn);
return btn;
},
/**
* Method: _removeButton
*
* Parameters:
* btn - {Object}
*/
_removeButton: function(btn) {
this.div.removeChild(btn);
OpenLayers.Util.removeItem(this.buttons, btn);
},
/**
* Method: removeButtons
*/
removeButtons: function() {
for(var i=this.buttons.length-1; i>=0; --i) {
this._removeButton(this.buttons[i]);
}
},
/**
* Method: onButtonClick
*
* Parameters:
* evt - {Event}
*/
onButtonClick: function(evt) {
var btn = evt.buttonElement;
switch (btn.action) {
case "panup":
this.map.pan(0, -this.getSlideFactor("h"));
break;
case "pandown":
this.map.pan(0, this.getSlideFactor("h"));
break;
case "panleft":
this.map.pan(-this.getSlideFactor("w"), 0);
break;
case "panright":
this.map.pan(this.getSlideFactor("w"), 0);
break;
case "zoomin":
this.map.zoomIn();
break;
case "zoomout":
this.map.zoomOut();
break;
case "zoomworld":
this.map.zoomToMaxExtent();
break;
}
},
/**
* Method: getSlideFactor
*
* Parameters:
* dim - {String} "w" or "h" (for width or height).
*
* Returns:
* {Number} The slide factor for panning in the requested direction.
*/
getSlideFactor: function(dim) {
return this.slideRatio ?
this.map.getSize()[dim] * this.slideRatio :
this.slideFactor;
},
CLASS_NAME: "OpenLayers.Control.PanZoom"
});
/**
* Constant: X
* {Integer}
*/
OpenLayers.Control.PanZoom.X = 4;
/**
* Constant: Y
* {Integer}
*/
OpenLayers.Control.PanZoom.Y = 4;

View File

@ -0,0 +1,408 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control/PanZoom.js
*/
/**
* Class: OpenLayers.Control.PanZoomBar
* The PanZoomBar is a visible control composed of a
* <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomBar>.
* By default it is displayed in the upper left corner of the map as 4
* directional arrows above a vertical slider.
*
* Inherits from:
* - <OpenLayers.Control.PanZoom>
*/
OpenLayers.Control.PanZoomBar = OpenLayers.Class(OpenLayers.Control.PanZoom, {
/**
* APIProperty: zoomStopWidth
*/
zoomStopWidth: 18,
/**
* APIProperty: zoomStopHeight
*/
zoomStopHeight: 11,
/**
* Property: slider
*/
slider: null,
/**
* Property: sliderEvents
* {<OpenLayers.Events>}
*/
sliderEvents: null,
/**
* Property: zoombarDiv
* {DOMElement}
*/
zoombarDiv: null,
/**
* APIProperty: zoomWorldIcon
* {Boolean}
*/
zoomWorldIcon: false,
/**
* APIProperty: panIcons
* {Boolean} Set this property to false not to display the pan icons. If
* false the zoom world icon is placed under the zoom bar. Defaults to
* true.
*/
panIcons: true,
/**
* APIProperty: forceFixedZoomLevel
* {Boolean} Force a fixed zoom level even though the map has
* fractionalZoom
*/
forceFixedZoomLevel: false,
/**
* Property: mouseDragStart
* {<OpenLayers.Pixel>}
*/
mouseDragStart: null,
/**
* Property: deltaY
* {Number} The cumulative vertical pixel offset during a zoom bar drag.
*/
deltaY: null,
/**
* Property: zoomStart
* {<OpenLayers.Pixel>}
*/
zoomStart: null,
/**
* Constructor: OpenLayers.Control.PanZoomBar
*/
/**
* APIMethod: destroy
*/
destroy: function() {
this._removeZoomBar();
this.map.events.un({
"changebaselayer": this.redraw,
"updatesize": this.redraw,
scope: this
});
OpenLayers.Control.PanZoom.prototype.destroy.apply(this, arguments);
delete this.mouseDragStart;
delete this.zoomStart;
},
/**
* Method: setMap
*
* Parameters:
* map - {<OpenLayers.Map>}
*/
setMap: function(map) {
OpenLayers.Control.PanZoom.prototype.setMap.apply(this, arguments);
this.map.events.on({
"changebaselayer": this.redraw,
"updatesize": this.redraw,
scope: this
});
},
/**
* Method: redraw
* clear the div and start over.
*/
redraw: function() {
if (this.div != null) {
this.removeButtons();
this._removeZoomBar();
}
this.draw();
},
/**
* Method: draw
*
* Parameters:
* px - {<OpenLayers.Pixel>}
*/
draw: function(px) {
// initialize our internal div
OpenLayers.Control.prototype.draw.apply(this, arguments);
px = this.position.clone();
// place the controls
this.buttons = [];
var sz = {w: 18, h: 18};
if (this.panIcons) {
var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
var wposition = sz.w;
if (this.zoomWorldIcon) {
centered = new OpenLayers.Pixel(px.x+sz.w, px.y);
}
this._addButton("panup", "north-mini.png", centered, sz);
px.y = centered.y+sz.h;
this._addButton("panleft", "west-mini.png", px, sz);
if (this.zoomWorldIcon) {
this._addButton("zoomworld", "zoom-world-mini.png", px.add(sz.w, 0), sz);
wposition *= 2;
}
this._addButton("panright", "east-mini.png", px.add(wposition, 0), sz);
this._addButton("pandown", "south-mini.png", centered.add(0, sz.h*2), sz);
this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h*3+5), sz);
centered = this._addZoomBar(centered.add(0, sz.h*4 + 5));
this._addButton("zoomout", "zoom-minus-mini.png", centered, sz);
}
else {
this._addButton("zoomin", "zoom-plus-mini.png", px, sz);
centered = this._addZoomBar(px.add(0, sz.h));
this._addButton("zoomout", "zoom-minus-mini.png", centered, sz);
if (this.zoomWorldIcon) {
centered = centered.add(0, sz.h+3);
this._addButton("zoomworld", "zoom-world-mini.png", centered, sz);
}
}
return this.div;
},
/**
* Method: _addZoomBar
*
* Parameters:
* centered - {<OpenLayers.Pixel>} where zoombar drawing is to start.
*/
_addZoomBar:function(centered) {
var imgLocation = OpenLayers.Util.getImageLocation("slider.png");
var id = this.id + "_" + this.map.id;
var minZoom = this.map.getMinZoom();
var zoomsToEnd = this.map.getNumZoomLevels() - 1 - this.map.getZoom();
var slider = OpenLayers.Util.createAlphaImageDiv(id,
centered.add(-1, zoomsToEnd * this.zoomStopHeight),
{w: 20, h: 9},
imgLocation,
"absolute");
slider.style.cursor = "move";
this.slider = slider;
this.sliderEvents = new OpenLayers.Events(this, slider, null, true,
{includeXY: true});
this.sliderEvents.on({
"touchstart": this.zoomBarDown,
"touchmove": this.zoomBarDrag,
"touchend": this.zoomBarUp,
"mousedown": this.zoomBarDown,
"mousemove": this.zoomBarDrag,
"mouseup": this.zoomBarUp
});
var sz = {
w: this.zoomStopWidth,
h: this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom)
};
var imgLocation = OpenLayers.Util.getImageLocation("zoombar.png");
var div = null;
if (OpenLayers.Util.alphaHack()) {
var id = this.id + "_" + this.map.id;
div = OpenLayers.Util.createAlphaImageDiv(id, centered,
{w: sz.w, h: this.zoomStopHeight},
imgLocation,
"absolute", null, "crop");
div.style.height = sz.h + "px";
} else {
div = OpenLayers.Util.createDiv(
'OpenLayers_Control_PanZoomBar_Zoombar' + this.map.id,
centered,
sz,
imgLocation);
}
div.style.cursor = "pointer";
div.className = "olButton";
this.zoombarDiv = div;
this.div.appendChild(div);
this.startTop = parseInt(div.style.top);
this.div.appendChild(slider);
this.map.events.register("zoomend", this, this.moveZoomBar);
centered = centered.add(0,
this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom));
return centered;
},
/**
* Method: _removeZoomBar
*/
_removeZoomBar: function() {
this.sliderEvents.un({
"touchstart": this.zoomBarDown,
"touchmove": this.zoomBarDrag,
"touchend": this.zoomBarUp,
"mousedown": this.zoomBarDown,
"mousemove": this.zoomBarDrag,
"mouseup": this.zoomBarUp
});
this.sliderEvents.destroy();
this.div.removeChild(this.zoombarDiv);
this.zoombarDiv = null;
this.div.removeChild(this.slider);
this.slider = null;
this.map.events.unregister("zoomend", this, this.moveZoomBar);
},
/**
* Method: onButtonClick
*
* Parameters:
* evt - {Event}
*/
onButtonClick: function(evt) {
OpenLayers.Control.PanZoom.prototype.onButtonClick.apply(this, arguments);
if (evt.buttonElement === this.zoombarDiv) {
var levels = evt.buttonXY.y / this.zoomStopHeight;
if(this.forceFixedZoomLevel || !this.map.fractionalZoom) {
levels = Math.floor(levels);
}
var zoom = (this.map.getNumZoomLevels() - 1) - levels;
zoom = Math.min(Math.max(zoom, 0), this.map.getNumZoomLevels() - 1);
this.map.zoomTo(zoom);
}
},
/**
* Method: passEventToSlider
* This function is used to pass events that happen on the div, or the map,
* through to the slider, which then does its moving thing.
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
passEventToSlider:function(evt) {
this.sliderEvents.handleBrowserEvent(evt);
},
/*
* Method: zoomBarDown
* event listener for clicks on the slider
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
zoomBarDown:function(evt) {
if (!OpenLayers.Event.isLeftClick(evt) && !OpenLayers.Event.isSingleTouch(evt)) {
return;
}
this.map.events.on({
"touchmove": this.passEventToSlider,
"mousemove": this.passEventToSlider,
"mouseup": this.passEventToSlider,
scope: this
});
this.mouseDragStart = evt.xy.clone();
this.zoomStart = evt.xy.clone();
this.div.style.cursor = "move";
// reset the div offsets just in case the div moved
this.zoombarDiv.offsets = null;
OpenLayers.Event.stop(evt);
},
/*
* Method: zoomBarDrag
* This is what happens when a click has occurred, and the client is
* dragging. Here we must ensure that the slider doesn't go beyond the
* bottom/top of the zoombar div, as well as moving the slider to its new
* visual location
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
zoomBarDrag:function(evt) {
if (this.mouseDragStart != null) {
var deltaY = this.mouseDragStart.y - evt.xy.y;
var offsets = OpenLayers.Util.pagePosition(this.zoombarDiv);
if ((evt.clientY - offsets[1]) > 0 &&
(evt.clientY - offsets[1]) < parseInt(this.zoombarDiv.style.height) - 2) {
var newTop = parseInt(this.slider.style.top) - deltaY;
this.slider.style.top = newTop+"px";
this.mouseDragStart = evt.xy.clone();
}
// set cumulative displacement
this.deltaY = this.zoomStart.y - evt.xy.y;
OpenLayers.Event.stop(evt);
}
},
/*
* Method: zoomBarUp
* Perform cleanup when a mouseup event is received -- discover new zoom
* level and switch to it.
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
zoomBarUp:function(evt) {
if (!OpenLayers.Event.isLeftClick(evt) && evt.type !== "touchend") {
return;
}
if (this.mouseDragStart) {
this.div.style.cursor="";
this.map.events.un({
"touchmove": this.passEventToSlider,
"mouseup": this.passEventToSlider,
"mousemove": this.passEventToSlider,
scope: this
});
var zoomLevel = this.map.zoom;
if (!this.forceFixedZoomLevel && this.map.fractionalZoom) {
zoomLevel += this.deltaY/this.zoomStopHeight;
zoomLevel = Math.min(Math.max(zoomLevel, 0),
this.map.getNumZoomLevels() - 1);
} else {
zoomLevel += this.deltaY/this.zoomStopHeight;
zoomLevel = Math.max(Math.round(zoomLevel), 0);
}
this.map.zoomTo(zoomLevel);
this.mouseDragStart = null;
this.zoomStart = null;
this.deltaY = 0;
OpenLayers.Event.stop(evt);
}
},
/*
* Method: moveZoomBar
* Change the location of the slider to match the current zoom level.
*/
moveZoomBar:function() {
var newTop =
((this.map.getNumZoomLevels()-1) - this.map.getZoom()) *
this.zoomStopHeight + this.startTop + 1;
this.slider.style.top = newTop + "px";
},
CLASS_NAME: "OpenLayers.Control.PanZoomBar"
});

View File

@ -0,0 +1,431 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Events/buttonclick.js
*/
/**
* Class: OpenLayers.Control.Panel
* The Panel control is a container for other controls. With it toolbars
* may be composed.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, {
/**
* Property: controls
* {Array(<OpenLayers.Control>)}
*/
controls: null,
/**
* APIProperty: autoActivate
* {Boolean} Activate the control when it is added to a map. Default is
* true.
*/
autoActivate: true,
/**
* APIProperty: defaultControl
* {<OpenLayers.Control>} The control which is activated when the control is
* activated (turned on), which also happens at instantiation.
* If <saveState> is true, <defaultControl> will be nullified after the
* first activation of the panel.
*/
defaultControl: null,
/**
* APIProperty: saveState
* {Boolean} If set to true, the active state of this panel's controls will
* be stored on panel deactivation, and restored on reactivation. Default
* is false.
*/
saveState: false,
/**
* APIProperty: allowDepress
* {Boolean} If is true the <OpenLayers.Control.TYPE_TOOL> controls can
* be deactivated by clicking the icon that represents them. Default
* is false.
*/
allowDepress: false,
/**
* Property: activeState
* {Object} stores the active state of this panel's controls.
*/
activeState: null,
/**
* Constructor: OpenLayers.Control.Panel
* Create a new control panel.
*
* Each control in the panel is represented by an icon. When clicking
* on an icon, the <activateControl> method is called.
*
* Specific properties for controls on a panel:
* type - {Number} One of <OpenLayers.Control.TYPE_TOOL>,
* <OpenLayers.Control.TYPE_TOGGLE>, <OpenLayers.Control.TYPE_BUTTON>.
* If not provided, <OpenLayers.Control.TYPE_TOOL> is assumed.
* title - {string} Text displayed when mouse is over the icon that
* represents the control.
*
* The <OpenLayers.Control.type> of a control determines the behavior when
* clicking its icon:
* <OpenLayers.Control.TYPE_TOOL> - The control is activated and other
* controls of this type in the same panel are deactivated. This is
* the default type.
* <OpenLayers.Control.TYPE_TOGGLE> - The active state of the control is
* toggled.
* <OpenLayers.Control.TYPE_BUTTON> - The
* <OpenLayers.Control.Button.trigger> method of the control is called,
* but its active state is not changed.
*
* If a control is <OpenLayers.Control.active>, it will be drawn with the
* olControl[Name]ItemActive class, otherwise with the
* olControl[Name]ItemInactive class.
*
* Parameters:
* options - {Object} An optional object whose properties will be used
* to extend the control.
*/
initialize: function(options) {
OpenLayers.Control.prototype.initialize.apply(this, [options]);
this.controls = [];
this.activeState = {};
},
/**
* APIMethod: destroy
*/
destroy: function() {
if (this.map) {
this.map.events.unregister("buttonclick", this, this.onButtonClick);
}
OpenLayers.Control.prototype.destroy.apply(this, arguments);
for (var ctl, i = this.controls.length - 1; i >= 0; i--) {
ctl = this.controls[i];
if (ctl.events) {
ctl.events.un({
activate: this.iconOn,
deactivate: this.iconOff
});
}
ctl.panel_div = null;
}
this.activeState = null;
},
/**
* APIMethod: activate
*/
activate: function() {
if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {
var control;
for (var i=0, len=this.controls.length; i<len; i++) {
control = this.controls[i];
if (control === this.defaultControl ||
(this.saveState && this.activeState[control.id])) {
control.activate();
}
}
if (this.saveState === true) {
this.defaultControl = null;
}
this.redraw();
return true;
} else {
return false;
}
},
/**
* APIMethod: deactivate
*/
deactivate: function() {
if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
var control;
for (var i=0, len=this.controls.length; i<len; i++) {
control = this.controls[i];
this.activeState[control.id] = control.deactivate();
}
this.redraw();
return true;
} else {
return false;
}
},
/**
* Method: draw
*
* Returns:
* {DOMElement}
*/
draw: function() {
OpenLayers.Control.prototype.draw.apply(this, arguments);
if (this.outsideViewport) {
this.events.attachToElement(this.div);
this.events.register("buttonclick", this, this.onButtonClick);
} else {
this.map.events.register("buttonclick", this, this.onButtonClick);
}
this.addControlsToMap(this.controls);
return this.div;
},
/**
* Method: redraw
*/
redraw: function() {
for (var l=this.div.childNodes.length, i=l-1; i>=0; i--) {
this.div.removeChild(this.div.childNodes[i]);
}
this.div.innerHTML = "";
if (this.active) {
for (var i=0, len=this.controls.length; i<len; i++) {
this.div.appendChild(this.controls[i].panel_div);
}
}
},
/**
* APIMethod: activateControl
* This method is called when the user click on the icon representing a
* control in the panel.
*
* Parameters:
* control - {<OpenLayers.Control>}
*/
activateControl: function (control) {
if (!this.active) { return false; }
if (control.type == OpenLayers.Control.TYPE_BUTTON) {
control.trigger();
return;
}
if (control.type == OpenLayers.Control.TYPE_TOGGLE) {
if (control.active) {
control.deactivate();
} else {
control.activate();
}
return;
}
if (this.allowDepress && control.active) {
control.deactivate();
} else {
var c;
for (var i=0, len=this.controls.length; i<len; i++) {
c = this.controls[i];
if (c != control &&
(c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) {
c.deactivate();
}
}
control.activate();
}
},
/**
* APIMethod: addControls
* To build a toolbar, you add a set of controls to it. addControls
* lets you add a single control or a list of controls to the
* Control Panel.
*
* Parameters:
* controls - {<OpenLayers.Control>} Controls to add in the panel.
*/
addControls: function(controls) {
if (!(OpenLayers.Util.isArray(controls))) {
controls = [controls];
}
this.controls = this.controls.concat(controls);
for (var i=0, len=controls.length; i<len; i++) {
var control = controls[i],
element = this.createControlMarkup(control);
OpenLayers.Element.addClass(element,
control.displayClass + "ItemInactive");
OpenLayers.Element.addClass(element, "olButton");
if (control.title != "" && !element.title) {
element.title = control.title;
}
control.panel_div = element;
}
if (this.map) { // map.addControl() has already been called on the panel
this.addControlsToMap(controls);
this.redraw();
}
},
/**
* APIMethod: createControlMarkup
* This function just creates a div for the control. If specific HTML
* markup is needed this function can be overridden in specific classes,
* or at panel instantiation time:
*
* Example:
* (code)
* var panel = new OpenLayers.Control.Panel({
* defaultControl: control,
* // ovverride createControlMarkup to create actual buttons
* // including texts wrapped into span elements.
* createControlMarkup: function(control) {
* var button = document.createElement('button'),
* span = document.createElement('span');
* if (control.text) {
* span.innerHTML = control.text;
* }
* return button;
* }
* });
* (end)
*
* Parameters:
* control - {<OpenLayers.Control>} The control to create the HTML
* markup for.
*
* Returns:
* {DOMElement} The markup.
*/
createControlMarkup: function(control) {
return document.createElement("div");
},
/**
* Method: addControlsToMap
* Only for internal use in draw() and addControls() methods.
*
* Parameters:
* controls - {Array(<OpenLayers.Control>)} Controls to add into map.
*/
addControlsToMap: function (controls) {
var control;
for (var i=0, len=controls.length; i<len; i++) {
control = controls[i];
if (control.autoActivate === true) {
control.autoActivate = false;
this.map.addControl(control);
control.autoActivate = true;
} else {
this.map.addControl(control);
control.deactivate();
}
control.events.on({
activate: this.iconOn,
deactivate: this.iconOff
});
}
},
/**
* Method: iconOn
* Internal use, for use only with "controls[i].events.on/un".
*/
iconOn: function() {
var d = this.panel_div; // "this" refers to a control on panel!
var re = new RegExp("\\b(" + this.displayClass + "Item)Inactive\\b");
d.className = d.className.replace(re, "$1Active");
},
/**
* Method: iconOff
* Internal use, for use only with "controls[i].events.on/un".
*/
iconOff: function() {
var d = this.panel_div; // "this" refers to a control on panel!
var re = new RegExp("\\b(" + this.displayClass + "Item)Active\\b");
d.className = d.className.replace(re, "$1Inactive");
},
/**
* Method: onButtonClick
*
* Parameters:
* evt - {Event}
*/
onButtonClick: function (evt) {
var controls = this.controls,
button = evt.buttonElement;
for (var i=controls.length-1; i>=0; --i) {
if (controls[i].panel_div === button) {
this.activateControl(controls[i]);
break;
}
}
},
/**
* APIMethod: getControlsBy
* Get a list of controls with properties matching the given criteria.
*
* Parameters:
* property - {String} A control property to be matched.
* match - {String | Object} A string to match. Can also be a regular
* expression literal or object. In addition, it can be any object
* with a method named test. For reqular expressions or other, if
* match.test(control[property]) evaluates to true, the control will be
* included in the array returned. If no controls are found, an empty
* array is returned.
*
* Returns:
* {Array(<OpenLayers.Control>)} A list of controls matching the given criteria.
* An empty array is returned if no matches are found.
*/
getControlsBy: function(property, match) {
var test = (typeof match.test == "function");
var found = OpenLayers.Array.filter(this.controls, function(item) {
return item[property] == match || (test && match.test(item[property]));
});
return found;
},
/**
* APIMethod: getControlsByName
* Get a list of contorls with names matching the given name.
*
* Parameters:
* match - {String | Object} A control name. The name can also be a regular
* expression literal or object. In addition, it can be any object
* with a method named test. For reqular expressions or other, if
* name.test(control.name) evaluates to true, the control will be included
* in the list of controls returned. If no controls are found, an empty
* array is returned.
*
* Returns:
* {Array(<OpenLayers.Control>)} A list of controls matching the given name.
* An empty array is returned if no matches are found.
*/
getControlsByName: function(match) {
return this.getControlsBy("name", match);
},
/**
* APIMethod: getControlsByClass
* Get a list of controls of a given type (CLASS_NAME).
*
* Parameters:
* match - {String | Object} A control class name. The type can also be a
* regular expression literal or object. In addition, it can be any
* object with a method named test. For reqular expressions or other,
* if type.test(control.CLASS_NAME) evaluates to true, the control will
* be included in the list of controls returned. If no controls are
* found, an empty array is returned.
*
* Returns:
* {Array(<OpenLayers.Control>)} A list of controls matching the given type.
* An empty array is returned if no matches are found.
*/
getControlsByClass: function(match) {
return this.getControlsBy("CLASS_NAME", match);
},
CLASS_NAME: "OpenLayers.Control.Panel"
});

View File

@ -0,0 +1,257 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Control/ArgParser.js
* @requires OpenLayers/Lang.js
*/
/**
* Class: OpenLayers.Control.Permalink
* The Permalink control is hyperlink that will return the user to the
* current map view. By default it is drawn in the lower right corner of the
* map. The href is updated as the map is zoomed, panned and whilst layers
* are switched.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.Permalink = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: argParserClass
* {Class} The ArgParser control class (not instance) to use with this
* control.
*/
argParserClass: OpenLayers.Control.ArgParser,
/**
* Property: element
* {DOMElement}
*/
element: null,
/**
* APIProperty: anchor
* {Boolean} This option changes 3 things:
* the character '#' is used in place of the character '?',
* the window.href is updated if no element is provided.
* When this option is set to true it's not recommend to provide
* a base without provide an element.
*/
anchor: false,
/**
* APIProperty: base
* {String}
*/
base: '',
/**
* APIProperty: displayProjection
* {<OpenLayers.Projection>} Requires proj4js support. Projection used
* when creating the coordinates in the link. This will reproject the
* map coordinates into display coordinates. If you are using this
* functionality, the permalink which is last added to the map will
* determine the coordinate type which is read from the URL, which
* means you should not add permalinks with different
* displayProjections to the same map.
*/
displayProjection: null,
/**
* Constructor: OpenLayers.Control.Permalink
*
* Parameters:
* element - {DOMElement}
* base - {String}
* options - {Object} options to the control.
*
* Or for anchor:
* options - {Object} options to the control.
*/
initialize: function(element, base, options) {
if (element !== null && typeof element == 'object' && !OpenLayers.Util.isElement(element)) {
options = element;
this.base = document.location.href;
OpenLayers.Control.prototype.initialize.apply(this, [options]);
if (this.element != null) {
this.element = OpenLayers.Util.getElement(this.element);
}
}
else {
OpenLayers.Control.prototype.initialize.apply(this, [options]);
this.element = OpenLayers.Util.getElement(element);
this.base = base || document.location.href;
}
},
/**
* APIMethod: destroy
*/
destroy: function() {
if (this.element && this.element.parentNode == this.div) {
this.div.removeChild(this.element);
this.element = null;
}
if (this.map) {
this.map.events.unregister('moveend', this, this.updateLink);
}
OpenLayers.Control.prototype.destroy.apply(this, arguments);
},
/**
* Method: setMap
* Set the map property for the control.
*
* Parameters:
* map - {<OpenLayers.Map>}
*/
setMap: function(map) {
OpenLayers.Control.prototype.setMap.apply(this, arguments);
//make sure we have an arg parser attached
for(var i=0, len=this.map.controls.length; i<len; i++) {
var control = this.map.controls[i];
if (control.CLASS_NAME == this.argParserClass.CLASS_NAME) {
// If a permalink is added to the map, and an ArgParser already
// exists, we override the displayProjection to be the one
// on the permalink.
if (control.displayProjection != this.displayProjection) {
this.displayProjection = control.displayProjection;
}
break;
}
}
if (i == this.map.controls.length) {
this.map.addControl(new this.argParserClass(
{ 'displayProjection': this.displayProjection }));
}
},
/**
* Method: draw
*
* Returns:
* {DOMElement}
*/
draw: function() {
OpenLayers.Control.prototype.draw.apply(this, arguments);
if (!this.element && !this.anchor) {
this.element = document.createElement("a");
this.element.innerHTML = OpenLayers.i18n("Permalink");
this.element.href="";
this.div.appendChild(this.element);
}
this.map.events.on({
'moveend': this.updateLink,
'changelayer': this.updateLink,
'changebaselayer': this.updateLink,
scope: this
});
// Make it so there is at least a link even though the map may not have
// moved yet.
this.updateLink();
return this.div;
},
/**
* Method: updateLink
*/
updateLink: function() {
var separator = this.anchor ? '#' : '?';
var href = this.base;
var anchor = null;
if (href.indexOf("#") != -1 && this.anchor == false) {
anchor = href.substring( href.indexOf("#"), href.length);
}
if (href.indexOf(separator) != -1) {
href = href.substring( 0, href.indexOf(separator) );
}
var splits = href.split("#");
href = splits[0] + separator+ OpenLayers.Util.getParameterString(this.createParams());
if (anchor) {
href += anchor;
}
if (this.anchor && !this.element) {
window.location.href = href;
}
else {
this.element.href = href;
}
},
/**
* APIMethod: createParams
* Creates the parameters that need to be encoded into the permalink url.
*
* Parameters:
* center - {<OpenLayers.LonLat>} center to encode in the permalink.
* Defaults to the current map center.
* zoom - {Integer} zoom level to encode in the permalink. Defaults to the
* current map zoom level.
* layers - {Array(<OpenLayers.Layer>)} layers to encode in the permalink.
* Defaults to the current map layers.
*
* Returns:
* {Object} Hash of parameters that will be url-encoded into the
* permalink.
*/
createParams: function(center, zoom, layers) {
center = center || this.map.getCenter();
var params = OpenLayers.Util.getParameters(this.base);
// If there's still no center, map is not initialized yet.
// Break out of this function, and simply return the params from the
// base link.
if (center) {
//zoom
params.zoom = zoom || this.map.getZoom();
//lon,lat
var lat = center.lat;
var lon = center.lon;
if (this.displayProjection) {
var mapPosition = OpenLayers.Projection.transform(
{ x: lon, y: lat },
this.map.getProjectionObject(),
this.displayProjection );
lon = mapPosition.x;
lat = mapPosition.y;
}
params.lat = Math.round(lat*100000)/100000;
params.lon = Math.round(lon*100000)/100000;
//layers
layers = layers || this.map.layers;
params.layers = '';
for (var i=0, len=layers.length; i<len; i++) {
var layer = layers[i];
if (layer.isBaseLayer) {
params.layers += (layer == this.map.baseLayer) ? "B" : "0";
} else {
params.layers += (layer.getVisibility()) ? "T" : "F";
}
}
}
return params;
},
CLASS_NAME: "OpenLayers.Control.Permalink"
});

View File

@ -0,0 +1,157 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Handler/Pinch.js
*/
/**
* Class: OpenLayers.Control.PinchZoom
*
* Inherits:
* - <OpenLayers.Control>
*/
OpenLayers.Control.PinchZoom = OpenLayers.Class(OpenLayers.Control, {
/**
* Property: type
* {OpenLayers.Control.TYPES}
*/
type: OpenLayers.Control.TYPE_TOOL,
/**
* Property: pinchOrigin
* {Object} Cached object representing the pinch start (in pixels).
*/
pinchOrigin: null,
/**
* Property: currentCenter
* {Object} Cached object representing the latest pinch center (in pixels).
*/
currentCenter: null,
/**
* APIProperty: autoActivate
* {Boolean} Activate the control when it is added to a map. Default is
* true.
*/
autoActivate: true,
/**
* APIProperty: preserveCenter
* {Boolean} Set this to true if you don't want the map center to change
* while pinching. For example you may want to set preserveCenter to
* true when the user location is being watched and you want to preserve
* the user location at the center of the map even if he zooms in or
* out using pinch. This property's value can be changed any time on an
* existing instance. Default is false.
*/
preserveCenter: false,
/**
* APIProperty: handlerOptions
* {Object} Used to set non-default properties on the pinch handler
*/
/**
* Constructor: OpenLayers.Control.PinchZoom
* Create a control for zooming with pinch gestures. This works on devices
* with multi-touch support.
*
* Parameters:
* options - {Object} An optional object whose properties will be set on
* the control
*/
initialize: function(options) {
OpenLayers.Control.prototype.initialize.apply(this, arguments);
this.handler = new OpenLayers.Handler.Pinch(this, {
start: this.pinchStart,
move: this.pinchMove,
done: this.pinchDone
}, this.handlerOptions);
},
/**
* Method: pinchStart
*
* Parameters:
* evt - {Event}
* pinchData - {Object} pinch data object related to the current touchmove
* of the pinch gesture. This give us the current scale of the pinch.
*/
pinchStart: function(evt, pinchData) {
var xy = (this.preserveCenter) ?
this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;
this.pinchOrigin = xy;
this.currentCenter = xy;
},
/**
* Method: pinchMove
*
* Parameters:
* evt - {Event}
* pinchData - {Object} pinch data object related to the current touchmove
* of the pinch gesture. This give us the current scale of the pinch.
*/
pinchMove: function(evt, pinchData) {
var scale = pinchData.scale;
var containerOrigin = this.map.layerContainerOriginPx;
var pinchOrigin = this.pinchOrigin;
var current = (this.preserveCenter) ?
this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;
var dx = Math.round((containerOrigin.x + current.x - pinchOrigin.x) + (scale - 1) * (containerOrigin.x - pinchOrigin.x));
var dy = Math.round((containerOrigin.y + current.y - pinchOrigin.y) + (scale - 1) * (containerOrigin.y - pinchOrigin.y));
this.map.applyTransform(dx, dy, scale);
this.currentCenter = current;
},
/**
* Method: pinchDone
*
* Parameters:
* evt - {Event}
* start - {Object} pinch data object related to the touchstart event that
* started the pinch gesture.
* last - {Object} pinch data object related to the last touchmove event
* of the pinch gesture. This give us the final scale of the pinch.
*/
pinchDone: function(evt, start, last) {
this.map.applyTransform();
var zoom = this.map.getZoomForResolution(this.map.getResolution() / last.scale, true);
if (zoom !== this.map.getZoom() || !this.currentCenter.equals(this.pinchOrigin)) {
var resolution = this.map.getResolutionForZoom(zoom);
var location = this.map.getLonLatFromPixel(this.pinchOrigin);
var zoomPixel = this.currentCenter;
var size = this.map.getSize();
location.lon += resolution * ((size.w / 2) - zoomPixel.x);
location.lat -= resolution * ((size.h / 2) - zoomPixel.y);
// Force a reflow before calling setCenter. This is to work
// around an issue occuring in iOS.
//
// See https://github.com/openlayers/openlayers/pull/351.
//
// Without a reflow setting the layer container div's top left
// style properties to "0px" - as done in Map.moveTo when zoom
// is changed - won't actually correctly reposition the layer
// container div.
//
// Also, we need to use a statement that the Google Closure
// compiler won't optimize away.
this.map.div.clientWidth = this.map.div.clientWidth;
this.map.setCenter(location, zoom);
}
},
CLASS_NAME: "OpenLayers.Control.PinchZoom"
});

View File

@ -0,0 +1,567 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Layer/WMS.js
* @requires OpenLayers/Handler/RegularPolygon.js
* @requires OpenLayers/Handler/Polygon.js
* @requires OpenLayers/Handler/Path.js
* @requires OpenLayers/Handler/Click.js
* @requires OpenLayers/Filter/Spatial.js
* @requires OpenLayers/Format/SLD/v1_0_0.js
*/
/**
* Class: OpenLayers.Control.SLDSelect
* Perform selections on WMS layers using Styled Layer Descriptor (SLD)
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.SLDSelect = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: events
* {<OpenLayers.Events>} Events instance for listeners and triggering
* control specific events.
*
* Register a listener for a particular event with the following syntax:
* (code)
* control.events.register(type, obj, listener);
* (end)
*
* Supported event types (in addition to those from <OpenLayers.Control.events>):
* selected - Triggered when a selection occurs. Listeners receive an
* event with *filters* and *layer* properties. Filters will be an
* array of OpenLayers.Filter objects created in order to perform
* the particular selection.
*/
/**
* APIProperty: clearOnDeactivate
* {Boolean} Should the selection be cleared when the control is
* deactivated. Default value is false.
*/
clearOnDeactivate: false,
/**
* APIProperty: layers
* {Array(<OpenLayers.Layer.WMS>)} The WMS layers this control will work
* on.
*/
layers: null,
/**
* Property: callbacks
* {Object} The functions that are sent to the handler for callback
*/
callbacks: null,
/**
* APIProperty: selectionSymbolizer
* {Object} Determines the styling of the selected objects. Default is
* a selection in red.
*/
selectionSymbolizer: {
'Polygon': {fillColor: '#FF0000', stroke: false},
'Line': {strokeColor: '#FF0000', strokeWidth: 2},
'Point': {graphicName: 'square', fillColor: '#FF0000', pointRadius: 5}
},
/**
* APIProperty: layerOptions
* {Object} The options to apply to the selection layer, by default the
* selection layer will be kept out of the layer switcher.
*/
layerOptions: null,
/**
* APIProperty: handlerOptions
* {Object} Used to set non-default properties on the control's handler
*/
/**
* APIProperty: sketchStyle
* {<OpenLayers.Style>|Object} Style or symbolizer to use for the sketch
* handler. The recommended way of styling the sketch layer, however, is
* to configure an <OpenLayers.StyleMap> in the layerOptions of the
* <handlerOptions>:
*
* (code)
* new OpenLayers.Control.SLDSelect(OpenLayers.Handler.Path, {
* handlerOptions: {
* layerOptions: {
* styleMap: new OpenLayers.StyleMap({
* "default": {strokeColor: "yellow"}
* })
* }
* }
* });
* (end)
*/
sketchStyle: null,
/**
* APIProperty: wfsCache
* {Object} Cache to use for storing parsed results from
* <OpenLayers.Format.WFSDescribeFeatureType.read>. If not provided,
* these will be cached on the prototype.
*/
wfsCache: {},
/**
* APIProperty: layerCache
* {Object} Cache to use for storing references to the selection layers.
* Normally each source layer will have exactly 1 selection layer of
* type OpenLayers.Layer.WMS. If not provided, layers will
* be cached on the prototype. Note that if <clearOnDeactivate> is
* true, the layer will no longer be cached after deactivating the
* control.
*/
layerCache: {},
/**
* Constructor: OpenLayers.Control.SLDSelect
* Create a new control for selecting features in WMS layers using
* Styled Layer Descriptor (SLD).
*
* Parameters:
* handler - {<OpenLayers.Class>} A sketch handler class. This determines
* the type of selection, e.g. box (<OpenLayers.Handler.Box>), point
* (<OpenLayers.Handler.Point>), path (<OpenLayers.Handler.Path>) or
* polygon (<OpenLayers.Handler.Polygon>) selection. To use circle
* type selection, use <OpenLayers.Handler.RegularPolygon> and pass
* the number of desired sides (e.g. 40) as "sides" property to the
* <handlerOptions>.
* options - {Object} An object containing all configuration properties for
* the control.
*
* Valid options:
* layers - Array({<OpenLayers.Layer.WMS>}) The layers to perform the
* selection on.
*/
initialize: function(handler, options) {
OpenLayers.Control.prototype.initialize.apply(this, [options]);
this.callbacks = OpenLayers.Util.extend({done: this.select,
click: this.select}, this.callbacks);
this.handlerOptions = this.handlerOptions || {};
this.layerOptions = OpenLayers.Util.applyDefaults(this.layerOptions, {
displayInLayerSwitcher: false,
tileOptions: {maxGetUrlLength: 2048}
});
if (this.sketchStyle) {
this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(
this.handlerOptions.layerOptions,
{styleMap: new OpenLayers.StyleMap({"default": this.sketchStyle})}
);
}
this.handler = new handler(this, this.callbacks, this.handlerOptions);
},
/**
* APIMethod: destroy
* Take care of things that are not handled in superclass.
*/
destroy: function() {
for (var key in this.layerCache) {
delete this.layerCache[key];
}
for (var key in this.wfsCache) {
delete this.wfsCache[key];
}
OpenLayers.Control.prototype.destroy.apply(this, arguments);
},
/**
* Method: coupleLayerVisiblity
* Couple the selection layer and the source layer with respect to
* layer visibility. So if the source layer is turned off, the
* selection layer is also turned off.
*
* Context:
* - {<OpenLayers.Layer>}
*
* Parameters:
* evt - {Object}
*/
coupleLayerVisiblity: function(evt) {
this.setVisibility(evt.object.getVisibility());
},
/**
* Method: createSelectionLayer
* Creates a "clone" from the source layer in which the selection can
* be drawn. This ensures both the source layer and the selection are
* visible and not only the selection.
*
* Parameters:
* source - {<OpenLayers.Layer.WMS>} The source layer on which the selection
* is performed.
*
* Returns:
* {<OpenLayers.Layer.WMS>} A WMS layer with maxGetUrlLength configured to 2048
* since SLD selections can easily get quite long.
*/
createSelectionLayer: function(source) {
// check if we already have a selection layer for the source layer
var selectionLayer;
if (!this.layerCache[source.id]) {
selectionLayer = new OpenLayers.Layer.WMS(source.name,
source.url, source.params,
OpenLayers.Util.applyDefaults(
this.layerOptions,
source.getOptions())
);
this.layerCache[source.id] = selectionLayer;
// make sure the layers are coupled wrt visibility, but only
// if they are not displayed in the layer switcher, because in
// that case the user cannot control visibility.
if (this.layerOptions.displayInLayerSwitcher === false) {
source.events.on({
"visibilitychanged": this.coupleLayerVisiblity,
scope: selectionLayer});
}
this.map.addLayer(selectionLayer);
} else {
selectionLayer = this.layerCache[source.id];
}
return selectionLayer;
},
/**
* Method: createSLD
* Create the SLD document for the layer using the supplied filters.
*
* Parameters:
* layer - {<OpenLayers.Layer.WMS>}
* filters - Array({<OpenLayers.Filter>}) The filters to be applied.
* geometryAttributes - Array({Object}) The geometry attributes of the
* layer.
*
* Returns:
* {String} The SLD document generated as a string.
*/
createSLD: function(layer, filters, geometryAttributes) {
var sld = {version: "1.0.0", namedLayers: {}};
var layerNames = [layer.params.LAYERS].join(",").split(",");
for (var i=0, len=layerNames.length; i<len; i++) {
var name = layerNames[i];
sld.namedLayers[name] = {name: name, userStyles: []};
var symbolizer = this.selectionSymbolizer;
var geometryAttribute = geometryAttributes[i];
if (geometryAttribute.type.indexOf('Polygon') >= 0) {
symbolizer = {Polygon: this.selectionSymbolizer['Polygon']};
} else if (geometryAttribute.type.indexOf('LineString') >= 0) {
symbolizer = {Line: this.selectionSymbolizer['Line']};
} else if (geometryAttribute.type.indexOf('Point') >= 0) {
symbolizer = {Point: this.selectionSymbolizer['Point']};
}
var filter = filters[i];
sld.namedLayers[name].userStyles.push({name: 'default', rules: [
new OpenLayers.Rule({symbolizer: symbolizer,
filter: filter,
maxScaleDenominator: layer.options.minScale})
]});
}
return new OpenLayers.Format.SLD({srsName: this.map.getProjection()}).write(sld);
},
/**
* Method: parseDescribeLayer
* Parse the SLD WMS DescribeLayer response and issue the corresponding
* WFS DescribeFeatureType request
*
* request - {XMLHttpRequest} The request object.
*/
parseDescribeLayer: function(request) {
var format = new OpenLayers.Format.WMSDescribeLayer();
var doc = request.responseXML;
if(!doc || !doc.documentElement) {
doc = request.responseText;
}
var describeLayer = format.read(doc);
var typeNames = [];
var url = null;
for (var i=0, len=describeLayer.length; i<len; i++) {
// perform a WFS DescribeFeatureType request
if (describeLayer[i].owsType == "WFS") {
typeNames.push(describeLayer[i].typeName);
url = describeLayer[i].owsURL;
}
}
var options = {
url: url,
params: {
SERVICE: "WFS",
TYPENAME: typeNames.toString(),
REQUEST: "DescribeFeatureType",
VERSION: "1.0.0"
},
callback: function(request) {
var format = new OpenLayers.Format.WFSDescribeFeatureType();
var doc = request.responseXML;
if(!doc || !doc.documentElement) {
doc = request.responseText;
}
var describeFeatureType = format.read(doc);
this.control.wfsCache[this.layer.id] = describeFeatureType;
this.control._queue && this.control.applySelection();
},
scope: this
};
OpenLayers.Request.GET(options);
},
/**
* Method: getGeometryAttributes
* Look up the geometry attributes from the WFS DescribeFeatureType response
*
* Parameters:
* layer - {<OpenLayers.Layer.WMS>} The layer for which to look up the
* geometry attributes.
*
* Returns:
* Array({Object}) Array of geometry attributes
*/
getGeometryAttributes: function(layer) {
var result = [];
var cache = this.wfsCache[layer.id];
for (var i=0, len=cache.featureTypes.length; i<len; i++) {
var typeName = cache.featureTypes[i];
var properties = typeName.properties;
for (var j=0, lenj=properties.length; j < lenj; j++) {
var property = properties[j];
var type = property.type;
if ((type.indexOf('LineString') >= 0) ||
(type.indexOf('GeometryAssociationType') >=0) ||
(type.indexOf('GeometryPropertyType') >= 0) ||
(type.indexOf('Point') >= 0) ||
(type.indexOf('Polygon') >= 0) ) {
result.push(property);
}
}
}
return result;
},
/**
* APIMethod: activate
* Activate the control. Activating the control will perform a SLD WMS
* DescribeLayer request followed by a WFS DescribeFeatureType request
* so that the proper symbolizers can be chosen based on the geometry
* type.
*/
activate: function() {
var activated = OpenLayers.Control.prototype.activate.call(this);
if(activated) {
for (var i=0, len=this.layers.length; i<len; i++) {
var layer = this.layers[i];
if (layer && !this.wfsCache[layer.id]) {
var options = {
url: layer.url,
params: {
SERVICE: "WMS",
VERSION: layer.params.VERSION,
LAYERS: layer.params.LAYERS,
REQUEST: "DescribeLayer"
},
callback: this.parseDescribeLayer,
scope: {layer: layer, control: this}
};
OpenLayers.Request.GET(options);
}
}
}
return activated;
},
/**
* APIMethod: deactivate
* Deactivate the control. If clearOnDeactivate is true, remove the
* selection layer(s).
*/
deactivate: function() {
var deactivated = OpenLayers.Control.prototype.deactivate.call(this);
if(deactivated) {
for (var i=0, len=this.layers.length; i<len; i++) {
var layer = this.layers[i];
if (layer && this.clearOnDeactivate === true) {
var layerCache = this.layerCache;
var selectionLayer = layerCache[layer.id];
if (selectionLayer) {
layer.events.un({
"visibilitychanged": this.coupleLayerVisiblity,
scope: selectionLayer});
selectionLayer.destroy();
delete layerCache[layer.id];
}
}
}
}
return deactivated;
},
/**
* APIMethod: setLayers
* Set the layers on which the selection should be performed. Call the
* setLayers method if the layer(s) to be used change and the same
* control should be used on a new set of layers.
* If the control is already active, it will be active after the new
* set of layers is set.
*
* Parameters:
* layers - {Array(<OpenLayers.Layer.WMS>)} The new set of layers on which
* the selection should be performed.
*/
setLayers: function(layers) {
if(this.active) {
this.deactivate();
this.layers = layers;
this.activate();
} else {
this.layers = layers;
}
},
/**
* Function: createFilter
* Create the filter to be used in the SLD.
*
* Parameters:
* geometryAttribute - {Object} Used to get the name of the geometry
* attribute which is needed for constructing the spatial filter.
* geometry - {<OpenLayers.Geometry>} The geometry to use.
*
* Returns:
* {<OpenLayers.Filter.Spatial>} The spatial filter created.
*/
createFilter: function(geometryAttribute, geometry) {
var filter = null;
if (this.handler instanceof OpenLayers.Handler.RegularPolygon) {
// box
if (this.handler.irregular === true) {
filter = new OpenLayers.Filter.Spatial({
type: OpenLayers.Filter.Spatial.BBOX,
property: geometryAttribute.name,
value: geometry.getBounds()}
);
} else {
filter = new OpenLayers.Filter.Spatial({
type: OpenLayers.Filter.Spatial.INTERSECTS,
property: geometryAttribute.name,
value: geometry}
);
}
} else if (this.handler instanceof OpenLayers.Handler.Polygon) {
filter = new OpenLayers.Filter.Spatial({
type: OpenLayers.Filter.Spatial.INTERSECTS,
property: geometryAttribute.name,
value: geometry}
);
} else if (this.handler instanceof OpenLayers.Handler.Path) {
// if source layer is point based, use DWITHIN instead
if (geometryAttribute.type.indexOf('Point') >= 0) {
filter = new OpenLayers.Filter.Spatial({
type: OpenLayers.Filter.Spatial.DWITHIN,
property: geometryAttribute.name,
distance: this.map.getExtent().getWidth()*0.01 ,
distanceUnits: this.map.getUnits(),
value: geometry}
);
} else {
filter = new OpenLayers.Filter.Spatial({
type: OpenLayers.Filter.Spatial.INTERSECTS,
property: geometryAttribute.name,
value: geometry}
);
}
} else if (this.handler instanceof OpenLayers.Handler.Click) {
if (geometryAttribute.type.indexOf('Polygon') >= 0) {
filter = new OpenLayers.Filter.Spatial({
type: OpenLayers.Filter.Spatial.INTERSECTS,
property: geometryAttribute.name,
value: geometry}
);
} else {
filter = new OpenLayers.Filter.Spatial({
type: OpenLayers.Filter.Spatial.DWITHIN,
property: geometryAttribute.name,
distance: this.map.getExtent().getWidth()*0.01 ,
distanceUnits: this.map.getUnits(),
value: geometry}
);
}
}
return filter;
},
/**
* Method: select
* When the handler is done, use SLD_BODY on the selection layer to
* display the selection in the map.
*
* Parameters:
* geometry - {Object} or {<OpenLayers.Geometry>}
*/
select: function(geometry) {
this._queue = function() {
for (var i=0, len=this.layers.length; i<len; i++) {
var layer = this.layers[i];
var geometryAttributes = this.getGeometryAttributes(layer);
var filters = [];
for (var j=0, lenj=geometryAttributes.length; j<lenj; j++) {
var geometryAttribute = geometryAttributes[j];
if (geometryAttribute !== null) {
// from the click handler we will not get an actual
// geometry so transform
if (!(geometry instanceof OpenLayers.Geometry)) {
var point = this.map.getLonLatFromPixel(
geometry.xy);
geometry = new OpenLayers.Geometry.Point(
point.lon, point.lat);
}
var filter = this.createFilter(geometryAttribute,
geometry);
if (filter !== null) {
filters.push(filter);
}
}
}
var selectionLayer = this.createSelectionLayer(layer);
this.events.triggerEvent("selected", {
layer: layer,
filters: filters
});
var sld = this.createSLD(layer, filters, geometryAttributes);
selectionLayer.mergeNewParams({SLD_BODY: sld});
delete this._queue;
}
};
this.applySelection();
},
/**
* Method: applySelection
* Checks if all required wfs data is cached, and applies the selection
*/
applySelection: function() {
var canApply = true;
for (var i=0, len=this.layers.length; i<len; i++) {
if(!this.wfsCache[this.layers[i].id]) {
canApply = false;
break;
}
}
canApply && this._queue.call(this);
},
CLASS_NAME: "OpenLayers.Control.SLDSelect"
});

View File

@ -0,0 +1,100 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Lang.js
*/
/**
* Class: OpenLayers.Control.Scale
* The Scale control displays the current map scale as a ratio (e.g. Scale =
* 1:1M). By default it is displayed in the lower right corner of the map.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.Scale = OpenLayers.Class(OpenLayers.Control, {
/**
* Property: element
* {DOMElement}
*/
element: null,
/**
* APIProperty: geodesic
* {Boolean} Use geodesic measurement. Default is false. The recommended
* setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to
* true, the scale will be calculated based on the horizontal size of the
* pixel in the center of the map viewport.
*/
geodesic: false,
/**
* Constructor: OpenLayers.Control.Scale
*
* Parameters:
* element - {DOMElement}
* options - {Object}
*/
initialize: function(element, options) {
OpenLayers.Control.prototype.initialize.apply(this, [options]);
this.element = OpenLayers.Util.getElement(element);
},
/**
* Method: draw
*
* Returns:
* {DOMElement}
*/
draw: function() {
OpenLayers.Control.prototype.draw.apply(this, arguments);
if (!this.element) {
this.element = document.createElement("div");
this.div.appendChild(this.element);
}
this.map.events.register( 'moveend', this, this.updateScale);
this.updateScale();
return this.div;
},
/**
* Method: updateScale
*/
updateScale: function() {
var scale;
if(this.geodesic === true) {
var units = this.map.getUnits();
if(!units) {
return;
}
var inches = OpenLayers.INCHES_PER_UNIT;
scale = (this.map.getGeodesicPixelSize().w || 0.000001) *
inches["km"] * OpenLayers.DOTS_PER_INCH;
} else {
scale = this.map.getScale();
}
if (!scale) {
return;
}
if (scale >= 9500 && scale <= 950000) {
scale = Math.round(scale / 1000) + "K";
} else if (scale >= 950000) {
scale = Math.round(scale / 1000000) + "M";
} else {
scale = Math.round(scale);
}
this.element.innerHTML = OpenLayers.i18n("Scale = 1 : ${scaleDenom}", {'scaleDenom':scale});
},
CLASS_NAME: "OpenLayers.Control.Scale"
});

View File

@ -0,0 +1,220 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
*/
/**
* Class: OpenLayers.Control.ScaleLine
* The ScaleLine displays a small line indicator representing the current
* map scale on the map. By default it is drawn in the lower left corner of
* the map.
*
* Inherits from:
* - <OpenLayers.Control>
*
* Is a very close copy of:
* - <OpenLayers.Control.Scale>
*/
OpenLayers.Control.ScaleLine = OpenLayers.Class(OpenLayers.Control, {
/**
* Property: maxWidth
* {Integer} Maximum width of the scale line in pixels. Default is 100.
*/
maxWidth: 100,
/**
* Property: topOutUnits
* {String} Units for zoomed out on top bar. Default is km.
*/
topOutUnits: "km",
/**
* Property: topInUnits
* {String} Units for zoomed in on top bar. Default is m.
*/
topInUnits: "m",
/**
* Property: bottomOutUnits
* {String} Units for zoomed out on bottom bar. Default is mi.
*/
bottomOutUnits: "mi",
/**
* Property: bottomInUnits
* {String} Units for zoomed in on bottom bar. Default is ft.
*/
bottomInUnits: "ft",
/**
* Property: eTop
* {DOMElement}
*/
eTop: null,
/**
* Property: eBottom
* {DOMElement}
*/
eBottom:null,
/**
* APIProperty: geodesic
* {Boolean} Use geodesic measurement. Default is false. The recommended
* setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to
* true, the scale will be calculated based on the horizontal size of the
* pixel in the center of the map viewport.
*/
geodesic: false,
/**
* Constructor: OpenLayers.Control.ScaleLine
* Create a new scale line control.
*
* Parameters:
* options - {Object} An optional object whose properties will be used
* to extend the control.
*/
/**
* Method: draw
*
* Returns:
* {DOMElement}
*/
draw: function() {
OpenLayers.Control.prototype.draw.apply(this, arguments);
if (!this.eTop) {
// stick in the top bar
this.eTop = document.createElement("div");
this.eTop.className = this.displayClass + "Top";
var theLen = this.topInUnits.length;
this.div.appendChild(this.eTop);
if((this.topOutUnits == "") || (this.topInUnits == "")) {
this.eTop.style.visibility = "hidden";
} else {
this.eTop.style.visibility = "visible";
}
// and the bottom bar
this.eBottom = document.createElement("div");
this.eBottom.className = this.displayClass + "Bottom";
this.div.appendChild(this.eBottom);
if((this.bottomOutUnits == "") || (this.bottomInUnits == "")) {
this.eBottom.style.visibility = "hidden";
} else {
this.eBottom.style.visibility = "visible";
}
}
this.map.events.register('moveend', this, this.update);
this.update();
return this.div;
},
/**
* Method: getBarLen
* Given a number, round it down to the nearest 1,2,5 times a power of 10.
* That seems a fairly useful set of number groups to use.
*
* Parameters:
* maxLen - {float} the number we're rounding down from
*
* Returns:
* {Float} the rounded number (less than or equal to maxLen)
*/
getBarLen: function(maxLen) {
// nearest power of 10 lower than maxLen
var digits = parseInt(Math.log(maxLen) / Math.log(10));
var pow10 = Math.pow(10, digits);
// ok, find first character
var firstChar = parseInt(maxLen / pow10);
// right, put it into the correct bracket
var barLen;
if(firstChar > 5) {
barLen = 5;
} else if(firstChar > 2) {
barLen = 2;
} else {
barLen = 1;
}
// scale it up the correct power of 10
return barLen * pow10;
},
/**
* Method: update
* Update the size of the bars, and the labels they contain.
*/
update: function() {
var res = this.map.getResolution();
if (!res) {
return;
}
var curMapUnits = this.map.getUnits();
var inches = OpenLayers.INCHES_PER_UNIT;
// convert maxWidth to map units
var maxSizeData = this.maxWidth * res * inches[curMapUnits];
var geodesicRatio = 1;
if(this.geodesic === true) {
var maxSizeGeodesic = (this.map.getGeodesicPixelSize().w ||
0.000001) * this.maxWidth;
var maxSizeKilometers = maxSizeData / inches["km"];
geodesicRatio = maxSizeGeodesic / maxSizeKilometers;
maxSizeData *= geodesicRatio;
}
// decide whether to use large or small scale units
var topUnits;
var bottomUnits;
if(maxSizeData > 100000) {
topUnits = this.topOutUnits;
bottomUnits = this.bottomOutUnits;
} else {
topUnits = this.topInUnits;
bottomUnits = this.bottomInUnits;
}
// and to map units units
var topMax = maxSizeData / inches[topUnits];
var bottomMax = maxSizeData / inches[bottomUnits];
// now trim this down to useful block length
var topRounded = this.getBarLen(topMax);
var bottomRounded = this.getBarLen(bottomMax);
// and back to display units
topMax = topRounded / inches[curMapUnits] * inches[topUnits];
bottomMax = bottomRounded / inches[curMapUnits] * inches[bottomUnits];
// and to pixel units
var topPx = topMax / res / geodesicRatio;
var bottomPx = bottomMax / res / geodesicRatio;
// now set the pixel widths
// and the values inside them
if (this.eBottom.style.visibility == "visible"){
this.eBottom.style.width = Math.round(bottomPx) + "px";
this.eBottom.innerHTML = bottomRounded + " " + bottomUnits ;
}
if (this.eTop.style.visibility == "visible"){
this.eTop.style.width = Math.round(topPx) + "px";
this.eTop.innerHTML = topRounded + " " + topUnits;
}
},
CLASS_NAME: "OpenLayers.Control.ScaleLine"
});

View File

@ -0,0 +1,643 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Feature/Vector.js
* @requires OpenLayers/Handler/Feature.js
* @requires OpenLayers/Layer/Vector/RootContainer.js
*/
/**
* Class: OpenLayers.Control.SelectFeature
* The SelectFeature control selects vector features from a given layer on
* click or hover.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: events
* {<OpenLayers.Events>} Events instance for listeners and triggering
* control specific events.
*
* Register a listener for a particular event with the following syntax:
* (code)
* control.events.register(type, obj, listener);
* (end)
*
* Supported event types (in addition to those from <OpenLayers.Control.events>):
* beforefeaturehighlighted - Triggered before a feature is highlighted
* featurehighlighted - Triggered when a feature is highlighted
* featureunhighlighted - Triggered when a feature is unhighlighted
* boxselectionstart - Triggered before box selection starts
* boxselectionend - Triggered after box selection ends
*/
/**
* Property: multipleKey
* {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
* the <multiple> property to true. Default is null.
*/
multipleKey: null,
/**
* Property: toggleKey
* {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
* the <toggle> property to true. Default is null.
*/
toggleKey: null,
/**
* APIProperty: multiple
* {Boolean} Allow selection of multiple geometries. Default is false.
*/
multiple: false,
/**
* APIProperty: clickout
* {Boolean} Unselect features when clicking outside any feature.
* Default is true.
*/
clickout: true,
/**
* APIProperty: toggle
* {Boolean} Unselect a selected feature on click. Default is false. Only
* has meaning if hover is false.
*/
toggle: false,
/**
* APIProperty: hover
* {Boolean} Select on mouse over and deselect on mouse out. If true, this
* ignores clicks and only listens to mouse moves.
*/
hover: false,
/**
* APIProperty: highlightOnly
* {Boolean} If true do not actually select features (that is place them in
* the layer's selected features array), just highlight them. This property
* has no effect if hover is false. Defaults to false.
*/
highlightOnly: false,
/**
* APIProperty: box
* {Boolean} Allow feature selection by drawing a box.
*/
box: false,
/**
* Property: onBeforeSelect
* {Function} Optional function to be called before a feature is selected.
* The function should expect to be called with a feature.
*/
onBeforeSelect: function() {},
/**
* APIProperty: onSelect
* {Function} Optional function to be called when a feature is selected.
* The function should expect to be called with a feature.
*/
onSelect: function() {},
/**
* APIProperty: onUnselect
* {Function} Optional function to be called when a feature is unselected.
* The function should expect to be called with a feature.
*/
onUnselect: function() {},
/**
* Property: scope
* {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect
* callbacks. If null the scope will be this control.
*/
scope: null,
/**
* APIProperty: geometryTypes
* {Array(String)} To restrict selecting to a limited set of geometry types,
* send a list of strings corresponding to the geometry class names.
*/
geometryTypes: null,
/**
* Property: layer
* {<OpenLayers.Layer.Vector>} The vector layer with a common renderer
* root for all layers this control is configured with (if an array of
* layers was passed to the constructor), or the vector layer the control
* was configured with (if a single layer was passed to the constructor).
*/
layer: null,
/**
* Property: layers
* {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on,
* or null if the control was configured with a single layer
*/
layers: null,
/**
* APIProperty: callbacks
* {Object} The functions that are sent to the handlers.feature for callback
*/
callbacks: null,
/**
* APIProperty: selectStyle
* {Object} Hash of styles
*/
selectStyle: null,
/**
* Property: renderIntent
* {String} key used to retrieve the select style from the layer's
* style map.
*/
renderIntent: "select",
/**
* Property: handlers
* {Object} Object with references to multiple <OpenLayers.Handler>
* instances.
*/
handlers: null,
/**
* Constructor: OpenLayers.Control.SelectFeature
* Create a new control for selecting features.
*
* Parameters:
* layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The
* layer(s) this control will select features from.
* options - {Object}
*/
initialize: function(layers, options) {
OpenLayers.Control.prototype.initialize.apply(this, [options]);
if(this.scope === null) {
this.scope = this;
}
this.initLayer(layers);
var callbacks = {
click: this.clickFeature,
clickout: this.clickoutFeature
};
if (this.hover) {
callbacks.over = this.overFeature;
callbacks.out = this.outFeature;
}
this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);
this.handlers = {
feature: new OpenLayers.Handler.Feature(
this, this.layer, this.callbacks,
{geometryTypes: this.geometryTypes}
)
};
if (this.box) {
this.handlers.box = new OpenLayers.Handler.Box(
this, {done: this.selectBox},
{boxDivClassName: "olHandlerBoxSelectFeature"}
);
}
},
/**
* Method: initLayer
* Assign the layer property. If layers is an array, we need to use
* a RootContainer.
*
* Parameters:
* layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers.
*/
initLayer: function(layers) {
if(OpenLayers.Util.isArray(layers)) {
this.layers = layers;
this.layer = new OpenLayers.Layer.Vector.RootContainer(
this.id + "_container", {
layers: layers
}
);
} else {
this.layer = layers;
}
},
/**
* Method: destroy
*/
destroy: function() {
if(this.active && this.layers) {
this.map.removeLayer(this.layer);
}
OpenLayers.Control.prototype.destroy.apply(this, arguments);
if(this.layers) {
this.layer.destroy();
}
},
/**
* Method: activate
* Activates the control.
*
* Returns:
* {Boolean} The control was effectively activated.
*/
activate: function () {
if (!this.active) {
if(this.layers) {
this.map.addLayer(this.layer);
}
this.handlers.feature.activate();
if(this.box && this.handlers.box) {
this.handlers.box.activate();
}
}
return OpenLayers.Control.prototype.activate.apply(
this, arguments
);
},
/**
* Method: deactivate
* Deactivates the control.
*
* Returns:
* {Boolean} The control was effectively deactivated.
*/
deactivate: function () {
if (this.active) {
this.handlers.feature.deactivate();
if(this.handlers.box) {
this.handlers.box.deactivate();
}
if(this.layers) {
this.map.removeLayer(this.layer);
}
}
return OpenLayers.Control.prototype.deactivate.apply(
this, arguments
);
},
/**
* Method: unselectAll
* Unselect all selected features. To unselect all except for a single
* feature, set the options.except property to the feature.
*
* Parameters:
* options - {Object} Optional configuration object.
*/
unselectAll: function(options) {
// we'll want an option to supress notification here
var layers = this.layers || [this.layer],
layer, feature, l, numExcept;
for(l=0; l<layers.length; ++l) {
layer = layers[l];
numExcept = 0;
//layer.selectedFeatures is null when layer is destroyed and
//one of it's preremovelayer listener calls setLayer
//with another layer on this control
if(layer.selectedFeatures != null) {
while(layer.selectedFeatures.length > numExcept) {
feature = layer.selectedFeatures[numExcept];
if(!options || options.except != feature) {
this.unselect(feature);
} else {
++numExcept;
}
}
}
}
},
/**
* Method: clickFeature
* Called on click in a feature
* Only responds if this.hover is false.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
*/
clickFeature: function(feature) {
if(!this.hover) {
var selected = (OpenLayers.Util.indexOf(
feature.layer.selectedFeatures, feature) > -1);
if(selected) {
if(this.toggleSelect()) {
this.unselect(feature);
} else if(!this.multipleSelect()) {
this.unselectAll({except: feature});
}
} else {
if(!this.multipleSelect()) {
this.unselectAll({except: feature});
}
this.select(feature);
}
}
},
/**
* Method: multipleSelect
* Allow for multiple selected features based on <multiple> property and
* <multipleKey> event modifier.
*
* Returns:
* {Boolean} Allow for multiple selected features.
*/
multipleSelect: function() {
return this.multiple || (this.handlers.feature.evt &&
this.handlers.feature.evt[this.multipleKey]);
},
/**
* Method: toggleSelect
* Event should toggle the selected state of a feature based on <toggle>
* property and <toggleKey> event modifier.
*
* Returns:
* {Boolean} Toggle the selected state of a feature.
*/
toggleSelect: function() {
return this.toggle || (this.handlers.feature.evt &&
this.handlers.feature.evt[this.toggleKey]);
},
/**
* Method: clickoutFeature
* Called on click outside a previously clicked (selected) feature.
* Only responds if this.hover is false.
*
* Parameters:
* feature - {<OpenLayers.Vector.Feature>}
*/
clickoutFeature: function(feature) {
if(!this.hover && this.clickout) {
this.unselectAll();
}
},
/**
* Method: overFeature
* Called on over a feature.
* Only responds if this.hover is true.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
*/
overFeature: function(feature) {
var layer = feature.layer;
if(this.hover) {
if(this.highlightOnly) {
this.highlight(feature);
} else if(OpenLayers.Util.indexOf(
layer.selectedFeatures, feature) == -1) {
this.select(feature);
}
}
},
/**
* Method: outFeature
* Called on out of a selected feature.
* Only responds if this.hover is true.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
*/
outFeature: function(feature) {
if(this.hover) {
if(this.highlightOnly) {
// we do nothing if we're not the last highlighter of the
// feature
if(feature._lastHighlighter == this.id) {
// if another select control had highlighted the feature before
// we did it ourself then we use that control to highlight the
// feature as it was before we highlighted it, else we just
// unhighlight it
if(feature._prevHighlighter &&
feature._prevHighlighter != this.id) {
delete feature._lastHighlighter;
var control = this.map.getControl(
feature._prevHighlighter);
if(control) {
control.highlight(feature);
}
} else {
this.unhighlight(feature);
}
}
} else {
this.unselect(feature);
}
}
},
/**
* Method: highlight
* Redraw feature with the select style.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
*/
highlight: function(feature) {
var layer = feature.layer;
var cont = this.events.triggerEvent("beforefeaturehighlighted", {
feature : feature
});
if(cont !== false) {
feature._prevHighlighter = feature._lastHighlighter;
feature._lastHighlighter = this.id;
var style = this.selectStyle || this.renderIntent;
layer.drawFeature(feature, style);
this.events.triggerEvent("featurehighlighted", {feature : feature});
}
},
/**
* Method: unhighlight
* Redraw feature with the "default" style
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
*/
unhighlight: function(feature) {
var layer = feature.layer;
// three cases:
// 1. there's no other highlighter, in that case _prev is undefined,
// and we just need to undef _last
// 2. another control highlighted the feature after we did it, in
// that case _last references this other control, and we just
// need to undef _prev
// 3. another control highlighted the feature before we did it, in
// that case _prev references this other control, and we need to
// set _last to _prev and undef _prev
if(feature._prevHighlighter == undefined) {
delete feature._lastHighlighter;
} else if(feature._prevHighlighter == this.id) {
delete feature._prevHighlighter;
} else {
feature._lastHighlighter = feature._prevHighlighter;
delete feature._prevHighlighter;
}
layer.drawFeature(feature, feature.style || feature.layer.style ||
"default");
this.events.triggerEvent("featureunhighlighted", {feature : feature});
},
/**
* Method: select
* Add feature to the layer's selectedFeature array, render the feature as
* selected, and call the onSelect function.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
*/
select: function(feature) {
var cont = this.onBeforeSelect.call(this.scope, feature);
var layer = feature.layer;
if(cont !== false) {
cont = layer.events.triggerEvent("beforefeatureselected", {
feature: feature
});
if(cont !== false) {
layer.selectedFeatures.push(feature);
this.highlight(feature);
// if the feature handler isn't involved in the feature
// selection (because the box handler is used or the
// feature is selected programatically) we fake the
// feature handler to allow unselecting on click
if(!this.handlers.feature.lastFeature) {
this.handlers.feature.lastFeature = layer.selectedFeatures[0];
}
layer.events.triggerEvent("featureselected", {feature: feature});
this.onSelect.call(this.scope, feature);
}
}
},
/**
* Method: unselect
* Remove feature from the layer's selectedFeature array, render the feature as
* normal, and call the onUnselect function.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
*/
unselect: function(feature) {
var layer = feature.layer;
// Store feature style for restoration later
this.unhighlight(feature);
OpenLayers.Util.removeItem(layer.selectedFeatures, feature);
layer.events.triggerEvent("featureunselected", {feature: feature});
this.onUnselect.call(this.scope, feature);
},
/**
* Method: selectBox
* Callback from the handlers.box set up when <box> selection is true
* on.
*
* Parameters:
* position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> }
*/
selectBox: function(position) {
if (position instanceof OpenLayers.Bounds) {
var minXY = this.map.getLonLatFromPixel({
x: position.left,
y: position.bottom
});
var maxXY = this.map.getLonLatFromPixel({
x: position.right,
y: position.top
});
var bounds = new OpenLayers.Bounds(
minXY.lon, minXY.lat, maxXY.lon, maxXY.lat
);
// if multiple is false, first deselect currently selected features
if (!this.multipleSelect()) {
this.unselectAll();
}
// because we're using a box, we consider we want multiple selection
var prevMultiple = this.multiple;
this.multiple = true;
var layers = this.layers || [this.layer];
this.events.triggerEvent("boxselectionstart", {layers: layers});
var layer;
for(var l=0; l<layers.length; ++l) {
layer = layers[l];
for(var i=0, len = layer.features.length; i<len; ++i) {
var feature = layer.features[i];
// check if the feature is displayed
if (!feature.getVisibility()) {
continue;
}
if (this.geometryTypes == null || OpenLayers.Util.indexOf(
this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {
if (bounds.toGeometry().intersects(feature.geometry)) {
if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {
this.select(feature);
}
}
}
}
}
this.multiple = prevMultiple;
this.events.triggerEvent("boxselectionend", {layers: layers});
}
},
/**
* Method: setMap
* Set the map property for the control.
*
* Parameters:
* map - {<OpenLayers.Map>}
*/
setMap: function(map) {
this.handlers.feature.setMap(map);
if (this.box) {
this.handlers.box.setMap(map);
}
OpenLayers.Control.prototype.setMap.apply(this, arguments);
},
/**
* APIMethod: setLayer
* Attach a new layer to the control, overriding any existing layers.
*
* Parameters:
* layers - Array of {<OpenLayers.Layer.Vector>} or a single
* {<OpenLayers.Layer.Vector>}
*/
setLayer: function(layers) {
var isActive = this.active;
this.unselectAll();
this.deactivate();
if(this.layers) {
this.layer.destroy();
this.layers = null;
}
this.initLayer(layers);
this.handlers.feature.layer = this.layer;
if (isActive) {
this.activate();
}
},
CLASS_NAME: "OpenLayers.Control.SelectFeature"
});

View File

@ -0,0 +1,560 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Layer/Vector.js
*/
/**
* Class: OpenLayers.Control.Snapping
* Acts as a snapping agent while editing vector features.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.Snapping = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: events
* {<OpenLayers.Events>} Events instance for listeners and triggering
* control specific events.
*
* Register a listener for a particular event with the following syntax:
* (code)
* control.events.register(type, obj, listener);
* (end)
*
* Supported event types (in addition to those from <OpenLayers.Control.events>):
* beforesnap - Triggered before a snap occurs. Listeners receive an
* event object with *point*, *x*, *y*, *distance*, *layer*, and
* *snapType* properties. The point property will be original point
* geometry considered for snapping. The x and y properties represent
* coordinates the point will receive. The distance is the distance
* of the snap. The layer is the target layer. The snapType property
* will be one of "node", "vertex", or "edge". Return false to stop
* snapping from occurring.
* snap - Triggered when a snap occurs. Listeners receive an event with
* *point*, *snapType*, *layer*, and *distance* properties. The point
* will be the location snapped to. The snapType will be one of "node",
* "vertex", or "edge". The layer will be the target layer. The
* distance will be the distance of the snap in map units.
* unsnap - Triggered when a vertex is unsnapped. Listeners receive an
* event with a *point* property.
*/
/**
* CONSTANT: DEFAULTS
* Default target properties.
*/
DEFAULTS: {
tolerance: 10,
node: true,
edge: true,
vertex: true
},
/**
* Property: greedy
* {Boolean} Snap to closest feature in first layer with an eligible
* feature. Default is true.
*/
greedy: true,
/**
* Property: precedence
* {Array} List representing precedence of different snapping types.
* Default is "node", "vertex", "edge".
*/
precedence: ["node", "vertex", "edge"],
/**
* Property: resolution
* {Float} The map resolution for the previously considered snap.
*/
resolution: null,
/**
* Property: geoToleranceCache
* {Object} A cache of geo-tolerances. Tolerance values (in map units) are
* calculated when the map resolution changes.
*/
geoToleranceCache: null,
/**
* Property: layer
* {<OpenLayers.Layer.Vector>} The current editable layer. Set at
* construction or after construction with <setLayer>.
*/
layer: null,
/**
* Property: feature
* {<OpenLayers.Feature.Vector>} The current editable feature.
*/
feature: null,
/**
* Property: point
* {<OpenLayers.Geometry.Point>} The currently snapped vertex.
*/
point: null,
/**
* Constructor: OpenLayers.Control.Snapping
* Creates a new snapping control. A control is constructed with an editable
* layer and a set of configuration objects for target layers. While the
* control is active, dragging vertices while drawing new features or
* modifying existing features on the editable layer will engage
* snapping to features on the target layers. Whether a vertex snaps to
* a feature on a target layer depends on the target layer configuration.
*
* Parameters:
* options - {Object} An object containing all configuration properties for
* the control.
*
* Valid options:
* layer - {<OpenLayers.Layer.Vector>} The editable layer. Features from this
* layer that are digitized or modified may have vertices snapped to
* features from any of the target layers.
* targets - {Array(Object | OpenLayers.Layer.Vector)} A list of objects for
* configuring target layers. See valid properties of the target
* objects below. If the items in the targets list are vector layers
* (instead of configuration objects), the defaults from the <defaults>
* property will apply. The editable layer itself may be a target
* layer, allowing newly created or edited features to be snapped to
* existing features from the same layer. If no targets are provided
* the layer given in the constructor (as <layer>) will become the
* initial target.
* defaults - {Object} An object with default properties to be applied
* to all target objects.
* greedy - {Boolean} Snap to closest feature in first target layer that
* applies. Default is true. If false, all features in all target
* layers will be checked and the closest feature in all target layers
* will be chosen. The greedy property determines if the order of the
* target layers is significant. By default, the order of the target
* layers is significant where layers earlier in the target layer list
* have precedence over layers later in the list. Within a single
* layer, the closest feature is always chosen for snapping. This
* property only determines whether the search for a closer feature
* continues after an eligible feature is found in a target layer.
*
* Valid target properties:
* layer - {<OpenLayers.Layer.Vector>} A target layer. Features from this
* layer will be eligible to act as snapping target for the editable
* layer.
* tolerance - {Float} The distance (in pixels) at which snapping may occur.
* Default is 10.
* node - {Boolean} Snap to nodes (first or last point in a geometry) in
* target layer. Default is true.
* nodeTolerance - {Float} Optional distance at which snapping may occur
* for nodes specifically. If none is provided, <tolerance> will be
* used.
* vertex - {Boolean} Snap to vertices in target layer. Default is true.
* vertexTolerance - {Float} Optional distance at which snapping may occur
* for vertices specifically. If none is provided, <tolerance> will be
* used.
* edge - {Boolean} Snap to edges in target layer. Default is true.
* edgeTolerance - {Float} Optional distance at which snapping may occur
* for edges specifically. If none is provided, <tolerance> will be
* used.
* filter - {<OpenLayers.Filter>} Optional filter to evaluate to determine if
* feature is eligible for snapping. If filter evaluates to true for a
* target feature a vertex may be snapped to the feature.
* minResolution - {Number} If a minResolution is provided, snapping to this
* target will only be considered if the map resolution is greater than
* or equal to this value (the minResolution is inclusive). Default is
* no minimum resolution limit.
* maxResolution - {Number} If a maxResolution is provided, snapping to this
* target will only be considered if the map resolution is strictly
* less than this value (the maxResolution is exclusive). Default is
* no maximum resolution limit.
*/
initialize: function(options) {
OpenLayers.Control.prototype.initialize.apply(this, [options]);
this.options = options || {}; // TODO: this could be done by the super
// set the editable layer if provided
if(this.options.layer) {
this.setLayer(this.options.layer);
}
// configure target layers
var defaults = OpenLayers.Util.extend({}, this.options.defaults);
this.defaults = OpenLayers.Util.applyDefaults(defaults, this.DEFAULTS);
this.setTargets(this.options.targets);
if(this.targets.length === 0 && this.layer) {
this.addTargetLayer(this.layer);
}
this.geoToleranceCache = {};
},
/**
* APIMethod: setLayer
* Set the editable layer. Call the setLayer method if the editable layer
* changes and the same control should be used on a new editable layer.
* If the control is already active, it will be active after the new
* layer is set.
*
* Parameters:
* layer - {<OpenLayers.Layer.Vector>} The new editable layer.
*/
setLayer: function(layer) {
if(this.active) {
this.deactivate();
this.layer = layer;
this.activate();
} else {
this.layer = layer;
}
},
/**
* Method: setTargets
* Set the targets for the snapping agent.
*
* Parameters:
* targets - {Array} An array of target configs or target layers.
*/
setTargets: function(targets) {
this.targets = [];
if(targets && targets.length) {
var target;
for(var i=0, len=targets.length; i<len; ++i) {
target = targets[i];
if(target instanceof OpenLayers.Layer.Vector) {
this.addTargetLayer(target);
} else {
this.addTarget(target);
}
}
}
},
/**
* Method: addTargetLayer
* Add a target layer with the default target config.
*
* Parameters:
* layer - {<OpenLayers.Layer.Vector>} A target layer.
*/
addTargetLayer: function(layer) {
this.addTarget({layer: layer});
},
/**
* Method: addTarget
* Add a configured target layer.
*
* Parameters:
* target - {Object} A target config.
*/
addTarget: function(target) {
target = OpenLayers.Util.applyDefaults(target, this.defaults);
target.nodeTolerance = target.nodeTolerance || target.tolerance;
target.vertexTolerance = target.vertexTolerance || target.tolerance;
target.edgeTolerance = target.edgeTolerance || target.tolerance;
this.targets.push(target);
},
/**
* Method: removeTargetLayer
* Remove a target layer.
*
* Parameters:
* layer - {<OpenLayers.Layer.Vector>} The target layer to remove.
*/
removeTargetLayer: function(layer) {
var target;
for(var i=this.targets.length-1; i>=0; --i) {
target = this.targets[i];
if(target.layer === layer) {
this.removeTarget(target);
}
}
},
/**
* Method: removeTarget
* Remove a target.
*
* Parameters:
* target - {Object} A target config.
*
* Returns:
* {Array} The targets array.
*/
removeTarget: function(target) {
return OpenLayers.Util.removeItem(this.targets, target);
},
/**
* APIMethod: activate
* Activate the control. Activating the control registers listeners for
* editing related events so that during feature creation and
* modification, moving vertices will trigger snapping.
*/
activate: function() {
var activated = OpenLayers.Control.prototype.activate.call(this);
if(activated) {
if(this.layer && this.layer.events) {
this.layer.events.on({
sketchstarted: this.onSketchModified,
sketchmodified: this.onSketchModified,
vertexmodified: this.onVertexModified,
scope: this
});
}
}
return activated;
},
/**
* APIMethod: deactivate
* Deactivate the control. Deactivating the control unregisters listeners
* so feature editing may proceed without engaging the snapping agent.
*/
deactivate: function() {
var deactivated = OpenLayers.Control.prototype.deactivate.call(this);
if(deactivated) {
if(this.layer && this.layer.events) {
this.layer.events.un({
sketchstarted: this.onSketchModified,
sketchmodified: this.onSketchModified,
vertexmodified: this.onVertexModified,
scope: this
});
}
}
this.feature = null;
this.point = null;
return deactivated;
},
/**
* Method: onSketchModified
* Registered as a listener for the sketchmodified event on the editable
* layer.
*
* Parameters:
* event - {Object} The sketch modified event.
*/
onSketchModified: function(event) {
this.feature = event.feature;
this.considerSnapping(event.vertex, event.vertex);
},
/**
* Method: onVertexModified
* Registered as a listener for the vertexmodified event on the editable
* layer.
*
* Parameters:
* event - {Object} The vertex modified event.
*/
onVertexModified: function(event) {
this.feature = event.feature;
var loc = this.layer.map.getLonLatFromViewPortPx(event.pixel);
this.considerSnapping(
event.vertex, new OpenLayers.Geometry.Point(loc.lon, loc.lat)
);
},
/**
* Method: considerSnapping
*
* Parameters:
* point - {<OpenLayers.Geometry.Point>} The vertex to be snapped (or
* unsnapped).
* loc - {<OpenLayers.Geometry.Point>} The location of the mouse in map
* coords.
*/
considerSnapping: function(point, loc) {
var best = {
rank: Number.POSITIVE_INFINITY,
dist: Number.POSITIVE_INFINITY,
x: null, y: null
};
var snapped = false;
var result, target;
for(var i=0, len=this.targets.length; i<len; ++i) {
target = this.targets[i];
result = this.testTarget(target, loc);
if(result) {
if(this.greedy) {
best = result;
best.target = target;
snapped = true;
break;
} else {
if((result.rank < best.rank) ||
(result.rank === best.rank && result.dist < best.dist)) {
best = result;
best.target = target;
snapped = true;
}
}
}
}
if(snapped) {
var proceed = this.events.triggerEvent("beforesnap", {
point: point, x: best.x, y: best.y, distance: best.dist,
layer: best.target.layer, snapType: this.precedence[best.rank]
});
if(proceed !== false) {
point.x = best.x;
point.y = best.y;
this.point = point;
this.events.triggerEvent("snap", {
point: point,
snapType: this.precedence[best.rank],
layer: best.target.layer,
distance: best.dist
});
} else {
snapped = false;
}
}
if(this.point && !snapped) {
point.x = loc.x;
point.y = loc.y;
this.point = null;
this.events.triggerEvent("unsnap", {point: point});
}
},
/**
* Method: testTarget
*
* Parameters:
* target - {Object} Object with target layer configuration.
* loc - {<OpenLayers.Geometry.Point>} The location of the mouse in map
* coords.
*
* Returns:
* {Object} A result object with rank, dist, x, and y properties.
* Returns null if candidate is not eligible for snapping.
*/
testTarget: function(target, loc) {
var resolution = this.layer.map.getResolution();
if ("minResolution" in target) {
if (resolution < target.minResolution) {
return null;
}
}
if ("maxResolution" in target) {
if (resolution >= target.maxResolution) {
return null;
}
}
var tolerance = {
node: this.getGeoTolerance(target.nodeTolerance, resolution),
vertex: this.getGeoTolerance(target.vertexTolerance, resolution),
edge: this.getGeoTolerance(target.edgeTolerance, resolution)
};
// this could be cached if we don't support setting tolerance values directly
var maxTolerance = Math.max(
tolerance.node, tolerance.vertex, tolerance.edge
);
var result = {
rank: Number.POSITIVE_INFINITY, dist: Number.POSITIVE_INFINITY
};
var eligible = false;
var features = target.layer.features;
var feature, type, vertices, vertex, closest, dist, found;
var numTypes = this.precedence.length;
var ll = new OpenLayers.LonLat(loc.x, loc.y);
for(var i=0, len=features.length; i<len; ++i) {
feature = features[i];
if(feature !== this.feature && !feature._sketch &&
feature.state !== OpenLayers.State.DELETE &&
(!target.filter || target.filter.evaluate(feature))) {
if(feature.atPoint(ll, maxTolerance, maxTolerance)) {
for(var j=0, stop=Math.min(result.rank+1, numTypes); j<stop; ++j) {
type = this.precedence[j];
if(target[type]) {
if(type === "edge") {
closest = feature.geometry.distanceTo(loc, {details: true});
dist = closest.distance;
if(dist <= tolerance[type] && dist < result.dist) {
result = {
rank: j, dist: dist,
x: closest.x0, y: closest.y0 // closest coords on feature
};
eligible = true;
// don't look for lower precedence types for this feature
break;
}
} else {
// look for nodes or vertices
vertices = feature.geometry.getVertices(type === "node");
found = false;
for(var k=0, klen=vertices.length; k<klen; ++k) {
vertex = vertices[k];
dist = vertex.distanceTo(loc);
if(dist <= tolerance[type] &&
(j < result.rank || (j === result.rank && dist < result.dist))) {
result = {
rank: j, dist: dist,
x: vertex.x, y: vertex.y
};
eligible = true;
found = true;
}
}
if(found) {
// don't look for lower precedence types for this feature
break;
}
}
}
}
}
}
}
return eligible ? result : null;
},
/**
* Method: getGeoTolerance
* Calculate a tolerance in map units given a tolerance in pixels. This
* takes advantage of the <geoToleranceCache> when the map resolution
* has not changed.
*
* Parameters:
* tolerance - {Number} A tolerance value in pixels.
* resolution - {Number} Map resolution.
*
* Returns:
* {Number} A tolerance value in map units.
*/
getGeoTolerance: function(tolerance, resolution) {
if(resolution !== this.resolution) {
this.resolution = resolution;
this.geoToleranceCache = {};
}
var geoTolerance = this.geoToleranceCache[tolerance];
if(geoTolerance === undefined) {
geoTolerance = tolerance * resolution;
this.geoToleranceCache[tolerance] = geoTolerance;
}
return geoTolerance;
},
/**
* Method: destroy
* Clean up the control.
*/
destroy: function() {
if(this.active) {
this.deactivate(); // TODO: this should be handled by the super
}
delete this.layer;
delete this.targets;
OpenLayers.Control.prototype.destroy.call(this);
},
CLASS_NAME: "OpenLayers.Control.Snapping"
});

View File

@ -0,0 +1,494 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Handler/Path.js
* @requires OpenLayers/Layer/Vector.js
*/
/**
* Class: OpenLayers.Control.Split
* Acts as a split feature agent while editing vector features.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.Split = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: events
* {<OpenLayers.Events>} Events instance for listeners and triggering
* control specific events.
*
* Register a listener for a particular event with the following syntax:
* (code)
* control.events.register(type, obj, listener);
* (end)
*
* Supported event types (in addition to those from <OpenLayers.Control.events>):
* beforesplit - Triggered before a split occurs. Listeners receive an
* event object with *source* and *target* properties.
* split - Triggered when a split occurs. Listeners receive an event with
* an *original* property and a *features* property. The original
* is a reference to the target feature that the sketch or modified
* feature intersects. The features property is a list of all features
* that result from this single split. This event is triggered before
* the resulting features are added to the layer (while the layer still
* has a reference to the original).
* aftersplit - Triggered after all splits resulting from a single sketch
* or feature modification have occurred. The original features
* have been destroyed and features that result from the split
* have already been added to the layer. Listeners receive an event
* with a *source* and *features* property. The source references the
* sketch or modified feature used as a splitter. The features
* property is a list of all resulting features.
*/
/**
* APIProperty: layer
* {<OpenLayers.Layer.Vector>} The target layer with features to be split.
* Set at construction or after construction with <setLayer>.
*/
layer: null,
/**
* Property: source
* {<OpenLayers.Layer.Vector>} Optional source layer. Any newly created
* or modified features from this layer will be used to split features
* on the target layer. If not provided, a temporary sketch layer will
* be created.
*/
source: null,
/**
* Property: sourceOptions
* {Options} If a temporary sketch layer is created, these layer options
* will be applied.
*/
sourceOptions: null,
/**
* APIProperty: tolerance
* {Number} Distance between the calculated intersection and a vertex on
* the source geometry below which the existing vertex will be used
* for the split. Default is null.
*/
tolerance: null,
/**
* APIProperty: edge
* {Boolean} Allow splits given intersection of edges only. Default is
* true. If false, a vertex on the source must be within the
* <tolerance> distance of the calculated intersection for a split
* to occur.
*/
edge: true,
/**
* APIProperty: deferDelete
* {Boolean} Instead of removing features from the layer, set feature
* states of split features to DELETE. This assumes a save strategy
* or other component is in charge of removing features from the
* layer. Default is false. If false, split features will be
* immediately deleted from the layer.
*/
deferDelete: false,
/**
* APIProperty: mutual
* {Boolean} If source and target layers are the same, split source
* features and target features where they intersect. Default is
* true. If false, only target features will be split.
*/
mutual: true,
/**
* APIProperty: targetFilter
* {<OpenLayers.Filter>} Optional filter that will be evaluated
* to determine if a feature from the target layer is eligible for
* splitting.
*/
targetFilter: null,
/**
* APIProperty: sourceFilter
* {<OpenLayers.Filter>} Optional filter that will be evaluated
* to determine if a feature from the source layer is eligible for
* splitting.
*/
sourceFilter: null,
/**
* Property: handler
* {<OpenLayers.Handler.Path>} The temporary sketch handler created if
* no source layer is provided.
*/
handler: null,
/**
* Constructor: OpenLayers.Control.Split
* Creates a new split control. A control is constructed with a target
* layer and an optional source layer. While the control is active,
* creating new features or modifying existing features on the source
* layer will result in splitting any eligible features on the target
* layer. If no source layer is provided, a temporary sketch layer will
* be created to create lines for splitting features on the target.
*
* Parameters:
* options - {Object} An object containing all configuration properties for
* the control.
*
* Valid options:
* layer - {<OpenLayers.Layer.Vector>} The target layer. Features from this
* layer will be split by new or modified features on the source layer
* or temporary sketch layer.
* source - {<OpenLayers.Layer.Vector>} Optional source layer. If provided
* newly created features or modified features will be used to split
* features on the target layer. If not provided, a temporary sketch
* layer will be created for drawing lines.
* tolerance - {Number} Optional value for the distance between a source
* vertex and the calculated intersection below which the split will
* occur at the vertex.
* edge - {Boolean} Allow splits given intersection of edges only. Default
* is true. If false, a vertex on the source must be within the
* <tolerance> distance of the calculated intersection for a split
* to occur.
* mutual - {Boolean} If source and target are the same, split source
* features and target features where they intersect. Default is
* true. If false, only target features will be split.
* targetFilter - {<OpenLayers.Filter>} Optional filter that will be evaluated
* to determine if a feature from the target layer is eligible for
* splitting.
* sourceFilter - {<OpenLayers.Filter>} Optional filter that will be evaluated
* to determine if a feature from the target layer is eligible for
* splitting.
*/
initialize: function(options) {
OpenLayers.Control.prototype.initialize.apply(this, [options]);
this.options = options || {}; // TODO: this could be done by the super
// set the source layer if provided
if(this.options.source) {
this.setSource(this.options.source);
}
},
/**
* APIMethod: setSource
* Set the source layer for edits layer.
*
* Parameters:
* layer - {<OpenLayers.Layer.Vector>} The new source layer layer. If
* null, a temporary sketch layer will be created.
*/
setSource: function(layer) {
if(this.active) {
this.deactivate();
if(this.handler) {
this.handler.destroy();
delete this.handler;
}
this.source = layer;
this.activate();
} else {
this.source = layer;
}
},
/**
* APIMethod: activate
* Activate the control. Activating the control registers listeners for
* editing related events so that during feature creation and
* modification, features in the target will be considered for
* splitting.
*/
activate: function() {
var activated = OpenLayers.Control.prototype.activate.call(this);
if(activated) {
if(!this.source) {
if(!this.handler) {
this.handler = new OpenLayers.Handler.Path(this,
{done: function(geometry) {
this.onSketchComplete({
feature: new OpenLayers.Feature.Vector(geometry)
});
}},
{layerOptions: this.sourceOptions}
);
}
this.handler.activate();
} else if(this.source.events) {
this.source.events.on({
sketchcomplete: this.onSketchComplete,
afterfeaturemodified: this.afterFeatureModified,
scope: this
});
}
}
return activated;
},
/**
* APIMethod: deactivate
* Deactivate the control. Deactivating the control unregisters listeners
* so feature editing may proceed without engaging the split agent.
*/
deactivate: function() {
var deactivated = OpenLayers.Control.prototype.deactivate.call(this);
if(deactivated) {
if(this.source && this.source.events) {
this.source.events.un({
sketchcomplete: this.onSketchComplete,
afterfeaturemodified: this.afterFeatureModified,
scope: this
});
}
}
return deactivated;
},
/**
* Method: onSketchComplete
* Registered as a listener for the sketchcomplete event on the editable
* layer.
*
* Parameters:
* event - {Object} The sketch complete event.
*
* Returns:
* {Boolean} Stop the sketch from being added to the layer (it has been
* split).
*/
onSketchComplete: function(event) {
this.feature = null;
return !this.considerSplit(event.feature);
},
/**
* Method: afterFeatureModified
* Registered as a listener for the afterfeaturemodified event on the
* editable layer.
*
* Parameters:
* event - {Object} The after feature modified event.
*/
afterFeatureModified: function(event) {
if(event.modified) {
var feature = event.feature;
if (typeof feature.geometry.split === "function") {
this.feature = event.feature;
this.considerSplit(event.feature);
}
}
},
/**
* Method: removeByGeometry
* Remove a feature from a list based on the given geometry.
*
* Parameters:
* features - {Array(<OpenLayers.Feature.Vector>)} A list of features.
* geometry - {<OpenLayers.Geometry>} A geometry.
*/
removeByGeometry: function(features, geometry) {
for(var i=0, len=features.length; i<len; ++i) {
if(features[i].geometry === geometry) {
features.splice(i, 1);
break;
}
}
},
/**
* Method: isEligible
* Test if a target feature is eligible for splitting.
*
* Parameters:
* target - {<OpenLayers.Feature.Vector>} The target feature.
*
* Returns:
* {Boolean} The target is eligible for splitting.
*/
isEligible: function(target) {
if (!target.geometry) {
return false;
} else {
return (
target.state !== OpenLayers.State.DELETE
) && (
typeof target.geometry.split === "function"
) && (
this.feature !== target
) && (
!this.targetFilter ||
this.targetFilter.evaluate(target.attributes)
);
}
},
/**
* Method: considerSplit
* Decide whether or not to split target features with the supplied
* feature. If <mutual> is true, both the source and target features
* will be split if eligible.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>} The newly created or modified
* feature.
*
* Returns:
* {Boolean} The supplied feature was split (and destroyed).
*/
considerSplit: function(feature) {
var sourceSplit = false;
var targetSplit = false;
if(!this.sourceFilter ||
this.sourceFilter.evaluate(feature.attributes)) {
var features = this.layer && this.layer.features || [];
var target, results, proceed;
var additions = [], removals = [];
var mutual = (this.layer === this.source) && this.mutual;
var options = {
edge: this.edge,
tolerance: this.tolerance,
mutual: mutual
};
var sourceParts = [feature.geometry];
var targetFeature, targetParts;
var source, parts;
for(var i=0, len=features.length; i<len; ++i) {
targetFeature = features[i];
if(this.isEligible(targetFeature)) {
targetParts = [targetFeature.geometry];
// work through source geoms - this array may change
for(var j=0; j<sourceParts.length; ++j) {
source = sourceParts[j];
// work through target parts - this array may change
for(var k=0; k<targetParts.length; ++k) {
target = targetParts[k];
if(source.getBounds().intersectsBounds(target.getBounds())) {
results = source.split(target, options);
if(results) {
proceed = this.events.triggerEvent(
"beforesplit", {source: feature, target: targetFeature}
);
if(proceed !== false) {
if(mutual) {
parts = results[0];
// handle parts that result from source splitting
if(parts.length > 1) {
// splice in new source parts
parts.unshift(j, 1); // add args for splice below
Array.prototype.splice.apply(sourceParts, parts);
j += parts.length - 3;
}
results = results[1];
}
// handle parts that result from target splitting
if(results.length > 1) {
// splice in new target parts
results.unshift(k, 1); // add args for splice below
Array.prototype.splice.apply(targetParts, results);
k += results.length - 3;
}
}
}
}
}
}
if(targetParts && targetParts.length > 1) {
this.geomsToFeatures(targetFeature, targetParts);
this.events.triggerEvent("split", {
original: targetFeature,
features: targetParts
});
Array.prototype.push.apply(additions, targetParts);
removals.push(targetFeature);
targetSplit = true;
}
}
}
if(sourceParts && sourceParts.length > 1) {
this.geomsToFeatures(feature, sourceParts);
this.events.triggerEvent("split", {
original: feature,
features: sourceParts
});
Array.prototype.push.apply(additions, sourceParts);
removals.push(feature);
sourceSplit = true;
}
if(sourceSplit || targetSplit) {
// remove and add feature events are suppressed
// listen for split event on this control instead
if(this.deferDelete) {
// Set state instead of removing. Take care to avoid
// setting delete for features that have not yet been
// inserted - those should be destroyed immediately.
var feat, destroys = [];
for(var i=0, len=removals.length; i<len; ++i) {
feat = removals[i];
if(feat.state === OpenLayers.State.INSERT) {
destroys.push(feat);
} else {
feat.state = OpenLayers.State.DELETE;
this.layer.drawFeature(feat);
}
}
this.layer.destroyFeatures(destroys, {silent: true});
for(var i=0, len=additions.length; i<len; ++i) {
additions[i].state = OpenLayers.State.INSERT;
}
} else {
this.layer.destroyFeatures(removals, {silent: true});
}
this.layer.addFeatures(additions, {silent: true});
this.events.triggerEvent("aftersplit", {
source: feature,
features: additions
});
}
}
return sourceSplit;
},
/**
* Method: geomsToFeatures
* Create new features given a template feature and a list of geometries.
* The list of geometries is modified in place. The result will be
* a list of new features.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>} The feature to be cloned.
* geoms - {Array(<OpenLayers.Geometry>)} List of goemetries. This will
* become a list of new features.
*/
geomsToFeatures: function(feature, geoms) {
var clone = feature.clone();
delete clone.geometry;
var newFeature;
for(var i=0, len=geoms.length; i<len; ++i) {
// turn results list from geoms to features
newFeature = clone.clone();
newFeature.geometry = geoms[i];
newFeature.state = OpenLayers.State.INSERT;
geoms[i] = newFeature;
}
},
/**
* Method: destroy
* Clean up the control.
*/
destroy: function() {
if(this.active) {
this.deactivate(); // TODO: this should be handled by the super
}
OpenLayers.Control.prototype.destroy.call(this);
},
CLASS_NAME: "OpenLayers.Control.Split"
});

View File

@ -0,0 +1,182 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control/DragPan.js
* @requires OpenLayers/Control/PinchZoom.js
* @requires OpenLayers/Handler/Click.js
*/
/**
* Class: OpenLayers.Control.TouchNavigation
* The navigation control handles map browsing with touch events (dragging,
* double-tapping, tap with two fingers, and pinch zoom). Create a new
* control with the <OpenLayers.Control.TouchNavigation> constructor.
*
* If youre only targeting touch enabled devices with your mapping application,
* you can create a map with only a TouchNavigation control. The
* <OpenLayers.Control.Navigation> control is mobile ready by default, but
* you can generate a smaller build of the library by only including this
* touch navigation control if you aren't concerned about mouse interaction.
*
* Inherits:
* - <OpenLayers.Control>
*/
OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, {
/**
* Property: dragPan
* {<OpenLayers.Control.DragPan>}
*/
dragPan: null,
/**
* APIProperty: dragPanOptions
* {Object} Options passed to the DragPan control.
*/
dragPanOptions: null,
/**
* Property: pinchZoom
* {<OpenLayers.Control.PinchZoom>}
*/
pinchZoom: null,
/**
* APIProperty: pinchZoomOptions
* {Object} Options passed to the PinchZoom control.
*/
pinchZoomOptions: null,
/**
* APIProperty: clickHandlerOptions
* {Object} Options passed to the Click handler.
*/
clickHandlerOptions: null,
/**
* APIProperty: documentDrag
* {Boolean} Allow panning of the map by dragging outside map viewport.
* Default is false.
*/
documentDrag: false,
/**
* APIProperty: autoActivate
* {Boolean} Activate the control when it is added to a map. Default is
* true.
*/
autoActivate: true,
/**
* Constructor: OpenLayers.Control.TouchNavigation
* Create a new navigation control
*
* Parameters:
* options - {Object} An optional object whose properties will be set on
* the control
*/
initialize: function(options) {
this.handlers = {};
OpenLayers.Control.prototype.initialize.apply(this, arguments);
},
/**
* Method: destroy
* The destroy method is used to perform any clean up before the control
* is dereferenced. Typically this is where event listeners are removed
* to prevent memory leaks.
*/
destroy: function() {
this.deactivate();
if(this.dragPan) {
this.dragPan.destroy();
}
this.dragPan = null;
if (this.pinchZoom) {
this.pinchZoom.destroy();
delete this.pinchZoom;
}
OpenLayers.Control.prototype.destroy.apply(this,arguments);
},
/**
* Method: activate
*/
activate: function() {
if(OpenLayers.Control.prototype.activate.apply(this,arguments)) {
this.dragPan.activate();
this.handlers.click.activate();
this.pinchZoom.activate();
return true;
}
return false;
},
/**
* Method: deactivate
*/
deactivate: function() {
if(OpenLayers.Control.prototype.deactivate.apply(this,arguments)) {
this.dragPan.deactivate();
this.handlers.click.deactivate();
this.pinchZoom.deactivate();
return true;
}
return false;
},
/**
* Method: draw
*/
draw: function() {
var clickCallbacks = {
click: this.defaultClick,
dblclick: this.defaultDblClick
};
var clickOptions = OpenLayers.Util.extend({
"double": true,
stopDouble: true,
pixelTolerance: 2
}, this.clickHandlerOptions);
this.handlers.click = new OpenLayers.Handler.Click(
this, clickCallbacks, clickOptions
);
this.dragPan = new OpenLayers.Control.DragPan(
OpenLayers.Util.extend({
map: this.map,
documentDrag: this.documentDrag
}, this.dragPanOptions)
);
this.dragPan.draw();
this.pinchZoom = new OpenLayers.Control.PinchZoom(
OpenLayers.Util.extend({map: this.map}, this.pinchZoomOptions)
);
},
/**
* Method: defaultClick
*
* Parameters:
* evt - {Event}
*/
defaultClick: function (evt) {
if(evt.lastTouches && evt.lastTouches.length == 2) {
this.map.zoomOut();
}
},
/**
* Method: defaultDblClick
*
* Parameters:
* evt - {Event}
*/
defaultDblClick: function (evt) {
this.map.zoomTo(this.map.zoom + 1, evt.xy);
},
CLASS_NAME: "OpenLayers.Control.TouchNavigation"
});

View File

@ -0,0 +1,624 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Control/DragFeature.js
* @requires OpenLayers/Feature/Vector.js
* @requires OpenLayers/Geometry/LineString.js
* @requires OpenLayers/Geometry/Point.js
*/
/**
* Class: OpenLayers.Control.TransformFeature
* Control to transform features with a standard transformation box.
*
* Inherits From:
* - <OpenLayers.Control>
*/
OpenLayers.Control.TransformFeature = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: events
* {<OpenLayers.Events>} Events instance for listeners and triggering
* control specific events.
*
* Register a listener for a particular event with the following syntax:
* (code)
* control.events.register(type, obj, listener);
* (end)
*
* Supported event types (in addition to those from <OpenLayers.Control.events>):
* beforesetfeature - Triggered before a feature is set for
* tranformation. The feature will not be set if a listener returns
* false. Listeners receive a *feature* property, with the feature
* that will be set for transformation. Listeners are allowed to
* set the control's *scale*, *ratio* and *rotation* properties,
* which will set the initial scale, ratio and rotation of the
* feature, like the <setFeature> method's initialParams argument.
* setfeature - Triggered when a feature is set for tranformation.
* Listeners receive a *feature* property, with the feature that
* is now set for transformation.
* beforetransform - Triggered while dragging, before a feature is
* transformed. The feature will not be transformed if a listener
* returns false (but the box still will). Listeners receive one or
* more of *center*, *scale*, *ratio* and *rotation*. The *center*
* property is an <OpenLayers.Geometry.Point> object with the new
* center of the transformed feature, the others are Floats with the
* scale, ratio or rotation change since the last transformation.
* transform - Triggered while dragging, when a feature is transformed.
* Listeners receive an event object with one or more of *center*,
* scale*, *ratio* and *rotation*. The *center* property is an
* <OpenLayers.Geometry.Point> object with the new center of the
* transformed feature, the others are Floats with the scale, ratio
* or rotation change of the feature since the last transformation.
* transformcomplete - Triggered after dragging. Listeners receive
* an event object with the transformed *feature*.
*/
/**
* APIProperty: geometryTypes
* {Array(String)} To restrict transformation to a limited set of geometry
* types, send a list of strings corresponding to the geometry class
* names.
*/
geometryTypes: null,
/**
* Property: layer
* {<OpenLayers.Layer.Vector>}
*/
layer: null,
/**
* APIProperty: preserveAspectRatio
* {Boolean} set to true to not change the feature's aspect ratio.
*/
preserveAspectRatio: false,
/**
* APIProperty: rotate
* {Boolean} set to false if rotation should be disabled. Default is true.
* To be passed with the constructor or set when the control is not
* active.
*/
rotate: true,
/**
* APIProperty: feature
* {<OpenLayers.Feature.Vector>} Feature currently available for
* transformation. Read-only, use <setFeature> to set it manually.
*/
feature: null,
/**
* APIProperty: renderIntent
* {String|Object} Render intent for the transformation box and
* handles. A symbolizer object can also be provided here.
*/
renderIntent: "temporary",
/**
* APIProperty: rotationHandleSymbolizer
* {Object|String} Optional. A custom symbolizer for the rotation handles.
* A render intent can also be provided here. Defaults to
* (code)
* {
* stroke: false,
* pointRadius: 10,
* fillOpacity: 0,
* cursor: "pointer"
* }
* (end)
*/
rotationHandleSymbolizer: null,
/**
* APIProperty: box
* {<OpenLayers.Feature.Vector>} The transformation box rectangle.
* Read-only.
*/
box: null,
/**
* APIProperty: center
* {<OpenLayers.Geometry.Point>} The center of the feature bounds.
* Read-only.
*/
center: null,
/**
* APIProperty: scale
* {Float} The scale of the feature, relative to the scale the time the
* feature was set. Read-only, except for *beforesetfeature*
* listeners.
*/
scale: 1,
/**
* APIProperty: ratio
* {Float} The ratio of the feature relative to the ratio the time the
* feature was set. Read-only, except for *beforesetfeature*
* listeners.
*/
ratio: 1,
/**
* Property: rotation
* {Integer} the current rotation angle of the box. Read-only, except for
* *beforesetfeature* listeners.
*/
rotation: 0,
/**
* APIProperty: handles
* {Array(<OpenLayers.Feature.Vector>)} The 8 handles currently available
* for scaling/resizing. Numbered counterclockwise, starting from the
* southwest corner. Read-only.
*/
handles: null,
/**
* APIProperty: rotationHandles
* {Array(<OpenLayers.Feature.Vector>)} The 4 rotation handles currently
* available for rotating. Numbered counterclockwise, starting from
* the southwest corner. Read-only.
*/
rotationHandles: null,
/**
* Property: dragControl
* {<OpenLayers.Control.DragFeature>}
*/
dragControl: null,
/**
* APIProperty: irregular
* {Boolean} Make scaling/resizing work irregularly. If true then
* dragging a handle causes the feature to resize in the direction
* of movement. If false then the feature resizes symetrically
* about it's center.
*/
irregular: false,
/**
* Constructor: OpenLayers.Control.TransformFeature
* Create a new transform feature control.
*
* Parameters:
* layer - {<OpenLayers.Layer.Vector>} Layer that contains features that
* will be transformed.
* options - {Object} Optional object whose properties will be set on the
* control.
*/
initialize: function(layer, options) {
OpenLayers.Control.prototype.initialize.apply(this, [options]);
this.layer = layer;
if(!this.rotationHandleSymbolizer) {
this.rotationHandleSymbolizer = {
stroke: false,
pointRadius: 10,
fillOpacity: 0,
cursor: "pointer"
};
}
this.createBox();
this.createControl();
},
/**
* APIMethod: activate
* Activates the control.
*/
activate: function() {
var activated = false;
if(OpenLayers.Control.prototype.activate.apply(this, arguments)) {
this.dragControl.activate();
this.layer.addFeatures([this.box]);
this.rotate && this.layer.addFeatures(this.rotationHandles);
this.layer.addFeatures(this.handles);
activated = true;
}
return activated;
},
/**
* APIMethod: deactivate
* Deactivates the control.
*/
deactivate: function() {
var deactivated = false;
if(OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
this.layer.removeFeatures(this.handles);
this.rotate && this.layer.removeFeatures(this.rotationHandles);
this.layer.removeFeatures([this.box]);
this.dragControl.deactivate();
deactivated = true;
}
return deactivated;
},
/**
* Method: setMap
*
* Parameters:
* map - {<OpenLayers.Map>}
*/
setMap: function(map) {
this.dragControl.setMap(map);
OpenLayers.Control.prototype.setMap.apply(this, arguments);
},
/**
* APIMethod: setFeature
* Place the transformation box on a feature and start transforming it.
* If the control is not active, it will be activated.
*
* Parameters:
* feature - {<OpenLayers.Feature.Vector>}
* initialParams - {Object} Initial values for rotation, scale or ratio.
* Setting a rotation value here will cause the transformation box to
* start rotated. Setting a scale or ratio will not affect the
* transormation box, but applications may use this to keep track of
* scale and ratio of a feature across multiple transforms.
*/
setFeature: function(feature, initialParams) {
initialParams = OpenLayers.Util.applyDefaults(initialParams, {
rotation: 0,
scale: 1,
ratio: 1
});
var oldRotation = this.rotation;
var oldCenter = this.center;
OpenLayers.Util.extend(this, initialParams);
var cont = this.events.triggerEvent("beforesetfeature",
{feature: feature}
);
if (cont === false) {
return;
}
this.feature = feature;
this.activate();
this._setfeature = true;
var featureBounds = this.feature.geometry.getBounds();
this.box.move(featureBounds.getCenterLonLat());
this.box.geometry.rotate(-oldRotation, oldCenter);
this._angle = 0;
var ll;
if(this.rotation) {
var geom = feature.geometry.clone();
geom.rotate(-this.rotation, this.center);
var box = new OpenLayers.Feature.Vector(
geom.getBounds().toGeometry());
box.geometry.rotate(this.rotation, this.center);
this.box.geometry.rotate(this.rotation, this.center);
this.box.move(box.geometry.getBounds().getCenterLonLat());
var llGeom = box.geometry.components[0].components[0];
ll = llGeom.getBounds().getCenterLonLat();
} else {
ll = new OpenLayers.LonLat(featureBounds.left, featureBounds.bottom);
}
this.handles[0].move(ll);
delete this._setfeature;
this.events.triggerEvent("setfeature", {feature: feature});
},
/**
* APIMethod: unsetFeature
* Remove the transformation box off any feature.
* If the control is active, it will be deactivated first.
*/
unsetFeature: function() {
if (this.active) {
this.deactivate();
} else {
this.feature = null;
this.rotation = 0;
this.scale = 1;
this.ratio = 1;
}
},
/**
* Method: createBox
* Creates the box with all handles and transformation handles.
*/
createBox: function() {
var control = this;
this.center = new OpenLayers.Geometry.Point(0, 0);
this.box = new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.LineString([
new OpenLayers.Geometry.Point(-1, -1),
new OpenLayers.Geometry.Point(0, -1),
new OpenLayers.Geometry.Point(1, -1),
new OpenLayers.Geometry.Point(1, 0),
new OpenLayers.Geometry.Point(1, 1),
new OpenLayers.Geometry.Point(0, 1),
new OpenLayers.Geometry.Point(-1, 1),
new OpenLayers.Geometry.Point(-1, 0),
new OpenLayers.Geometry.Point(-1, -1)
]), null,
typeof this.renderIntent == "string" ? null : this.renderIntent
);
// Override for box move - make sure that the center gets updated
this.box.geometry.move = function(x, y) {
control._moving = true;
OpenLayers.Geometry.LineString.prototype.move.apply(this, arguments);
control.center.move(x, y);
delete control._moving;
};
// Overrides for vertex move, resize and rotate - make sure that
// handle and rotationHandle geometries are also moved, resized and
// rotated.
var vertexMoveFn = function(x, y) {
OpenLayers.Geometry.Point.prototype.move.apply(this, arguments);
this._rotationHandle && this._rotationHandle.geometry.move(x, y);
this._handle.geometry.move(x, y);
};
var vertexResizeFn = function(scale, center, ratio) {
OpenLayers.Geometry.Point.prototype.resize.apply(this, arguments);
this._rotationHandle && this._rotationHandle.geometry.resize(
scale, center, ratio);
this._handle.geometry.resize(scale, center, ratio);
};
var vertexRotateFn = function(angle, center) {
OpenLayers.Geometry.Point.prototype.rotate.apply(this, arguments);
this._rotationHandle && this._rotationHandle.geometry.rotate(
angle, center);
this._handle.geometry.rotate(angle, center);
};
// Override for handle move - make sure that the box and other handles
// are updated, and finally transform the feature.
var handleMoveFn = function(x, y) {
var oldX = this.x, oldY = this.y;
OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
if(control._moving) {
return;
}
var evt = control.dragControl.handlers.drag.evt;
var preserveAspectRatio = !control._setfeature &&
control.preserveAspectRatio;
var reshape = !preserveAspectRatio && !(evt && evt.shiftKey);
var oldGeom = new OpenLayers.Geometry.Point(oldX, oldY);
var centerGeometry = control.center;
this.rotate(-control.rotation, centerGeometry);
oldGeom.rotate(-control.rotation, centerGeometry);
var dx1 = this.x - centerGeometry.x;
var dy1 = this.y - centerGeometry.y;
var dx0 = dx1 - (this.x - oldGeom.x);
var dy0 = dy1 - (this.y - oldGeom.y);
if (control.irregular && !control._setfeature) {
dx1 -= (this.x - oldGeom.x) / 2;
dy1 -= (this.y - oldGeom.y) / 2;
}
this.x = oldX;
this.y = oldY;
var scale, ratio = 1;
if (reshape) {
scale = Math.abs(dy0) < 0.00001 ? 1 : dy1 / dy0;
ratio = (Math.abs(dx0) < 0.00001 ? 1 : (dx1 / dx0)) / scale;
} else {
var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0));
var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1));
scale = l1 / l0;
}
// rotate the box to 0 before resizing - saves us some
// calculations and is inexpensive because we don't drawFeature.
control._moving = true;
control.box.geometry.rotate(-control.rotation, centerGeometry);
delete control._moving;
control.box.geometry.resize(scale, centerGeometry, ratio);
control.box.geometry.rotate(control.rotation, centerGeometry);
control.transformFeature({scale: scale, ratio: ratio});
if (control.irregular && !control._setfeature) {
var newCenter = centerGeometry.clone();
newCenter.x += Math.abs(oldX - centerGeometry.x) < 0.00001 ? 0 : (this.x - oldX);
newCenter.y += Math.abs(oldY - centerGeometry.y) < 0.00001 ? 0 : (this.y - oldY);
control.box.geometry.move(this.x - oldX, this.y - oldY);
control.transformFeature({center: newCenter});
}
};
// Override for rotation handle move - make sure that the box and
// other handles are updated, and finally transform the feature.
var rotationHandleMoveFn = function(x, y){
var oldX = this.x, oldY = this.y;
OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
if(control._moving) {
return;
}
var evt = control.dragControl.handlers.drag.evt;
var constrain = (evt && evt.shiftKey) ? 45 : 1;
var centerGeometry = control.center;
var dx1 = this.x - centerGeometry.x;
var dy1 = this.y - centerGeometry.y;
var dx0 = dx1 - x;
var dy0 = dy1 - y;
this.x = oldX;
this.y = oldY;
var a0 = Math.atan2(dy0, dx0);
var a1 = Math.atan2(dy1, dx1);
var angle = a1 - a0;
angle *= 180 / Math.PI;
control._angle = (control._angle + angle) % 360;
var diff = control.rotation % constrain;
if(Math.abs(control._angle) >= constrain || diff !== 0) {
angle = Math.round(control._angle / constrain) * constrain -
diff;
control._angle = 0;
control.box.geometry.rotate(angle, centerGeometry);
control.transformFeature({rotation: angle});
}
};
var handles = new Array(8);
var rotationHandles = new Array(4);
var geom, handle, rotationHandle;
var positions = ["sw", "s", "se", "e", "ne", "n", "nw", "w"];
for(var i=0; i<8; ++i) {
geom = this.box.geometry.components[i];
handle = new OpenLayers.Feature.Vector(geom.clone(), {
role: positions[i] + "-resize"
}, typeof this.renderIntent == "string" ? null :
this.renderIntent);
if(i % 2 == 0) {
rotationHandle = new OpenLayers.Feature.Vector(geom.clone(), {
role: positions[i] + "-rotate"
}, typeof this.rotationHandleSymbolizer == "string" ?
null : this.rotationHandleSymbolizer);
rotationHandle.geometry.move = rotationHandleMoveFn;
geom._rotationHandle = rotationHandle;
rotationHandles[i/2] = rotationHandle;
}
geom.move = vertexMoveFn;
geom.resize = vertexResizeFn;
geom.rotate = vertexRotateFn;
handle.geometry.move = handleMoveFn;
geom._handle = handle;
handles[i] = handle;
}
this.rotationHandles = rotationHandles;
this.handles = handles;
},
/**
* Method: createControl
* Creates a DragFeature control for this control.
*/
createControl: function() {
var control = this;
this.dragControl = new OpenLayers.Control.DragFeature(this.layer, {
documentDrag: true,
// avoid moving the feature itself - move the box instead
moveFeature: function(pixel) {
if(this.feature === control.feature) {
this.feature = control.box;
}
OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this,
arguments);
},
// transform while dragging
onDrag: function(feature, pixel) {
if(feature === control.box) {
control.transformFeature({center: control.center});
}
},
// set a new feature
onStart: function(feature, pixel) {
var eligible = !control.geometryTypes ||
OpenLayers.Util.indexOf(control.geometryTypes,
feature.geometry.CLASS_NAME) !== -1;
var i = OpenLayers.Util.indexOf(control.handles, feature);
i += OpenLayers.Util.indexOf(control.rotationHandles,
feature);
if(feature !== control.feature && feature !== control.box &&
i == -2 && eligible) {
control.setFeature(feature);
}
},
onComplete: function(feature, pixel) {
control.events.triggerEvent("transformcomplete",
{feature: control.feature});
}
});
},
/**
* Method: drawHandles
* Draws the handles to match the box.
*/
drawHandles: function() {
var layer = this.layer;
for(var i=0; i<8; ++i) {
if(this.rotate && i % 2 === 0) {
layer.drawFeature(this.rotationHandles[i/2],
this.rotationHandleSymbolizer);
}
layer.drawFeature(this.handles[i], this.renderIntent);
}
},
/**
* Method: transformFeature
* Transforms the feature.
*
* Parameters:
* mods - {Object} An object with optional scale, ratio, rotation and
* center properties.
*/
transformFeature: function(mods) {
if(!this._setfeature) {
this.scale *= (mods.scale || 1);
this.ratio *= (mods.ratio || 1);
var oldRotation = this.rotation;
this.rotation = (this.rotation + (mods.rotation || 0)) % 360;
if(this.events.triggerEvent("beforetransform", mods) !== false) {
var feature = this.feature;
var geom = feature.geometry;
var center = this.center;
geom.rotate(-oldRotation, center);
if(mods.scale || mods.ratio) {
geom.resize(mods.scale, center, mods.ratio);
} else if(mods.center) {
feature.move(mods.center.getBounds().getCenterLonLat());
}
geom.rotate(this.rotation, center);
this.layer.drawFeature(feature);
feature.toState(OpenLayers.State.UPDATE);
this.events.triggerEvent("transform", mods);
}
}
this.layer.drawFeature(this.box, this.renderIntent);
this.drawHandles();
},
/**
* APIMethod: destroy
* Take care of things that are not handled in superclass.
*/
destroy: function() {
var geom;
for(var i=0; i<8; ++i) {
geom = this.box.geometry.components[i];
geom._handle.destroy();
geom._handle = null;
geom._rotationHandle && geom._rotationHandle.destroy();
geom._rotationHandle = null;
}
this.center = null;
this.feature = null;
this.handles = null;
this.rotationHandleSymbolizer = null;
this.rotationHandles = null;
this.box.destroy();
this.box = null;
this.layer = null;
this.dragControl.destroy();
this.dragControl = null;
OpenLayers.Control.prototype.destroy.apply(this, arguments);
},
CLASS_NAME: "OpenLayers.Control.TransformFeature"
});

View File

@ -0,0 +1,240 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Handler/Hover.js
* @requires OpenLayers/Handler/Click.js
*/
/**
* Class: OpenLayers.Control.UTFGrid
*
* This Control provides behavior associated with UTFGrid Layers.
* These 'hit grids' provide underlying feature attributes without
* calling the server (again). This control allows Mousemove, Hovering
* and Click events to trigger callbacks that use the attributes in
* whatever way you need.
*
* The most common example may be a UTFGrid layer containing feature
* attributes that are displayed in a div as you mouseover.
*
* Example Code:
*
* (start code)
* var world_utfgrid = new OpenLayers.Layer.UTFGrid(
* 'UTFGrid Layer',
* "http://tiles/world_utfgrid/${z}/${x}/${y}.json"
* );
* map.addLayer(world_utfgrid);
*
* var control = new OpenLayers.Control.UTFGrid({
* layers: [world_utfgrid],
* handlerMode: 'move',
* callback: function(infoLookup) {
* // do something with returned data
*
* }
* })
* (end code)
*
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.UTFGrid = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: autoActivate
* {Boolean} Activate the control when it is added to a map. Default is
* true.
*/
autoActivate: true,
/**
* APIProperty: Layers
* List of layers to consider. Must be Layer.UTFGrids
* `null` is the default indicating all UTFGrid Layers are queried.
* {Array} <OpenLayers.Layer.UTFGrid>
*/
layers: null,
/* Property: defaultHandlerOptions
* The default opts passed to the handler constructors
*/
defaultHandlerOptions: {
'delay': 300,
'pixelTolerance': 4,
'stopMove': false,
'single': true,
'double': false,
'stopSingle': false,
'stopDouble': false
},
/* APIProperty: handlerMode
* Defaults to 'click'. Can be 'hover' or 'move'.
*/
handlerMode: 'click',
/**
* APIMethod: setHandler
* sets this.handlerMode and calls resetHandler()
*
* Parameters:
* hm - {String} Handler Mode string; 'click', 'hover' or 'move'.
*/
setHandler: function(hm) {
this.handlerMode = hm;
this.resetHandler();
},
/**
* Method: resetHandler
* Deactivates the old hanlder and creates a new
* <OpenLayers.Handler> based on the mode specified in
* this.handlerMode
*
*/
resetHandler: function() {
if (this.handler) {
this.handler.deactivate();
this.handler.destroy();
this.handler = null;
}
if (this.handlerMode == 'hover') {
// Handle this event on hover
this.handler = new OpenLayers.Handler.Hover(
this,
{'pause': this.handleEvent, 'move': this.reset},
this.handlerOptions
);
} else if (this.handlerMode == 'click') {
// Handle this event on click
this.handler = new OpenLayers.Handler.Click(
this, {
'click': this.handleEvent
}, this.handlerOptions
);
} else if (this.handlerMode == 'move') {
this.handler = new OpenLayers.Handler.Hover(
this,
// Handle this event while hovering OR moving
{'pause': this.handleEvent, 'move': this.handleEvent},
this.handlerOptions
);
}
if (this.handler) {
return true;
} else {
return false;
}
},
/**
* Constructor: <OpenLayers.Control.UTFGrid>
*
* Parameters:
* options - {Object}
*/
initialize: function(options) {
options = options || {};
options.handlerOptions = options.handlerOptions || this.defaultHandlerOptions;
OpenLayers.Control.prototype.initialize.apply(this, [options]);
this.resetHandler();
},
/**
* Method: handleEvent
* Internal method called when specified event is triggered.
*
* This method does several things:
*
* Gets the lonLat of the event.
*
* Loops through the appropriate hit grid layers and gathers the attributes.
*
* Passes the attributes to the callback
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
handleEvent: function(evt) {
if (evt == null) {
this.reset();
return;
}
var lonLat = this.map.getLonLatFromPixel(evt.xy);
if (!lonLat) {
return;
}
var layers = this.findLayers();
if (layers.length > 0) {
var infoLookup = {};
var layer, idx;
for (var i=0, len=layers.length; i<len; i++) {
layer = layers[i];
idx = OpenLayers.Util.indexOf(this.map.layers, layer);
infoLookup[idx] = layer.getFeatureInfo(lonLat);
}
this.callback(infoLookup, lonLat, evt.xy);
}
},
/**
* APIMethod: callback
* Function to be called when a mouse event corresponds with a location that
* includes data in one of the configured UTFGrid layers.
*
* Parameters:
* infoLookup - {Object} Keys of this object are layer indexes and can be
* used to resolve a layer in the map.layers array. The structure of
* the property values depend on the data included in the underlying
* UTFGrid and may be any valid JSON type.
*/
callback: function(infoLookup) {
// to be provided in the constructor
},
/**
* Method: reset
* Calls the callback with null.
*/
reset: function(evt) {
this.callback(null);
},
/**
* Method: findLayers
* Internal method to get the layers, independent of whether we are
* inspecting the map or using a client-provided array
*
* The default value of this.layers is null; this causes the
* findLayers method to return ALL UTFGrid layers encountered.
*
* Parameters:
* None
*
* Returns:
* {Array} Layers to handle on each event
*/
findLayers: function() {
var candidates = this.layers || this.map.layers;
var layers = [];
var layer;
for (var i=candidates.length-1; i>=0; --i) {
layer = candidates[i];
if (layer instanceof OpenLayers.Layer.UTFGrid ) {
layers.push(layer);
}
}
return layers;
},
CLASS_NAME: "OpenLayers.Control.UTFGrid"
});

View File

@ -0,0 +1,532 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Handler/Click.js
* @requires OpenLayers/Handler/Hover.js
* @requires OpenLayers/Request.js
* @requires OpenLayers/Format/WMSGetFeatureInfo.js
*/
/**
* Class: OpenLayers.Control.WMSGetFeatureInfo
* The WMSGetFeatureInfo control uses a WMS query to get information about a point on the map. The
* information may be in a display-friendly format such as HTML, or a machine-friendly format such
* as GML, depending on the server's capabilities and the client's configuration. This control
* handles click or hover events, attempts to parse the results using an OpenLayers.Format, and
* fires a 'getfeatureinfo' event with the click position, the raw body of the response, and an
* array of features if it successfully read the response.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: hover
* {Boolean} Send GetFeatureInfo requests when mouse stops moving.
* Default is false.
*/
hover: false,
/**
* APIProperty: drillDown
* {Boolean} Drill down over all WMS layers in the map. When
* using drillDown mode, hover is not possible, and an infoFormat that
* returns parseable features is required. Default is false.
*/
drillDown: false,
/**
* APIProperty: maxFeatures
* {Integer} Maximum number of features to return from a WMS query. This
* sets the feature_count parameter on WMS GetFeatureInfo
* requests.
*/
maxFeatures: 10,
/**
* APIProperty: clickCallback
* {String} The click callback to register in the
* {<OpenLayers.Handler.Click>} object created when the hover
* option is set to false. Default is "click".
*/
clickCallback: "click",
/**
* APIProperty: output
* {String} Either "features" or "object". When triggering a getfeatureinfo
* request should we pass on an array of features or an object with with
* a "features" property and other properties (such as the url of the
* WMS). Default is "features".
*/
output: "features",
/**
* APIProperty: layers
* {Array(<OpenLayers.Layer.WMS>)} The layers to query for feature info.
* If omitted, all map WMS layers with a url that matches this <url> or
* <layerUrls> will be considered.
*/
layers: null,
/**
* APIProperty: queryVisible
* {Boolean} If true, filter out hidden layers when searching the map for
* layers to query. Default is false.
*/
queryVisible: false,
/**
* APIProperty: url
* {String} The URL of the WMS service to use. If not provided, the url
* of the first eligible layer will be used.
*/
url: null,
/**
* APIProperty: layerUrls
* {Array(String)} Optional list of urls for layers that should be queried.
* This can be used when the layer url differs from the url used for
* making GetFeatureInfo requests (in the case of a layer using cached
* tiles).
*/
layerUrls: null,
/**
* APIProperty: infoFormat
* {String} The mimetype to request from the server. If you are using
* drillDown mode and have multiple servers that do not share a common
* infoFormat, you can override the control's infoFormat by providing an
* INFO_FORMAT parameter in your <OpenLayers.Layer.WMS> instance(s).
*/
infoFormat: 'text/html',
/**
* APIProperty: vendorParams
* {Object} Additional parameters that will be added to the request, for
* WMS implementations that support them. This could e.g. look like
* (start code)
* {
* radius: 5
* }
* (end)
*/
vendorParams: {},
/**
* APIProperty: format
* {<OpenLayers.Format>} A format for parsing GetFeatureInfo responses.
* Default is <OpenLayers.Format.WMSGetFeatureInfo>.
*/
format: null,
/**
* APIProperty: formatOptions
* {Object} Optional properties to set on the format (if one is not provided
* in the <format> property.
*/
formatOptions: null,
/**
* APIProperty: handlerOptions
* {Object} Additional options for the handlers used by this control, e.g.
* (start code)
* {
* "click": {delay: 100},
* "hover": {delay: 300}
* }
* (end)
*/
/**
* Property: handler
* {Object} Reference to the <OpenLayers.Handler> for this control
*/
handler: null,
/**
* Property: hoverRequest
* {<OpenLayers.Request>} contains the currently running hover request
* (if any).
*/
hoverRequest: null,
/**
* APIProperty: events
* {<OpenLayers.Events>} Events instance for listeners and triggering
* control specific events.
*
* Register a listener for a particular event with the following syntax:
* (code)
* control.events.register(type, obj, listener);
* (end)
*
* Supported event types (in addition to those from <OpenLayers.Control.events>):
* beforegetfeatureinfo - Triggered before the request is sent.
* The event object has an *xy* property with the position of the
* mouse click or hover event that triggers the request.
* nogetfeatureinfo - no queryable layers were found.
* getfeatureinfo - Triggered when a GetFeatureInfo response is received.
* The event object has a *text* property with the body of the
* response (String), a *features* property with an array of the
* parsed features, an *xy* property with the position of the mouse
* click or hover event that triggered the request, and a *request*
* property with the request itself. If drillDown is set to true and
* multiple requests were issued to collect feature info from all
* layers, *text* and *request* will only contain the response body
* and request object of the last request.
*/
/**
* Constructor: <OpenLayers.Control.WMSGetFeatureInfo>
*
* Parameters:
* options - {Object}
*/
initialize: function(options) {
options = options || {};
options.handlerOptions = options.handlerOptions || {};
OpenLayers.Control.prototype.initialize.apply(this, [options]);
if(!this.format) {
this.format = new OpenLayers.Format.WMSGetFeatureInfo(
options.formatOptions
);
}
if(this.drillDown === true) {
this.hover = false;
}
if(this.hover) {
this.handler = new OpenLayers.Handler.Hover(
this, {
'move': this.cancelHover,
'pause': this.getInfoForHover
},
OpenLayers.Util.extend(this.handlerOptions.hover || {}, {
'delay': 250
}));
} else {
var callbacks = {};
callbacks[this.clickCallback] = this.getInfoForClick;
this.handler = new OpenLayers.Handler.Click(
this, callbacks, this.handlerOptions.click || {});
}
},
/**
* Method: getInfoForClick
* Called on click
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
getInfoForClick: function(evt) {
this.events.triggerEvent("beforegetfeatureinfo", {xy: evt.xy});
// Set the cursor to "wait" to tell the user we're working on their
// click.
OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait");
this.request(evt.xy, {});
},
/**
* Method: getInfoForHover
* Pause callback for the hover handler
*
* Parameters:
* evt - {Object}
*/
getInfoForHover: function(evt) {
this.events.triggerEvent("beforegetfeatureinfo", {xy: evt.xy});
this.request(evt.xy, {hover: true});
},
/**
* Method: cancelHover
* Cancel callback for the hover handler
*/
cancelHover: function() {
if (this.hoverRequest) {
this.hoverRequest.abort();
this.hoverRequest = null;
}
},
/**
* Method: findLayers
* Internal method to get the layers, independent of whether we are
* inspecting the map or using a client-provided array
*/
findLayers: function() {
var candidates = this.layers || this.map.layers;
var layers = [];
var layer, url;
for(var i = candidates.length - 1; i >= 0; --i) {
layer = candidates[i];
if(layer instanceof OpenLayers.Layer.WMS &&
(!this.queryVisible || layer.getVisibility())) {
url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url;
// if the control was not configured with a url, set it
// to the first layer url
if(this.drillDown === false && !this.url) {
this.url = url;
}
if(this.drillDown === true || this.urlMatches(url)) {
layers.push(layer);
}
}
}
return layers;
},
/**
* Method: urlMatches
* Test to see if the provided url matches either the control <url> or one
* of the <layerUrls>.
*
* Parameters:
* url - {String} The url to test.
*
* Returns:
* {Boolean} The provided url matches the control <url> or one of the
* <layerUrls>.
*/
urlMatches: function(url) {
var matches = OpenLayers.Util.isEquivalentUrl(this.url, url);
if(!matches && this.layerUrls) {
for(var i=0, len=this.layerUrls.length; i<len; ++i) {
if(OpenLayers.Util.isEquivalentUrl(this.layerUrls[i], url)) {
matches = true;
break;
}
}
}
return matches;
},
/**
* Method: buildWMSOptions
* Build an object with the relevant WMS options for the GetFeatureInfo request
*
* Parameters:
* url - {String} The url to be used for sending the request
* layers - {Array(<OpenLayers.Layer.WMS)} An array of layers
* clickPosition - {<OpenLayers.Pixel>} The position on the map where the mouse
* event occurred.
* format - {String} The format from the corresponding GetMap request
*/
buildWMSOptions: function(url, layers, clickPosition, format) {
var layerNames = [], styleNames = [];
for (var i = 0, len = layers.length; i < len; i++) {
if (layers[i].params.LAYERS != null) {
layerNames = layerNames.concat(layers[i].params.LAYERS);
styleNames = styleNames.concat(this.getStyleNames(layers[i]));
}
}
var firstLayer = layers[0];
// use the firstLayer's projection if it matches the map projection -
// this assumes that all layers will be available in this projection
var projection = this.map.getProjection();
var layerProj = firstLayer.projection;
if (layerProj && layerProj.equals(this.map.getProjectionObject())) {
projection = layerProj.getCode();
}
var params = OpenLayers.Util.extend({
service: "WMS",
version: firstLayer.params.VERSION,
request: "GetFeatureInfo",
exceptions: firstLayer.params.EXCEPTIONS,
bbox: this.map.getExtent().toBBOX(null,
firstLayer.reverseAxisOrder()),
feature_count: this.maxFeatures,
height: this.map.getSize().h,
width: this.map.getSize().w,
format: format,
info_format: firstLayer.params.INFO_FORMAT || this.infoFormat
}, (parseFloat(firstLayer.params.VERSION) >= 1.3) ?
{
crs: projection,
i: parseInt(clickPosition.x),
j: parseInt(clickPosition.y)
} :
{
srs: projection,
x: parseInt(clickPosition.x),
y: parseInt(clickPosition.y)
}
);
if (layerNames.length != 0) {
params = OpenLayers.Util.extend({
layers: layerNames,
query_layers: layerNames,
styles: styleNames
}, params);
}
OpenLayers.Util.applyDefaults(params, this.vendorParams);
return {
url: url,
params: OpenLayers.Util.upperCaseObject(params),
callback: function(request) {
this.handleResponse(clickPosition, request, url);
},
scope: this
};
},
/**
* Method: getStyleNames
* Gets the STYLES parameter for the layer. Make sure the STYLES parameter
* matches the LAYERS parameter
*
* Parameters:
* layer - {<OpenLayers.Layer.WMS>}
*
* Returns:
* {Array(String)} The STYLES parameter
*/
getStyleNames: function(layer) {
// in the event of a WMS layer bundling multiple layers but not
// specifying styles,we need the same number of commas to specify
// the default style for each of the layers. We can't just leave it
// blank as we may be including other layers that do specify styles.
var styleNames;
if (layer.params.STYLES) {
styleNames = layer.params.STYLES;
} else {
if (OpenLayers.Util.isArray(layer.params.LAYERS)) {
styleNames = new Array(layer.params.LAYERS.length);
} else { // Assume it's a String
styleNames = layer.params.LAYERS.replace(/[^,]/g, "");
}
}
return styleNames;
},
/**
* Method: request
* Sends a GetFeatureInfo request to the WMS
*
* Parameters:
* clickPosition - {<OpenLayers.Pixel>} The position on the map where the
* mouse event occurred.
* options - {Object} additional options for this method.
*
* Valid options:
* - *hover* {Boolean} true if we do the request for the hover handler
*/
request: function(clickPosition, options) {
var layers = this.findLayers();
if(layers.length == 0) {
this.events.triggerEvent("nogetfeatureinfo");
// Reset the cursor.
OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
return;
}
options = options || {};
if(this.drillDown === false) {
var wmsOptions = this.buildWMSOptions(this.url, layers,
clickPosition, layers[0].params.FORMAT);
var request = OpenLayers.Request.GET(wmsOptions);
if (options.hover === true) {
this.hoverRequest = request;
}
} else {
this._requestCount = 0;
this._numRequests = 0;
this.features = [];
// group according to service url to combine requests
var services = {}, url;
for(var i=0, len=layers.length; i<len; i++) {
var layer = layers[i];
var service, found = false;
url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url;
if(url in services) {
services[url].push(layer);
} else {
this._numRequests++;
services[url] = [layer];
}
}
var layers;
for (var url in services) {
layers = services[url];
var wmsOptions = this.buildWMSOptions(url, layers,
clickPosition, layers[0].params.FORMAT);
OpenLayers.Request.GET(wmsOptions);
}
}
},
/**
* Method: triggerGetFeatureInfo
* Trigger the getfeatureinfo event when all is done
*
* Parameters:
* request - {XMLHttpRequest} The request object
* xy - {<OpenLayers.Pixel>} The position on the map where the
* mouse event occurred.
* features - {Array(<OpenLayers.Feature.Vector>)} or
* {Array({Object}) when output is "object". The object has a url and a
* features property which contains an array of features.
*/
triggerGetFeatureInfo: function(request, xy, features) {
this.events.triggerEvent("getfeatureinfo", {
text: request.responseText,
features: features,
request: request,
xy: xy
});
// Reset the cursor.
OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
},
/**
* Method: handleResponse
* Handler for the GetFeatureInfo response.
*
* Parameters:
* xy - {<OpenLayers.Pixel>} The position on the map where the
* mouse event occurred.
* request - {XMLHttpRequest} The request object.
* url - {String} The url which was used for this request.
*/
handleResponse: function(xy, request, url) {
var doc = request.responseXML;
if(!doc || !doc.documentElement) {
doc = request.responseText;
}
var features = this.format.read(doc);
if (this.drillDown === false) {
this.triggerGetFeatureInfo(request, xy, features);
} else {
this._requestCount++;
if (this.output === "object") {
this._features = (this._features || []).concat(
{url: url, features: features}
);
} else {
this._features = (this._features || []).concat(features);
}
if (this._requestCount === this._numRequests) {
this.triggerGetFeatureInfo(request, xy, this._features.concat());
delete this._features;
delete this._requestCount;
delete this._numRequests;
}
}
},
CLASS_NAME: "OpenLayers.Control.WMSGetFeatureInfo"
});

View File

@ -0,0 +1,400 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Handler/Click.js
* @requires OpenLayers/Handler/Hover.js
* @requires OpenLayers/Request.js
* @requires OpenLayers/Format/WMSGetFeatureInfo.js
*/
/**
* Class: OpenLayers.Control.WMTSGetFeatureInfo
* The WMTSGetFeatureInfo control uses a WMTS query to get information about a
* point on the map. The information may be in a display-friendly format
* such as HTML, or a machine-friendly format such as GML, depending on the
* server's capabilities and the client's configuration. This control
* handles click or hover events, attempts to parse the results using an
* OpenLayers.Format, and fires a 'getfeatureinfo' event for each layer
* queried.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.WMTSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: hover
* {Boolean} Send GetFeatureInfo requests when mouse stops moving.
* Default is false.
*/
hover: false,
/**
* Property: requestEncoding
* {String} One of "KVP" or "REST". Only KVP encoding is supported at this
* time.
*/
requestEncoding: "KVP",
/**
* APIProperty: drillDown
* {Boolean} Drill down over all WMTS layers in the map. When
* using drillDown mode, hover is not possible. A getfeatureinfo event
* will be fired for each layer queried.
*/
drillDown: false,
/**
* APIProperty: maxFeatures
* {Integer} Maximum number of features to return from a WMTS query. This
* sets the feature_count parameter on WMTS GetFeatureInfo
* requests.
*/
maxFeatures: 10,
/** APIProperty: clickCallback
* {String} The click callback to register in the
* {<OpenLayers.Handler.Click>} object created when the hover
* option is set to false. Default is "click".
*/
clickCallback: "click",
/**
* Property: layers
* {Array(<OpenLayers.Layer.WMTS>)} The layers to query for feature info.
* If omitted, all map WMTS layers will be considered.
*/
layers: null,
/**
* APIProperty: queryVisible
* {Boolean} Filter out hidden layers when searching the map for layers to
* query. Default is true.
*/
queryVisible: true,
/**
* Property: infoFormat
* {String} The mimetype to request from the server
*/
infoFormat: 'text/html',
/**
* Property: vendorParams
* {Object} Additional parameters that will be added to the request, for
* WMTS implementations that support them. This could e.g. look like
* (start code)
* {
* radius: 5
* }
* (end)
*/
vendorParams: {},
/**
* Property: format
* {<OpenLayers.Format>} A format for parsing GetFeatureInfo responses.
* Default is <OpenLayers.Format.WMSGetFeatureInfo>.
*/
format: null,
/**
* Property: formatOptions
* {Object} Optional properties to set on the format (if one is not provided
* in the <format> property.
*/
formatOptions: null,
/**
* APIProperty: handlerOptions
* {Object} Additional options for the handlers used by this control, e.g.
* (start code)
* {
* "click": {delay: 100},
* "hover": {delay: 300}
* }
* (end)
*/
/**
* Property: handler
* {Object} Reference to the <OpenLayers.Handler> for this control
*/
handler: null,
/**
* Property: hoverRequest
* {<OpenLayers.Request>} contains the currently running hover request
* (if any).
*/
hoverRequest: null,
/**
* APIProperty: events
* {<OpenLayers.Events>} Events instance for listeners and triggering
* control specific events.
*
* Register a listener for a particular event with the following syntax:
* (code)
* control.events.register(type, obj, listener);
* (end)
*
* Supported event types (in addition to those from <OpenLayers.Control.events>):
* beforegetfeatureinfo - Triggered before each request is sent.
* The event object has an *xy* property with the position of the
* mouse click or hover event that triggers the request and a *layer*
* property referencing the layer about to be queried. If a listener
* returns false, the request will not be issued.
* getfeatureinfo - Triggered when a GetFeatureInfo response is received.
* The event object has a *text* property with the body of the
* response (String), a *features* property with an array of the
* parsed features, an *xy* property with the position of the mouse
* click or hover event that triggered the request, a *layer* property
* referencing the layer queried and a *request* property with the
* request itself. If drillDown is set to true, one event will be fired
* for each layer queried.
* exception - Triggered when a GetFeatureInfo request fails (with a
* status other than 200) or whenparsing fails. Listeners will receive
* an event with *request*, *xy*, and *layer* properties. In the case
* of a parsing error, the event will also contain an *error* property.
*/
/**
* Property: pending
* {Number} The number of pending requests.
*/
pending: 0,
/**
* Constructor: <OpenLayers.Control.WMTSGetFeatureInfo>
*
* Parameters:
* options - {Object}
*/
initialize: function(options) {
options = options || {};
options.handlerOptions = options.handlerOptions || {};
OpenLayers.Control.prototype.initialize.apply(this, [options]);
if (!this.format) {
this.format = new OpenLayers.Format.WMSGetFeatureInfo(
options.formatOptions
);
}
if (this.drillDown === true) {
this.hover = false;
}
if (this.hover) {
this.handler = new OpenLayers.Handler.Hover(
this, {
move: this.cancelHover,
pause: this.getInfoForHover
},
OpenLayers.Util.extend(
this.handlerOptions.hover || {}, {delay: 250}
)
);
} else {
var callbacks = {};
callbacks[this.clickCallback] = this.getInfoForClick;
this.handler = new OpenLayers.Handler.Click(
this, callbacks, this.handlerOptions.click || {}
);
}
},
/**
* Method: getInfoForClick
* Called on click
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
getInfoForClick: function(evt) {
this.request(evt.xy, {});
},
/**
* Method: getInfoForHover
* Pause callback for the hover handler
*
* Parameters:
* evt - {Object}
*/
getInfoForHover: function(evt) {
this.request(evt.xy, {hover: true});
},
/**
* Method: cancelHover
* Cancel callback for the hover handler
*/
cancelHover: function() {
if (this.hoverRequest) {
--this.pending;
if (this.pending <= 0) {
OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
this.pending = 0;
}
this.hoverRequest.abort();
this.hoverRequest = null;
}
},
/**
* Method: findLayers
* Internal method to get the layers, independent of whether we are
* inspecting the map or using a client-provided array
*/
findLayers: function() {
var candidates = this.layers || this.map.layers;
var layers = [];
var layer;
for (var i=candidates.length-1; i>=0; --i) {
layer = candidates[i];
if (layer instanceof OpenLayers.Layer.WMTS &&
layer.requestEncoding === this.requestEncoding &&
(!this.queryVisible || layer.getVisibility())) {
layers.push(layer);
if (!this.drillDown || this.hover) {
break;
}
}
}
return layers;
},
/**
* Method: buildRequestOptions
* Build an object with the relevant options for the GetFeatureInfo request.
*
* Parameters:
* layer - {<OpenLayers.Layer.WMTS>} A WMTS layer.
* xy - {<OpenLayers.Pixel>} The position on the map where the
* mouse event occurred.
*/
buildRequestOptions: function(layer, xy) {
var loc = this.map.getLonLatFromPixel(xy);
var getTileUrl = layer.getURL(
new OpenLayers.Bounds(loc.lon, loc.lat, loc.lon, loc.lat)
);
var params = OpenLayers.Util.getParameters(getTileUrl);
var tileInfo = layer.getTileInfo(loc);
OpenLayers.Util.extend(params, {
service: "WMTS",
version: layer.version,
request: "GetFeatureInfo",
infoFormat: this.infoFormat,
i: tileInfo.i,
j: tileInfo.j
});
OpenLayers.Util.applyDefaults(params, this.vendorParams);
return {
url: OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url,
params: OpenLayers.Util.upperCaseObject(params),
callback: function(request) {
this.handleResponse(xy, request, layer);
},
scope: this
};
},
/**
* Method: request
* Sends a GetFeatureInfo request to the WMTS
*
* Parameters:
* xy - {<OpenLayers.Pixel>} The position on the map where the mouse event
* occurred.
* options - {Object} additional options for this method.
*
* Valid options:
* - *hover* {Boolean} true if we do the request for the hover handler
*/
request: function(xy, options) {
options = options || {};
var layers = this.findLayers();
if (layers.length > 0) {
var issue, layer;
for (var i=0, len=layers.length; i<len; i++) {
layer = layers[i];
issue = this.events.triggerEvent("beforegetfeatureinfo", {
xy: xy,
layer: layer
});
if (issue !== false) {
++this.pending;
var requestOptions = this.buildRequestOptions(layer, xy);
var request = OpenLayers.Request.GET(requestOptions);
if (options.hover === true) {
this.hoverRequest = request;
}
}
}
if (this.pending > 0) {
OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait");
}
}
},
/**
* Method: handleResponse
* Handler for the GetFeatureInfo response.
*
* Parameters:
* xy - {<OpenLayers.Pixel>} The position on the map where the mouse event
* occurred.
* request - {XMLHttpRequest} The request object.
* layer - {<OpenLayers.Layer.WMTS>} The queried layer.
*/
handleResponse: function(xy, request, layer) {
--this.pending;
if (this.pending <= 0) {
OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
this.pending = 0;
}
if (request.status && (request.status < 200 || request.status >= 300)) {
this.events.triggerEvent("exception", {
xy: xy,
request: request,
layer: layer
});
} else {
var doc = request.responseXML;
if (!doc || !doc.documentElement) {
doc = request.responseText;
}
var features, except;
try {
features = this.format.read(doc);
} catch (error) {
except = true;
this.events.triggerEvent("exception", {
xy: xy,
request: request,
error: error,
layer: layer
});
}
if (!except) {
this.events.triggerEvent("getfeatureinfo", {
text: request.responseText,
features: features,
request: request,
xy: xy,
layer: layer
});
}
}
},
CLASS_NAME: "OpenLayers.Control.WMTSGetFeatureInfo"
});

View File

@ -0,0 +1,138 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Events/buttonclick.js
*/
/**
* Class: OpenLayers.Control.Zoom
* The Zoom control is a pair of +/- links for zooming in and out.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: zoomInText
* {String}
* Text for zoom-in link. Default is "+".
*/
zoomInText: "+",
/**
* APIProperty: zoomInId
* {String}
* Instead of having the control create a zoom in link, you can provide
* the identifier for an anchor element already added to the document.
* By default, an element with id "olZoomInLink" will be searched for
* and used if it exists.
*/
zoomInId: "olZoomInLink",
/**
* APIProperty: zoomOutText
* {String}
* Text for zoom-out link. Default is "\u2212".
*/
zoomOutText: "\u2212",
/**
* APIProperty: zoomOutId
* {String}
* Instead of having the control create a zoom out link, you can provide
* the identifier for an anchor element already added to the document.
* By default, an element with id "olZoomOutLink" will be searched for
* and used if it exists.
*/
zoomOutId: "olZoomOutLink",
/**
* Method: draw
*
* Returns:
* {DOMElement} A reference to the DOMElement containing the zoom links.
*/
draw: function() {
var div = OpenLayers.Control.prototype.draw.apply(this),
links = this.getOrCreateLinks(div),
zoomIn = links.zoomIn,
zoomOut = links.zoomOut,
eventsInstance = this.map.events;
if (zoomOut.parentNode !== div) {
eventsInstance = this.events;
eventsInstance.attachToElement(zoomOut.parentNode);
}
eventsInstance.register("buttonclick", this, this.onZoomClick);
this.zoomInLink = zoomIn;
this.zoomOutLink = zoomOut;
return div;
},
/**
* Method: getOrCreateLinks
*
* Parameters:
* el - {DOMElement}
*
* Return:
* {Object} Object with zoomIn and zoomOut properties referencing links.
*/
getOrCreateLinks: function(el) {
var zoomIn = document.getElementById(this.zoomInId),
zoomOut = document.getElementById(this.zoomOutId);
if (!zoomIn) {
zoomIn = document.createElement("a");
zoomIn.href = "#zoomIn";
zoomIn.appendChild(document.createTextNode(this.zoomInText));
zoomIn.className = "olControlZoomIn";
el.appendChild(zoomIn);
}
OpenLayers.Element.addClass(zoomIn, "olButton");
if (!zoomOut) {
zoomOut = document.createElement("a");
zoomOut.href = "#zoomOut";
zoomOut.appendChild(document.createTextNode(this.zoomOutText));
zoomOut.className = "olControlZoomOut";
el.appendChild(zoomOut);
}
OpenLayers.Element.addClass(zoomOut, "olButton");
return {
zoomIn: zoomIn, zoomOut: zoomOut
};
},
/**
* Method: onZoomClick
* Called when zoomin/out link is clicked.
*/
onZoomClick: function(evt) {
var button = evt.buttonElement;
if (button === this.zoomInLink) {
this.map.zoomIn();
} else if (button === this.zoomOutLink) {
this.map.zoomOut();
}
},
/**
* Method: destroy
* Clean up.
*/
destroy: function() {
if (this.map) {
this.map.events.unregister("buttonclick", this, this.onZoomClick);
}
delete this.zoomInLink;
delete this.zoomOutLink;
OpenLayers.Control.prototype.destroy.apply(this);
},
CLASS_NAME: "OpenLayers.Control.Zoom"
});

View File

@ -0,0 +1,129 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control.js
* @requires OpenLayers/Handler/Box.js
*/
/**
* Class: OpenLayers.Control.ZoomBox
* The ZoomBox control enables zooming directly to a given extent, by drawing
* a box on the map. The box is drawn by holding down shift, whilst dragging
* the mouse.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {
/**
* Property: type
* {OpenLayers.Control.TYPE}
*/
type: OpenLayers.Control.TYPE_TOOL,
/**
* Property: out
* {Boolean} Should the control be used for zooming out?
*/
out: false,
/**
* APIProperty: keyMask
* {Integer} Zoom only occurs if the keyMask matches the combination of
* keys down. Use bitwise operators and one or more of the
* <OpenLayers.Handler> constants to construct a keyMask. Leave null if
* not used mask. Default is null.
*/
keyMask: null,
/**
* APIProperty: alwaysZoom
* {Boolean} Always zoom in/out when box drawn, even if the zoom level does
* not change.
*/
alwaysZoom: false,
/**
* APIProperty: zoomOnClick
* {Boolean} Should we zoom when no box was dragged, i.e. the user only
* clicked? Default is true.
*/
zoomOnClick: true,
/**
* Method: draw
*/
draw: function() {
this.handler = new OpenLayers.Handler.Box( this,
{done: this.zoomBox}, {keyMask: this.keyMask} );
},
/**
* Method: zoomBox
*
* Parameters:
* position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>}
*/
zoomBox: function (position) {
if (position instanceof OpenLayers.Bounds) {
var bounds,
targetCenterPx = position.getCenterPixel();
if (!this.out) {
var minXY = this.map.getLonLatFromPixel({
x: position.left,
y: position.bottom
});
var maxXY = this.map.getLonLatFromPixel({
x: position.right,
y: position.top
});
bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat,
maxXY.lon, maxXY.lat);
} else {
var pixWidth = position.right - position.left;
var pixHeight = position.bottom - position.top;
var zoomFactor = Math.min((this.map.size.h / pixHeight),
(this.map.size.w / pixWidth));
var extent = this.map.getExtent();
var center = this.map.getLonLatFromPixel(targetCenterPx);
var xmin = center.lon - (extent.getWidth()/2)*zoomFactor;
var xmax = center.lon + (extent.getWidth()/2)*zoomFactor;
var ymin = center.lat - (extent.getHeight()/2)*zoomFactor;
var ymax = center.lat + (extent.getHeight()/2)*zoomFactor;
bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax);
}
// always zoom in/out
var lastZoom = this.map.getZoom(),
size = this.map.getSize(),
centerPx = {x: size.w / 2, y: size.h / 2},
zoom = this.map.getZoomForExtent(bounds),
oldRes = this.map.getResolution(),
newRes = this.map.getResolutionForZoom(zoom);
if (oldRes == newRes) {
this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx));
} else {
var zoomOriginPx = {
x: (oldRes * targetCenterPx.x - newRes * centerPx.x) /
(oldRes - newRes),
y: (oldRes * targetCenterPx.y - newRes * centerPx.y) /
(oldRes - newRes)
};
this.map.zoomTo(zoom, zoomOriginPx);
}
if (lastZoom == this.map.getZoom() && this.alwaysZoom == true){
this.map.zoomTo(lastZoom + (this.out ? -1 : 1));
}
} else if (this.zoomOnClick) { // it's a pixel
if (!this.out) {
this.map.zoomTo(this.map.getZoom() + 1, position);
} else {
this.map.zoomTo(this.map.getZoom() - 1, position);
}
}
},
CLASS_NAME: "OpenLayers.Control.ZoomBox"
});

View File

@ -0,0 +1,29 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control/Button.js
*/
/**
* Class: OpenLayers.Control.ZoomIn
* The ZoomIn control is a button to increase the zoom level of a map.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.ZoomIn = OpenLayers.Class(OpenLayers.Control.Button, {
/**
* Method: trigger
*/
trigger: function(){
if (this.map) {
this.map.zoomIn();
}
},
CLASS_NAME: "OpenLayers.Control.ZoomIn"
});

View File

@ -0,0 +1,29 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control/Button.js
*/
/**
* Class: OpenLayers.Control.ZoomOut
* The ZoomOut control is a button to decrease the zoom level of a map.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.ZoomOut = OpenLayers.Class(OpenLayers.Control.Button, {
/**
* Method: trigger
*/
trigger: function(){
if (this.map) {
this.map.zoomOut();
}
},
CLASS_NAME: "OpenLayers.Control.ZoomOut"
});

View File

@ -0,0 +1,54 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control/Panel.js
* @requires OpenLayers/Control/ZoomIn.js
* @requires OpenLayers/Control/ZoomOut.js
* @requires OpenLayers/Control/ZoomToMaxExtent.js
*/
/**
* Class: OpenLayers.Control.ZoomPanel
* The ZoomPanel control is a compact collecton of 3 zoom controls: a
* <OpenLayers.Control.ZoomIn>, a <OpenLayers.Control.ZoomToMaxExtent>, and a
* <OpenLayers.Control.ZoomOut>. By default it is drawn in the upper left
* corner of the map.
*
* Note:
* If you wish to use this class with the default images and you want
* it to look nice in ie6, you should add the following, conditionally
* added css stylesheet to your HTML file:
*
* (code)
* <!--[if lte IE 6]>
* <link rel="stylesheet" href="../theme/default/ie6-style.css" type="text/css" />
* <![endif]-->
* (end)
*
* Inherits from:
* - <OpenLayers.Control.Panel>
*/
OpenLayers.Control.ZoomPanel = OpenLayers.Class(OpenLayers.Control.Panel, {
/**
* Constructor: OpenLayers.Control.ZoomPanel
* Add the three zooming controls.
*
* Parameters:
* options - {Object} An optional object whose properties will be used
* to extend the control.
*/
initialize: function(options) {
OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);
this.addControls([
new OpenLayers.Control.ZoomIn(),
new OpenLayers.Control.ZoomToMaxExtent(),
new OpenLayers.Control.ZoomOut()
]);
},
CLASS_NAME: "OpenLayers.Control.ZoomPanel"
});

View File

@ -0,0 +1,35 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control/Button.js
*/
/**
* Class: OpenLayers.Control.ZoomToMaxExtent
* The ZoomToMaxExtent control is a button that zooms out to the maximum
* extent of the map. It is designed to be used with a
* <OpenLayers.Control.Panel>.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.ZoomToMaxExtent = OpenLayers.Class(OpenLayers.Control.Button, {
/**
* Method: trigger
*
* Called whenever this control is being rendered inside of a panel and a
* click occurs on this controls element. Actually zooms to the maximum
* extent of this controls map.
*/
trigger: function() {
if (this.map) {
this.map.zoomToMaxExtent();
}
},
CLASS_NAME: "OpenLayers.Control.ZoomToMaxExtent"
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,206 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Events.js
*/
/**
* Class: OpenLayers.Events.buttonclick
* Extension event type for handling buttons on top of a dom element. This
* event type fires "buttonclick" on its <target> when a button was
* clicked. Buttons are detected by the "olButton" class.
*
* This event type makes sure that button clicks do not interfere with other
* events that are registered on the same <element>.
*
* Event types provided by this extension:
* - *buttonclick* Triggered when a button is clicked. Listeners receive an
* object with a *buttonElement* property referencing the dom element of
* the clicked button, and an *buttonXY* property with the click position
* relative to the button.
*/
OpenLayers.Events.buttonclick = OpenLayers.Class({
/**
* Property: target
* {<OpenLayers.Events>} The events instance that the buttonclick event will
* be triggered on.
*/
target: null,
/**
* Property: events
* {Array} Events to observe and conditionally stop from propagating when
* an element with the olButton class (or its olAlphaImg child) is
* clicked.
*/
events: [
'mousedown', 'mouseup', 'click', 'dblclick',
'touchstart', 'touchmove', 'touchend', 'keydown'
],
/**
* Property: startRegEx
* {RegExp} Regular expression to test Event.type for events that start
* a buttonclick sequence.
*/
startRegEx: /^mousedown|touchstart$/,
/**
* Property: cancelRegEx
* {RegExp} Regular expression to test Event.type for events that cancel
* a buttonclick sequence.
*/
cancelRegEx: /^touchmove$/,
/**
* Property: completeRegEx
* {RegExp} Regular expression to test Event.type for events that complete
* a buttonclick sequence.
*/
completeRegEx: /^mouseup|touchend$/,
/**
* Property: startEvt
* {Event} The event that started the click sequence
*/
/**
* Constructor: OpenLayers.Events.buttonclick
* Construct a buttonclick event type. Applications are not supposed to
* create instances of this class - they are created on demand by
* <OpenLayers.Events> instances.
*
* Parameters:
* target - {<OpenLayers.Events>} The events instance that the buttonclick
* event will be triggered on.
*/
initialize: function(target) {
this.target = target;
for (var i=this.events.length-1; i>=0; --i) {
this.target.register(this.events[i], this, this.buttonClick, {
extension: true
});
}
},
/**
* Method: destroy
*/
destroy: function() {
for (var i=this.events.length-1; i>=0; --i) {
this.target.unregister(this.events[i], this, this.buttonClick);
}
delete this.target;
},
/**
* Method: getPressedButton
* Get the pressed button, if any. Returns undefined if no button
* was pressed.
*
* Arguments:
* element - {DOMElement} The event target.
*
* Returns:
* {DOMElement} The button element, or undefined.
*/
getPressedButton: function(element) {
var depth = 3, // limit the search depth
button;
do {
if(OpenLayers.Element.hasClass(element, "olButton")) {
// hit!
button = element;
break;
}
element = element.parentNode;
} while(--depth > 0 && element);
return button;
},
/**
* Method: ignore
* Check for event target elements that should be ignored by OpenLayers.
*
* Parameters:
* element - {DOMElement} The event target.
*/
ignore: function(element) {
var depth = 3,
ignore = false;
do {
if (element.nodeName.toLowerCase() === 'a') {
ignore = true;
break;
}
element = element.parentNode;
} while (--depth > 0 && element);
return ignore;
},
/**
* Method: buttonClick
* Check if a button was clicked, and fire the buttonclick event
*
* Parameters:
* evt - {Event}
*/
buttonClick: function(evt) {
var propagate = true,
element = OpenLayers.Event.element(evt);
if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf("mouse"))) {
// was a button pressed?
var button = this.getPressedButton(element);
if (button) {
if (evt.type === "keydown") {
switch (evt.keyCode) {
case OpenLayers.Event.KEY_RETURN:
case OpenLayers.Event.KEY_SPACE:
this.target.triggerEvent("buttonclick", {
buttonElement: button
});
OpenLayers.Event.stop(evt);
propagate = false;
break;
}
} else if (this.startEvt) {
if (this.completeRegEx.test(evt.type)) {
var pos = OpenLayers.Util.pagePosition(button);
var viewportElement = OpenLayers.Util.getViewportElement();
var scrollTop = window.pageYOffset || viewportElement.scrollTop;
var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;
pos[0] = pos[0] - scrollLeft;
pos[1] = pos[1] - scrollTop;
this.target.triggerEvent("buttonclick", {
buttonElement: button,
buttonXY: {
x: this.startEvt.clientX - pos[0],
y: this.startEvt.clientY - pos[1]
}
});
}
if (this.cancelRegEx.test(evt.type)) {
delete this.startEvt;
}
OpenLayers.Event.stop(evt);
propagate = false;
}
if (this.startRegEx.test(evt.type)) {
this.startEvt = evt;
OpenLayers.Event.stop(evt);
propagate = false;
}
} else {
propagate = !this.ignore(OpenLayers.Event.element(evt));
delete this.startEvt;
}
}
return propagate;
}
});

View File

@ -0,0 +1,321 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Events.js
*/
/**
* Class: OpenLayers.Events.featureclick
*
* Extension event type for handling feature click events, including overlapping
* features.
*
* Event types provided by this extension:
* - featureclick
*/
OpenLayers.Events.featureclick = OpenLayers.Class({
/**
* Property: cache
* {Object} A cache of features under the mouse.
*/
cache: null,
/**
* Property: map
* {<OpenLayers.Map>} The map to register browser events on.
*/
map: null,
/**
* Property: provides
* {Array(String)} The event types provided by this extension.
*/
provides: ["featureclick", "nofeatureclick", "featureover", "featureout"],
/**
* Constructor: OpenLayers.Events.featureclick
* Create a new featureclick event type.
*
* Parameters:
* target - {<OpenLayers.Events>} The events instance to create the events
* for.
*/
initialize: function(target) {
this.target = target;
if (target.object instanceof OpenLayers.Map) {
this.setMap(target.object);
} else if (target.object instanceof OpenLayers.Layer.Vector) {
if (target.object.map) {
this.setMap(target.object.map);
} else {
target.object.events.register("added", this, function(evt) {
this.setMap(target.object.map);
});
}
} else {
throw("Listeners for '" + this.provides.join("', '") +
"' events can only be registered for OpenLayers.Layer.Vector " +
"or OpenLayers.Map instances");
}
for (var i=this.provides.length-1; i>=0; --i) {
target.extensions[this.provides[i]] = true;
}
},
/**
* Method: setMap
*
* Parameters:
* map - {<OpenLayers.Map>} The map to register browser events on.
*/
setMap: function(map) {
this.map = map;
this.cache = {};
map.events.register("mousedown", this, this.start, {extension: true});
map.events.register("mouseup", this, this.onClick, {extension: true});
map.events.register("touchstart", this, this.start, {extension: true});
map.events.register("touchmove", this, this.cancel, {extension: true});
map.events.register("touchend", this, this.onClick, {extension: true});
map.events.register("mousemove", this, this.onMousemove, {extension: true});
},
/**
* Method: start
* Sets startEvt = evt.
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
start: function(evt) {
this.startEvt = evt;
},
/**
* Method: cancel
* Deletes the start event.
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
cancel: function(evt) {
delete this.startEvt;
},
/**
* Method: onClick
* Listener for the click event.
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
onClick: function(evt) {
if (!this.startEvt || evt.type !== "touchend" &&
!OpenLayers.Event.isLeftClick(evt)) {
return;
}
var features = this.getFeatures(this.startEvt);
delete this.startEvt;
// fire featureclick events
var feature, layer, more, clicked = {};
for (var i=0, len=features.length; i<len; ++i) {
feature = features[i];
layer = feature.layer;
clicked[layer.id] = true;
more = this.triggerEvent("featureclick", {feature: feature});
if (more === false) {
break;
}
}
// fire nofeatureclick events on all vector layers with no targets
for (i=0, len=this.map.layers.length; i<len; ++i) {
layer = this.map.layers[i];
if (layer instanceof OpenLayers.Layer.Vector && !clicked[layer.id]) {
this.triggerEvent("nofeatureclick", {layer: layer});
}
}
},
/**
* Method: onMousemove
* Listener for the mousemove event.
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
onMousemove: function(evt) {
delete this.startEvt;
var features = this.getFeatures(evt);
var over = {}, newly = [], feature;
for (var i=0, len=features.length; i<len; ++i) {
feature = features[i];
over[feature.id] = feature;
if (!this.cache[feature.id]) {
newly.push(feature);
}
}
// check if already over features
var out = [];
for (var id in this.cache) {
feature = this.cache[id];
if (feature.layer && feature.layer.map) {
if (!over[feature.id]) {
out.push(feature);
}
} else {
// removed
delete this.cache[id];
}
}
// fire featureover events
var more;
for (i=0, len=newly.length; i<len; ++i) {
feature = newly[i];
this.cache[feature.id] = feature;
more = this.triggerEvent("featureover", {feature: feature});
if (more === false) {
break;
}
}
// fire featureout events
for (i=0, len=out.length; i<len; ++i) {
feature = out[i];
delete this.cache[feature.id];
more = this.triggerEvent("featureout", {feature: feature});
if (more === false) {
break;
}
}
},
/**
* Method: triggerEvent
* Determines where to trigger the event and triggers it.
*
* Parameters:
* type - {String} The event type to trigger
* evt - {Object} The listener argument
*
* Returns:
* {Boolean} The last listener return.
*/
triggerEvent: function(type, evt) {
var layer = evt.feature ? evt.feature.layer : evt.layer,
object = this.target.object;
if (object instanceof OpenLayers.Map || object === layer) {
return this.target.triggerEvent(type, evt);
}
},
/**
* Method: getFeatures
* Get all features at the given screen location.
*
* Parameters:
* evt - {Object} Event object.
*
* Returns:
* {Array(<OpenLayers.Feature.Vector>)} List of features at the given point.
*/
getFeatures: function(evt) {
var x = evt.clientX, y = evt.clientY,
features = [], targets = [], layers = [],
layer, target, feature, i, len;
// go through all layers looking for targets
for (i=this.map.layers.length-1; i>=0; --i) {
layer = this.map.layers[i];
if (layer.div.style.display !== "none") {
if (layer.renderer instanceof OpenLayers.Renderer.Elements) {
if (layer instanceof OpenLayers.Layer.Vector) {
target = document.elementFromPoint(x, y);
while (target && target._featureId) {
feature = layer.getFeatureById(target._featureId);
if (feature) {
features.push(feature);
target.style.display = "none";
targets.push(target);
target = document.elementFromPoint(x, y);
} else {
// sketch, all bets off
target = false;
}
}
}
layers.push(layer);
layer.div.style.display = "none";
} else if (layer.renderer instanceof OpenLayers.Renderer.Canvas) {
feature = layer.renderer.getFeatureIdFromEvent(evt);
if (feature) {
features.push(feature);
layers.push(layer);
}
}
}
}
// restore feature visibility
for (i=0, len=targets.length; i<len; ++i) {
targets[i].style.display = "";
}
// restore layer visibility
for (i=layers.length-1; i>=0; --i) {
layers[i].div.style.display = "block";
}
return features;
},
/**
* APIMethod: destroy
* Clean up.
*/
destroy: function() {
for (var i=this.provides.length-1; i>=0; --i) {
delete this.target.extensions[this.provides[i]];
}
this.map.events.un({
mousemove: this.onMousemove,
mousedown: this.start,
mouseup: this.onClick,
touchstart: this.start,
touchmove: this.cancel,
touchend: this.onClick,
scope: this
});
delete this.cache;
delete this.map;
delete this.target;
}
});
/**
* Class: OpenLayers.Events.nofeatureclick
*
* Extension event type for handling click events that do not hit a feature.
*
* Event types provided by this extension:
* - nofeatureclick
*/
OpenLayers.Events.nofeatureclick = OpenLayers.Events.featureclick;
/**
* Class: OpenLayers.Events.featureover
*
* Extension event type for handling hovering over a feature.
*
* Event types provided by this extension:
* - featureover
*/
OpenLayers.Events.featureover = OpenLayers.Events.featureclick;
/**
* Class: OpenLayers.Events.featureout
*
* Extension event type for handling leaving a feature.
*
* Event types provided by this extension:
* - featureout
*/
OpenLayers.Events.featureout = OpenLayers.Events.featureclick;

View File

@ -0,0 +1,225 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/BaseTypes/Class.js
* @requires OpenLayers/Util.js
*/
/**
* Class: OpenLayers.Feature
* Features are combinations of geography and attributes. The OpenLayers.Feature
* class specifically combines a marker and a lonlat.
*/
OpenLayers.Feature = OpenLayers.Class({
/**
* Property: layer
* {<OpenLayers.Layer>}
*/
layer: null,
/**
* Property: id
* {String}
*/
id: null,
/**
* Property: lonlat
* {<OpenLayers.LonLat>}
*/
lonlat: null,
/**
* Property: data
* {Object}
*/
data: null,
/**
* Property: marker
* {<OpenLayers.Marker>}
*/
marker: null,
/**
* APIProperty: popupClass
* {<OpenLayers.Class>} The class which will be used to instantiate
* a new Popup. Default is <OpenLayers.Popup.Anchored>.
*/
popupClass: null,
/**
* Property: popup
* {<OpenLayers.Popup>}
*/
popup: null,
/**
* Constructor: OpenLayers.Feature
* Constructor for features.
*
* Parameters:
* layer - {<OpenLayers.Layer>}
* lonlat - {<OpenLayers.LonLat>}
* data - {Object}
*
* Returns:
* {<OpenLayers.Feature>}
*/
initialize: function(layer, lonlat, data) {
this.layer = layer;
this.lonlat = lonlat;
this.data = (data != null) ? data : {};
this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
},
/**
* Method: destroy
* nullify references to prevent circular references and memory leaks
*/
destroy: function() {
//remove the popup from the map
if ((this.layer != null) && (this.layer.map != null)) {
if (this.popup != null) {
this.layer.map.removePopup(this.popup);
}
}
// remove the marker from the layer
if (this.layer != null && this.marker != null) {
this.layer.removeMarker(this.marker);
}
this.layer = null;
this.id = null;
this.lonlat = null;
this.data = null;
if (this.marker != null) {
this.destroyMarker(this.marker);
this.marker = null;
}
if (this.popup != null) {
this.destroyPopup(this.popup);
this.popup = null;
}
},
/**
* Method: onScreen
*
* Returns:
* {Boolean} Whether or not the feature is currently visible on screen
* (based on its 'lonlat' property)
*/
onScreen:function() {
var onScreen = false;
if ((this.layer != null) && (this.layer.map != null)) {
var screenBounds = this.layer.map.getExtent();
onScreen = screenBounds.containsLonLat(this.lonlat);
}
return onScreen;
},
/**
* Method: createMarker
* Based on the data associated with the Feature, create and return a marker object.
*
* Returns:
* {<OpenLayers.Marker>} A Marker Object created from the 'lonlat' and 'icon' properties
* set in this.data. If no 'lonlat' is set, returns null. If no
* 'icon' is set, OpenLayers.Marker() will load the default image.
*
* Note - this.marker is set to return value
*
*/
createMarker: function() {
if (this.lonlat != null) {
this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);
}
return this.marker;
},
/**
* Method: destroyMarker
* Destroys marker.
* If user overrides the createMarker() function, s/he should be able
* to also specify an alternative function for destroying it
*/
destroyMarker: function() {
this.marker.destroy();
},
/**
* Method: createPopup
* Creates a popup object created from the 'lonlat', 'popupSize',
* and 'popupContentHTML' properties set in this.data. It uses
* this.marker.icon as default anchor.
*
* If no 'lonlat' is set, returns null.
* If no this.marker has been created, no anchor is sent.
*
* Note - the returned popup object is 'owned' by the feature, so you
* cannot use the popup's destroy method to discard the popup.
* Instead, you must use the feature's destroyPopup
*
* Note - this.popup is set to return value
*
* Parameters:
* closeBox - {Boolean} create popup with closebox or not
*
* Returns:
* {<OpenLayers.Popup>} Returns the created popup, which is also set
* as 'popup' property of this feature. Will be of whatever type
* specified by this feature's 'popupClass' property, but must be
* of type <OpenLayers.Popup>.
*
*/
createPopup: function(closeBox) {
if (this.lonlat != null) {
if (!this.popup) {
var anchor = (this.marker) ? this.marker.icon : null;
var popupClass = this.popupClass ?
this.popupClass : OpenLayers.Popup.Anchored;
this.popup = new popupClass(this.id + "_popup",
this.lonlat,
this.data.popupSize,
this.data.popupContentHTML,
anchor,
closeBox);
}
if (this.data.overflow != null) {
this.popup.contentDiv.style.overflow = this.data.overflow;
}
this.popup.feature = this;
}
return this.popup;
},
/**
* Method: destroyPopup
* Destroys the popup created via createPopup.
*
* As with the marker, if user overrides the createPopup() function, s/he
* should also be able to override the destruction
*/
destroyPopup: function() {
if (this.popup) {
this.popup.feature = null;
this.popup.destroy();
this.popup = null;
}
},
CLASS_NAME: "OpenLayers.Feature"
});

View File

@ -0,0 +1,510 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
// TRASH THIS
OpenLayers.State = {
/** states */
UNKNOWN: 'Unknown',
INSERT: 'Insert',
UPDATE: 'Update',
DELETE: 'Delete'
};
/**
* @requires OpenLayers/Feature.js
* @requires OpenLayers/Util.js
*/
/**
* Class: OpenLayers.Feature.Vector
* Vector features use the OpenLayers.Geometry classes as geometry description.
* They have an 'attributes' property, which is the data object, and a 'style'
* property, the default values of which are defined in the
* <OpenLayers.Feature.Vector.style> objects.
*
* Inherits from:
* - <OpenLayers.Feature>
*/
OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {
/**
* Property: fid
* {String}
*/
fid: null,
/**
* APIProperty: geometry
* {<OpenLayers.Geometry>}
*/
geometry: null,
/**
* APIProperty: attributes
* {Object} This object holds arbitrary, serializable properties that
* describe the feature.
*/
attributes: null,
/**
* Property: bounds
* {<OpenLayers.Bounds>} The box bounding that feature's geometry, that
* property can be set by an <OpenLayers.Format> object when
* deserializing the feature, so in most cases it represents an
* information set by the server.
*/
bounds: null,
/**
* Property: state
* {String}
*/
state: null,
/**
* APIProperty: style
* {Object}
*/
style: null,
/**
* APIProperty: url
* {String} If this property is set it will be taken into account by
* {<OpenLayers.HTTP>} when upadting or deleting the feature.
*/
url: null,
/**
* Property: renderIntent
* {String} rendering intent currently being used
*/
renderIntent: "default",
/**
* APIProperty: modified
* {Object} An object with the originals of the geometry and attributes of
* the feature, if they were changed. Currently this property is only read
* by <OpenLayers.Format.WFST.v1>, and written by
* <OpenLayers.Control.ModifyFeature>, which sets the geometry property.
* Applications can set the originals of modified attributes in the
* attributes property. Note that applications have to check if this
* object and the attributes property is already created before using it.
* After a change made with ModifyFeature, this object could look like
*
* (code)
* {
* geometry: >Object
* }
* (end)
*
* When an application has made changes to feature attributes, it could
* have set the attributes to something like this:
*
* (code)
* {
* attributes: {
* myAttribute: "original"
* }
* }
* (end)
*
* Note that <OpenLayers.Format.WFST.v1> only checks for truthy values in
* *modified.geometry* and the attribute names in *modified.attributes*,
* but it is recommended to set the original values (and not just true) as
* attribute value, so applications could use this information to undo
* changes.
*/
modified: null,
/**
* Constructor: OpenLayers.Feature.Vector
* Create a vector feature.
*
* Parameters:
* geometry - {<OpenLayers.Geometry>} The geometry that this feature
* represents.
* attributes - {Object} An optional object that will be mapped to the
* <attributes> property.
* style - {Object} An optional style object.
*/
initialize: function(geometry, attributes, style) {
OpenLayers.Feature.prototype.initialize.apply(this,
[null, null, attributes]);
this.lonlat = null;
this.geometry = geometry ? geometry : null;
this.state = null;
this.attributes = {};
if (attributes) {
this.attributes = OpenLayers.Util.extend(this.attributes,
attributes);
}
this.style = style ? style : null;
},
/**
* Method: destroy
* nullify references to prevent circular references and memory leaks
*/
destroy: function() {
if (this.layer) {
this.layer.removeFeatures(this);
this.layer = null;
}
this.geometry = null;
this.modified = null;
OpenLayers.Feature.prototype.destroy.apply(this, arguments);
},
/**
* Method: clone
* Create a clone of this vector feature. Does not set any non-standard
* properties.
*
* Returns:
* {<OpenLayers.Feature.Vector>} An exact clone of this vector feature.
*/
clone: function () {
return new OpenLayers.Feature.Vector(
this.geometry ? this.geometry.clone() : null,
this.attributes,
this.style);
},
/**
* Method: onScreen
* Determine whether the feature is within the map viewport. This method
* tests for an intersection between the geometry and the viewport
* bounds. If a more effecient but less precise geometry bounds
* intersection is desired, call the method with the boundsOnly
* parameter true.
*
* Parameters:
* boundsOnly - {Boolean} Only test whether a feature's bounds intersects
* the viewport bounds. Default is false. If false, the feature's
* geometry must intersect the viewport for onScreen to return true.
*
* Returns:
* {Boolean} The feature is currently visible on screen (optionally
* based on its bounds if boundsOnly is true).
*/
onScreen:function(boundsOnly) {
var onScreen = false;
if(this.layer && this.layer.map) {
var screenBounds = this.layer.map.getExtent();
if(boundsOnly) {
var featureBounds = this.geometry.getBounds();
onScreen = screenBounds.intersectsBounds(featureBounds);
} else {
var screenPoly = screenBounds.toGeometry();
onScreen = screenPoly.intersects(this.geometry);
}
}
return onScreen;
},
/**
* Method: getVisibility
* Determine whether the feature is displayed or not. It may not displayed
* because:
* - its style display property is set to 'none',
* - it doesn't belong to any layer,
* - the styleMap creates a symbolizer with display property set to 'none'
* for it,
* - the layer which it belongs to is not visible.
*
* Returns:
* {Boolean} The feature is currently displayed.
*/
getVisibility: function() {
return !(this.style && this.style.display == 'none' ||
!this.layer ||
this.layer && this.layer.styleMap &&
this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == 'none' ||
this.layer && !this.layer.getVisibility());
},
/**
* Method: createMarker
* HACK - we need to decide if all vector features should be able to
* create markers
*
* Returns:
* {<OpenLayers.Marker>} For now just returns null
*/
createMarker: function() {
return null;
},
/**
* Method: destroyMarker
* HACK - we need to decide if all vector features should be able to
* delete markers
*
* If user overrides the createMarker() function, s/he should be able
* to also specify an alternative function for destroying it
*/
destroyMarker: function() {
// pass
},
/**
* Method: createPopup
* HACK - we need to decide if all vector features should be able to
* create popups
*
* Returns:
* {<OpenLayers.Popup>} For now just returns null
*/
createPopup: function() {
return null;
},
/**
* Method: atPoint
* Determins whether the feature intersects with the specified location.
*
* Parameters:
* lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
* object with a 'lon' and 'lat' properties.
* toleranceLon - {float} Optional tolerance in Geometric Coords
* toleranceLat - {float} Optional tolerance in Geographic Coords
*
* Returns:
* {Boolean} Whether or not the feature is at the specified location
*/
atPoint: function(lonlat, toleranceLon, toleranceLat) {
var atPoint = false;
if(this.geometry) {
atPoint = this.geometry.atPoint(lonlat, toleranceLon,
toleranceLat);
}
return atPoint;
},
/**
* Method: destroyPopup
* HACK - we need to decide if all vector features should be able to
* delete popups
*/
destroyPopup: function() {
// pass
},
/**
* Method: move
* Moves the feature and redraws it at its new location
*
* Parameters:
* location - {<OpenLayers.LonLat> or <OpenLayers.Pixel>} the
* location to which to move the feature.
*/
move: function(location) {
if(!this.layer || !this.geometry.move){
//do nothing if no layer or immoveable geometry
return undefined;
}
var pixel;
if (location.CLASS_NAME == "OpenLayers.LonLat") {
pixel = this.layer.getViewPortPxFromLonLat(location);
} else {
pixel = location;
}
var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());
var res = this.layer.map.getResolution();
this.geometry.move(res * (pixel.x - lastPixel.x),
res * (lastPixel.y - pixel.y));
this.layer.drawFeature(this);
return lastPixel;
},
/**
* Method: toState
* Sets the new state
*
* Parameters:
* state - {String}
*/
toState: function(state) {
if (state == OpenLayers.State.UPDATE) {
switch (this.state) {
case OpenLayers.State.UNKNOWN:
case OpenLayers.State.DELETE:
this.state = state;
break;
case OpenLayers.State.UPDATE:
case OpenLayers.State.INSERT:
break;
}
} else if (state == OpenLayers.State.INSERT) {
switch (this.state) {
case OpenLayers.State.UNKNOWN:
break;
default:
this.state = state;
break;
}
} else if (state == OpenLayers.State.DELETE) {
switch (this.state) {
case OpenLayers.State.INSERT:
// the feature should be destroyed
break;
case OpenLayers.State.DELETE:
break;
case OpenLayers.State.UNKNOWN:
case OpenLayers.State.UPDATE:
this.state = state;
break;
}
} else if (state == OpenLayers.State.UNKNOWN) {
this.state = state;
}
},
CLASS_NAME: "OpenLayers.Feature.Vector"
});
/**
* Constant: OpenLayers.Feature.Vector.style
* OpenLayers features can have a number of style attributes. The 'default'
* style will typically be used if no other style is specified. These
* styles correspond for the most part, to the styling properties defined
* by the SVG standard.
* Information on fill properties: http://www.w3.org/TR/SVG/painting.html#FillProperties
* Information on stroke properties: http://www.w3.org/TR/SVG/painting.html#StrokeProperties
*
* Symbolizer properties:
* fill - {Boolean} Set to false if no fill is desired.
* fillColor - {String} Hex fill color. Default is "#ee9900".
* fillOpacity - {Number} Fill opacity (0-1). Default is 0.4
* stroke - {Boolean} Set to false if no stroke is desired.
* strokeColor - {String} Hex stroke color. Default is "#ee9900".
* strokeOpacity - {Number} Stroke opacity (0-1). Default is 1.
* strokeWidth - {Number} Pixel stroke width. Default is 1.
* strokeLinecap - {String} Stroke cap type. Default is "round". [butt | round | square]
* strokeDashstyle - {String} Stroke dash style. Default is "solid". [dot | dash | dashdot | longdash | longdashdot | solid]
* graphic - {Boolean} Set to false if no graphic is desired.
* pointRadius - {Number} Pixel point radius. Default is 6.
* pointerEvents - {String} Default is "visiblePainted".
* cursor - {String} Default is "".
* externalGraphic - {String} Url to an external graphic that will be used for rendering points.
* graphicWidth - {Number} Pixel width for sizing an external graphic.
* graphicHeight - {Number} Pixel height for sizing an external graphic.
* graphicOpacity - {Number} Opacity (0-1) for an external graphic.
* graphicXOffset - {Number} Pixel offset along the positive x axis for displacing an external graphic.
* graphicYOffset - {Number} Pixel offset along the positive y axis for displacing an external graphic.
* rotation - {Number} For point symbolizers, this is the rotation of a graphic in the clockwise direction about its center point (or any point off center as specified by graphicXOffset and graphicYOffset).
* graphicZIndex - {Number} The integer z-index value to use in rendering.
* graphicName - {String} Named graphic to use when rendering points. Supported values include "circle" (default),
* "square", "star", "x", "cross", "triangle".
* graphicTitle - {String} Tooltip when hovering over a feature. *deprecated*, use title instead
* title - {String} Tooltip when hovering over a feature. Not supported by the canvas renderer.
* backgroundGraphic - {String} Url to a graphic to be used as the background under an externalGraphic.
* backgroundGraphicZIndex - {Number} The integer z-index value to use in rendering the background graphic.
* backgroundXOffset - {Number} The x offset (in pixels) for the background graphic.
* backgroundYOffset - {Number} The y offset (in pixels) for the background graphic.
* backgroundHeight - {Number} The height of the background graphic. If not provided, the graphicHeight will be used.
* backgroundWidth - {Number} The width of the background width. If not provided, the graphicWidth will be used.
* label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either
* fillText or mozDrawText to be available.
* labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string
* composed of two characters. The first character is for the horizontal alignment, the second for the vertical
* alignment. Valid values for horizontal alignment: "l"=left, "c"=center, "r"=right. Valid values for vertical
* alignment: "t"=top, "m"=middle, "b"=bottom. Example values: "lt", "cm", "rb". Default is "cm".
* labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer.
* labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer.
* labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls.
* Default is false.
* labelOutlineColor - {String} The color of the label outline. Default is 'white'. Only supported by the canvas & SVG renderers.
* labelOutlineWidth - {Number} The width of the label outline. Default is 3, set to 0 or null to disable. Only supported by the SVG renderers.
* labelOutlineOpacity - {Number} The opacity (0-1) of the label outline. Default is fontOpacity. Only supported by the canvas & SVG renderers.
* fontColor - {String} The font color for the label, to be provided like CSS.
* fontOpacity - {Number} Opacity (0-1) for the label
* fontFamily - {String} The font family for the label, to be provided like in CSS.
* fontSize - {String} The font size for the label, to be provided like in CSS.
* fontStyle - {String} The font style for the label, to be provided like in CSS.
* fontWeight - {String} The font weight for the label, to be provided like in CSS.
* display - {String} Symbolizers will have no effect if display is set to "none". All other values have no effect.
*/
OpenLayers.Feature.Vector.style = {
'default': {
fillColor: "#ee9900",
fillOpacity: 0.4,
hoverFillColor: "white",
hoverFillOpacity: 0.8,
strokeColor: "#ee9900",
strokeOpacity: 1,
strokeWidth: 1,
strokeLinecap: "round",
strokeDashstyle: "solid",
hoverStrokeColor: "red",
hoverStrokeOpacity: 1,
hoverStrokeWidth: 0.2,
pointRadius: 6,
hoverPointRadius: 1,
hoverPointUnit: "%",
pointerEvents: "visiblePainted",
cursor: "inherit",
fontColor: "#000000",
labelAlign: "cm",
labelOutlineColor: "white",
labelOutlineWidth: 3
},
'select': {
fillColor: "blue",
fillOpacity: 0.4,
hoverFillColor: "white",
hoverFillOpacity: 0.8,
strokeColor: "blue",
strokeOpacity: 1,
strokeWidth: 2,
strokeLinecap: "round",
strokeDashstyle: "solid",
hoverStrokeColor: "red",
hoverStrokeOpacity: 1,
hoverStrokeWidth: 0.2,
pointRadius: 6,
hoverPointRadius: 1,
hoverPointUnit: "%",
pointerEvents: "visiblePainted",
cursor: "pointer",
fontColor: "#000000",
labelAlign: "cm",
labelOutlineColor: "white",
labelOutlineWidth: 3
},
'temporary': {
fillColor: "#66cccc",
fillOpacity: 0.2,
hoverFillColor: "white",
hoverFillOpacity: 0.8,
strokeColor: "#66cccc",
strokeOpacity: 1,
strokeLinecap: "round",
strokeWidth: 2,
strokeDashstyle: "solid",
hoverStrokeColor: "red",
hoverStrokeOpacity: 1,
hoverStrokeWidth: 0.2,
pointRadius: 6,
hoverPointRadius: 1,
hoverPointUnit: "%",
pointerEvents: "visiblePainted",
cursor: "inherit",
fontColor: "#000000",
labelAlign: "cm",
labelOutlineColor: "white",
labelOutlineWidth: 3
},
'delete': {
display: "none"
}
};

View File

@ -0,0 +1,87 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/BaseTypes/Class.js
* @requires OpenLayers/Util.js
* @requires OpenLayers/Style.js
*/
/**
* Class: OpenLayers.Filter
* This class represents an OGC Filter.
*/
OpenLayers.Filter = OpenLayers.Class({
/**
* Constructor: OpenLayers.Filter
* This class represents a generic filter.
*
* Parameters:
* options - {Object} Optional object whose properties will be set on the
* instance.
*
* Returns:
* {<OpenLayers.Filter>}
*/
initialize: function(options) {
OpenLayers.Util.extend(this, options);
},
/**
* APIMethod: destroy
* Remove reference to anything added.
*/
destroy: function() {
},
/**
* APIMethod: evaluate
* Evaluates this filter in a specific context. Instances or subclasses
* are supposed to override this method.
*
* Parameters:
* context - {Object} Context to use in evaluating the filter. If a vector
* feature is provided, the feature.attributes will be used as context.
*
* Returns:
* {Boolean} The filter applies.
*/
evaluate: function(context) {
return true;
},
/**
* APIMethod: clone
* Clones this filter. Should be implemented by subclasses.
*
* Returns:
* {<OpenLayers.Filter>} Clone of this filter.
*/
clone: function() {
return null;
},
/**
* APIMethod: toString
*
* Returns:
* {String} Include <OpenLayers.Format.CQL> in your build to get a CQL
* representation of the filter returned. Otherwise "[Object object]"
* will be returned.
*/
toString: function() {
var string;
if (OpenLayers.Format && OpenLayers.Format.CQL) {
string = OpenLayers.Format.CQL.prototype.write(this);
} else {
string = Object.prototype.toString.call(this);
}
return string;
},
CLASS_NAME: "OpenLayers.Filter"
});

View File

@ -0,0 +1,267 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Filter.js
*/
/**
* Class: OpenLayers.Filter.Comparison
* This class represents a comparison filter.
*
* Inherits from:
* - <OpenLayers.Filter>
*/
OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {
/**
* APIProperty: type
* {String} type: type of the comparison. This is one of
* - OpenLayers.Filter.Comparison.EQUAL_TO = "==";
* - OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!=";
* - OpenLayers.Filter.Comparison.LESS_THAN = "<";
* - OpenLayers.Filter.Comparison.GREATER_THAN = ">";
* - OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<=";
* - OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
* - OpenLayers.Filter.Comparison.BETWEEN = "..";
* - OpenLayers.Filter.Comparison.LIKE = "~";
* - OpenLayers.Filter.Comparison.IS_NULL = "NULL";
*/
type: null,
/**
* APIProperty: property
* {String}
* name of the context property to compare
*/
property: null,
/**
* APIProperty: value
* {Number} or {String}
* comparison value for binary comparisons. In the case of a String, this
* can be a combination of text and propertyNames in the form
* "literal ${propertyName}"
*/
value: null,
/**
* Property: matchCase
* {Boolean} Force case sensitive searches for EQUAL_TO and NOT_EQUAL_TO
* comparisons. The Filter Encoding 1.1 specification added a matchCase
* attribute to ogc:PropertyIsEqualTo and ogc:PropertyIsNotEqualTo
* elements. This property will be serialized with those elements only
* if using the v1.1.0 filter format. However, when evaluating filters
* here, the matchCase property will always be respected (for EQUAL_TO
* and NOT_EQUAL_TO). Default is true.
*/
matchCase: true,
/**
* APIProperty: lowerBoundary
* {Number} or {String}
* lower boundary for between comparisons. In the case of a String, this
* can be a combination of text and propertyNames in the form
* "literal ${propertyName}"
*/
lowerBoundary: null,
/**
* APIProperty: upperBoundary
* {Number} or {String}
* upper boundary for between comparisons. In the case of a String, this
* can be a combination of text and propertyNames in the form
* "literal ${propertyName}"
*/
upperBoundary: null,
/**
* Constructor: OpenLayers.Filter.Comparison
* Creates a comparison rule.
*
* Parameters:
* options - {Object} An optional object with properties to set on the
* rule
*
* Returns:
* {<OpenLayers.Filter.Comparison>}
*/
initialize: function(options) {
OpenLayers.Filter.prototype.initialize.apply(this, [options]);
// since matchCase on PropertyIsLike is not schema compliant, we only
// want to use this if explicitly asked for
if (this.type === OpenLayers.Filter.Comparison.LIKE
&& options.matchCase === undefined) {
this.matchCase = null;
}
},
/**
* APIMethod: evaluate
* Evaluates this filter in a specific context.
*
* Parameters:
* context - {Object} Context to use in evaluating the filter. If a vector
* feature is provided, the feature.attributes will be used as context.
*
* Returns:
* {Boolean} The filter applies.
*/
evaluate: function(context) {
if (context instanceof OpenLayers.Feature.Vector) {
context = context.attributes;
}
var result = false;
var got = context[this.property];
var exp;
switch(this.type) {
case OpenLayers.Filter.Comparison.EQUAL_TO:
exp = this.value;
if(!this.matchCase &&
typeof got == "string" && typeof exp == "string") {
result = (got.toUpperCase() == exp.toUpperCase());
} else {
result = (got == exp);
}
break;
case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:
exp = this.value;
if(!this.matchCase &&
typeof got == "string" && typeof exp == "string") {
result = (got.toUpperCase() != exp.toUpperCase());
} else {
result = (got != exp);
}
break;
case OpenLayers.Filter.Comparison.LESS_THAN:
result = got < this.value;
break;
case OpenLayers.Filter.Comparison.GREATER_THAN:
result = got > this.value;
break;
case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:
result = got <= this.value;
break;
case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:
result = got >= this.value;
break;
case OpenLayers.Filter.Comparison.BETWEEN:
result = (got >= this.lowerBoundary) &&
(got <= this.upperBoundary);
break;
case OpenLayers.Filter.Comparison.LIKE:
var regexp = new RegExp(this.value, "gi");
result = regexp.test(got);
break;
case OpenLayers.Filter.Comparison.IS_NULL:
result = (got === null);
break;
}
return result;
},
/**
* APIMethod: value2regex
* Converts the value of this rule into a regular expression string,
* according to the wildcard characters specified. This method has to
* be called after instantiation of this class, if the value is not a
* regular expression already.
*
* Parameters:
* wildCard - {Char} wildcard character in the above value, default
* is "*"
* singleChar - {Char} single-character wildcard in the above value
* default is "."
* escapeChar - {Char} escape character in the above value, default is
* "!"
*
* Returns:
* {String} regular expression string
*/
value2regex: function(wildCard, singleChar, escapeChar) {
if (wildCard == ".") {
throw new Error("'.' is an unsupported wildCard character for " +
"OpenLayers.Filter.Comparison");
}
// set UMN MapServer defaults for unspecified parameters
wildCard = wildCard ? wildCard : "*";
singleChar = singleChar ? singleChar : ".";
escapeChar = escapeChar ? escapeChar : "!";
this.value = this.value.replace(
new RegExp("\\"+escapeChar+"(.|$)", "g"), "\\$1");
this.value = this.value.replace(
new RegExp("\\"+singleChar, "g"), ".");
this.value = this.value.replace(
new RegExp("\\"+wildCard, "g"), ".*");
this.value = this.value.replace(
new RegExp("\\\\.\\*", "g"), "\\"+wildCard);
this.value = this.value.replace(
new RegExp("\\\\\\.", "g"), "\\"+singleChar);
return this.value;
},
/**
* Method: regex2value
* Convert the value of this rule from a regular expression string into an
* ogc literal string using a wildCard of *, a singleChar of ., and an
* escape of !. Leaves the <value> property unmodified.
*
* Returns:
* {String} A string value.
*/
regex2value: function() {
var value = this.value;
// replace ! with !!
value = value.replace(/!/g, "!!");
// replace \. with !. (watching out for \\.)
value = value.replace(/(\\)?\\\./g, function($0, $1) {
return $1 ? $0 : "!.";
});
// replace \* with #* (watching out for \\*)
value = value.replace(/(\\)?\\\*/g, function($0, $1) {
return $1 ? $0 : "!*";
});
// replace \\ with \
value = value.replace(/\\\\/g, "\\");
// convert .* to * (the sequence #.* is not allowed)
value = value.replace(/\.\*/g, "*");
return value;
},
/**
* APIMethod: clone
* Clones this filter.
*
* Returns:
* {<OpenLayers.Filter.Comparison>} Clone of this filter.
*/
clone: function() {
return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this);
},
CLASS_NAME: "OpenLayers.Filter.Comparison"
});
OpenLayers.Filter.Comparison.EQUAL_TO = "==";
OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!=";
OpenLayers.Filter.Comparison.LESS_THAN = "<";
OpenLayers.Filter.Comparison.GREATER_THAN = ">";
OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<=";
OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
OpenLayers.Filter.Comparison.BETWEEN = "..";
OpenLayers.Filter.Comparison.LIKE = "~";
OpenLayers.Filter.Comparison.IS_NULL = "NULL";

View File

@ -0,0 +1,87 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Filter.js
*/
/**
* Class: OpenLayers.Filter.FeatureId
* This class represents a ogc:FeatureId Filter, as being used for rule-based SLD
* styling
*
* Inherits from:
* - <OpenLayers.Filter>
*/
OpenLayers.Filter.FeatureId = OpenLayers.Class(OpenLayers.Filter, {
/**
* APIProperty: fids
* {Array(String)} Feature Ids to evaluate this rule against.
* To be passed inside the params object.
*/
fids: null,
/**
* Property: type
* {String} Type to identify this filter.
*/
type: "FID",
/**
* Constructor: OpenLayers.Filter.FeatureId
* Creates an ogc:FeatureId rule.
*
* Parameters:
* options - {Object} An optional object with properties to set on the
* rule
*
* Returns:
* {<OpenLayers.Filter.FeatureId>}
*/
initialize: function(options) {
this.fids = [];
OpenLayers.Filter.prototype.initialize.apply(this, [options]);
},
/**
* APIMethod: evaluate
* evaluates this rule for a specific feature
*
* Parameters:
* feature - {<OpenLayers.Feature>} feature to apply the rule to.
* For vector features, the check is run against the fid,
* for plain features against the id.
*
* Returns:
* {Boolean} true if the rule applies, false if it does not
*/
evaluate: function(feature) {
for (var i=0, len=this.fids.length; i<len; i++) {
var fid = feature.fid || feature.id;
if (fid == this.fids[i]) {
return true;
}
}
return false;
},
/**
* APIMethod: clone
* Clones this filter.
*
* Returns:
* {<OpenLayers.Filter.FeatureId>} Clone of this filter.
*/
clone: function() {
var filter = new OpenLayers.Filter.FeatureId();
OpenLayers.Util.extend(filter, this);
filter.fids = this.fids.slice();
return filter;
},
CLASS_NAME: "OpenLayers.Filter.FeatureId"
});

View File

@ -0,0 +1,49 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Filter.js
*/
/**
* Class: OpenLayers.Filter.Function
* This class represents a filter function.
* We are using this class for creation of complex
* filters that can contain filter functions as values.
* Nesting function as other functions parameter is supported.
*
* Inherits from:
* - <OpenLayers.Filter>
*/
OpenLayers.Filter.Function = OpenLayers.Class(OpenLayers.Filter, {
/**
* APIProperty: name
* {String} Name of the function.
*/
name: null,
/**
* APIProperty: params
* {Array(<OpenLayers.Filter.Function> || String || Number)} Function parameters
* For now support only other Functions, String or Number
*/
params: null,
/**
* Constructor: OpenLayers.Filter.Function
* Creates a filter function.
*
* Parameters:
* options - {Object} An optional object with properties to set on the
* function.
*
* Returns:
* {<OpenLayers.Filter.Function>}
*/
CLASS_NAME: "OpenLayers.Filter.Function"
});

View File

@ -0,0 +1,121 @@
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Filter.js
*/
/**
* Class: OpenLayers.Filter.Logical
* This class represents ogc:And, ogc:Or and ogc:Not rules.
*
* Inherits from:
* - <OpenLayers.Filter>
*/
OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {
/**
* APIProperty: filters
* {Array(<OpenLayers.Filter>)} Child filters for this filter.
*/
filters: null,
/**
* APIProperty: type
* {String} type of logical operator. Available types are:
* - OpenLayers.Filter.Logical.AND = "&&";
* - OpenLayers.Filter.Logical.OR = "||";
* - OpenLayers.Filter.Logical.NOT = "!";
*/
type: null,
/**
* Constructor: OpenLayers.Filter.Logical
* Creates a logical filter (And, Or, Not).
*
* Parameters:
* options - {Object} An optional object with properties to set on the
* filter.
*
* Returns:
* {<OpenLayers.Filter.Logical>}
*/
initialize: function(options) {
this.filters = [];
OpenLayers.Filter.prototype.initialize.apply(this, [options]);
},
/**
* APIMethod: destroy
* Remove reference to child filters.
*/
destroy: function() {
this.filters = null;
OpenLayers.Filter.prototype.destroy.apply(this);
},
/**
* APIMethod: evaluate
* Evaluates this filter in a specific context.
*
* Parameters:
* context - {Object} Context to use in evaluating the filter. A vector
* feature may also be provided to evaluate feature attributes in
* comparison filters or geometries in spatial filters.
*
* Returns:
* {Boolean} The filter applies.
*/
evaluate: function(context) {
var i, len;
switch(this.type) {
case OpenLayers.Filter.Logical.AND:
for (i=0, len=this.filters.length; i<len; i++) {
if (this.filters[i].evaluate(context) == false) {
return false;
}
}
return true;
case OpenLayers.Filter.Logical.OR:
for (i=0, len=this.filters.length; i<len; i++) {
if (this.filters[i].evaluate(context) == true) {
return true;
}
}
return false;
case OpenLayers.Filter.Logical.NOT:
return (!this.filters[0].evaluate(context));
}
return undefined;
},
/**
* APIMethod: clone
* Clones this filter.
*
* Returns:
* {<OpenLayers.Filter.Logical>} Clone of this filter.
*/
clone: function() {
var filters = [];
for(var i=0, len=this.filters.length; i<len; ++i) {
filters.push(this.filters[i].clone());
}
return new OpenLayers.Filter.Logical({
type: this.type,
filters: filters
});
},
CLASS_NAME: "OpenLayers.Filter.Logical"
});
OpenLayers.Filter.Logical.AND = "&&";
OpenLayers.Filter.Logical.OR = "||";
OpenLayers.Filter.Logical.NOT = "!";

Some files were not shown because too many files have changed in this diff Show More