|
||||||||
PREV NEXT | FRAMES NO FRAMES |
dBox class provides support functions for advanced
web clients using the MapServer (although this code is NOT MapServer
specific. Functions supported include drag panning, rubber-band box
drawing, measure and area tools. Basically the dBox class handles
user interaction (via a mouse) with a map.
Class Summary | |
dBox | This is the basic dBox class. |
Method Summary | |
static void
|
clear_coords()
function to be called on mouse exit events. |
static void
|
main_area(a, d, l, n)
function to be called on area events. |
static void
|
main_measure(s, t, n, a)
function to be called on measure events. |
static void
|
main_mousemove(x, y)
function to be called on mouse move events. |
static void
|
main_setbox(minx, miny, maxx, maxy)
function to be called on mouse click/box draw events. |
/** * @fileoverview dBox class provides support functions for advanced * web clients using the MapServer (although this code is NOT MapServer * specific. Functions supported include drag panning, rubber-band box * drawing, measure and area tools. Basically the dBox class handles * user interaction (via a mouse) with a map. */ var DBOX_MOUSEENTER = 1; var DBOX_MOUSEEXIT = 2; var DBOX_MOUSEMOVE = 3; var DBOX_SETBOX = 4; var DBOX_RESET = 5; var DBOX_MEASURE = 6; var DBOX_AREA = 7; var DEFAULT_BUSY_LOADING_IMG_SRC = "busy.gif"; var DEFAULT_BUSY_LOADING_IMG_WIDTH = 180; var DEFAULT_BUSY_LOADING_IMG_HEIGHT = 20; /** * Construct a new dBox object. * @class This is the basic dBox class. * * @constructor * @param {String} name The name of the anchor element (img or div). * @return A new mapserv object */ function dBox(name) { this.name = name; /** * color (either name or hexidecimal) of any drawing elements. (default: red) * @type String */ this.color = 'red'; /** * thickness (in pixels) of lines for any drawing elements. (default: 1) * @type Integer */ this.thickness = 1; this.width = 0; this.height = 0; this.box = true; this.line = false; this.drag = false; this.poly = false; this.verbose = false; this.anchor = null; // DHTML elements this.container = null; this.canvas = null; this.image = null; /** * cursor to be displayed when over the dBox. (default: crosshair). * @type String */ this.cursor = 'crosshair'; /** * minimum size (in pixels_ of a mouse event resulting in a point. (default: 10). * @type Integer */ this.jitter = 10; this.x1 = this.y1 = this.x2 = this.y2 = -1; this.offsetx = this.offsety = 0; this.x = new Array(); // arrays to hold coordinates this.y = new Array(); this.length = 0; this.area = 0; this.graphics; this.dragging = false; this.waiting = false; // are we waiting for a new image? // handlers/callbacks /** * function to be called on mouse enter events. * <p>Must be defined and set (via {@link #setHandler}) elsewhere in * the application, or nothing happens with these events.</p> * @type Function */ this.mouseEnterHandler = null; /** * function to be called on mouse exit events. * <p>Must be defined and set (via {@link #setHandler}) elsewhere in * the application, or nothing happens with these events.</p> * <pre>main.setHandler(DBOX_MOUSEEXIT, clear_coords); * function clear_coords() { * var e = document.getElementById("coords"); * if(e) e.innerHTML = ' '; * }</pre> * @type Function */ this.mouseExitHandler = null; /** * function to be called on mouse move events. * <p>Must be defined and set (via {@link #setHandler}) elsewhere in * the application, or nothing happens with these events.</p> * <pre>main.setHandler(DBOX_MOUSEMOVE, main_mousemove); * function main_mousemove(x, y) { * var text = ''; * var utmx = Number(ms.extent[0] + x*ms.cellsize); * var utmy = Number(ms.extent[3] - y*ms.cellsize); * * text = " UTM Coordinates: x =" + Math.round(utmx) + " and y = " + Math.round(utmy); * * var e = document.getElementById("coords"); * if(e) e.innerHTML = text; * }</pre> * @type Function */ this.mouseMoveHandler = null; /** * function to be called on mouse click/box draw events. * <p>Must be defined and set (via {@link #setHandler}) elsewhere in * the application, or nothing happens with these events.</p> * <p>typically this is where you would trigger draw or query behaviour * within your application.</p> * <pre>main.setHandler(DBOX_SETBOX, main_setbox); * function main_setbox(minx, miny, maxx, maxy) { * if(ms.mode == 'map') { * if(minx != maxx && miny != maxy) * ms.applyBox(minx, miny, maxx, maxy); * else * ms.applyZoom(minx, miny); * ms.draw(); // builds draw URL and calls draw callback * } else if(ms.mode != 'map') { * ms.applyBoxQuery(minx, miny, maxx, maxy); * ms.applyPointQuery(minx, miny); * ms.query(); // builds query URL and calls query callback * } * }</pre> * @type Function */ this.setBoxHandler = null; this.resetHandler = null; /** * function to be called on measure events. * <p>Must be defined and set (via {@link #setHandler}) elsewhere in * the application, or nothing happens with these events.</p> * <p>typically this is where you would do something with the coordinate * information generated by dBox. For example, you might convert from * pixels to map coordinates and update a page element.</p> * <pre>main.setHandler(DBOX_MEASURE, main_measure); * function main_measure(s, t, n, a) { * var text = ' Distance: ' + Math.round(t*ms.cellsize) + " meters (" + n + " points)"; * var e = document.getElementById("measure"); * if(e) e.innerHTML = text; * }</pre> * @type Function */ this.measureHandler = null; /** * function to be called on area events. * <p>Must be defined and set (via {@link #setHandler}) elsewhere in * the application, or nothing happens with these events.</p> * <p>typically this is where you would do something with the coordinate * information generated by dBox. For example, you might convert from * pixels to map coordinates and update a page element. You can also do * simple feature digitizing using this feature</p> * <pre>main.setHandler(DBOX_AREA, main_area); * function main_area(a, d, l, n) { * var text = ' Area: ' + Math.round(a*ms.cellsize) + " sq. m. Distance: " + Math.round(l*ms.cellsize) + " m. (" + n + " points)"; * var e = document.getElementById("measure"); * if(e) e.innerHTML = text; * }</pre> * @type Function */ this.areaHandler = null; this.busyMessage = null; // busy message/image this.busyImage = null; var self = this; // object reference to avoid "this" conflicts with callbacks and event handlers function distance(x1, y1, x2, y2) { return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); } function area(x, y) { var area = 0; for (var i = 0; i < x.length; i++) { var j = (i + 1) % x.length; area += x[i] * y[j] - x[j] * y[i]; } return (area < 0 ? -area / 2.0:area / 2.0); } /** * set a handler function for draw/query operations. * @param {Enumerated} type constant for handler type to be set. * Valid type values are: * <ul> * <li> DBOX_MOUSEENTER (0)</li> * <li> DBOX_MOUSEEXIT (1)</li> * <li> DBOX_MOUSEMOVE (2)</li> * <li> DBOX_SETBOX (3)</li> * <li> DBOX_MEASURE (5)</li> * <li> DBOX_AREA (6)</li> * </ul> * @param {Function} handler function to be called */ this.setHandler = function(type, handler) { if (type == DBOX_MOUSEENTER) self.mouseEnterHandler = handler; else if (type == DBOX_MOUSEEXIT) self.mouseExitHandler = handler; else if (type == DBOX_MOUSEMOVE) self.mouseMoveHandler = handler; else if (type == DBOX_SETBOX) self.setBoxHandler = handler; else if (type == DBOX_RESET) self.resetHandler = handler; else if (type == DBOX_MEASURE) self.measureHandler = handler; else if (type == DBOX_AREA) self.areaHandler = handler; } /** * Method to initialize dBox. This should be called after page load. * */ this.initialize = function() { self.anchor = xGetElementById(self.name); // anchor *must* exist in the document, either as an image or a div self.width = xWidth(self.anchor); self.height = xHeight(self.anchor); self.container = document.createElement('div'); // holds the image and graphics canvas self.container.style.position = 'absolute'; self.container.id = self.name + "_container"; document.body.appendChild(self.container); xResizeTo(self.container, self.width, self.height); xMoveTo(self.container, xPageX(self.anchor), xPageY(self.anchor)); // align to anchor xClip(self.container, 0, self.width, self.height, 0); xShow(self.container); self.image = document.createElement('img'); self.image.style.position = 'absolute'; self.image.id = self.name + "_image"; self.container.appendChild(self.image); self.image.onload = self.reset; xResizeTo(self.image, self.width, self.height); xMoveTo(self.image, 0, 0); xShow(self.image); self.canvas = document.createElement('div'); self.canvas.style.position = 'absolute'; self.canvas.id = self.name + "_canvas"; self.container.appendChild(self.canvas); xResizeTo(self.canvas, self.width, self.height); xMoveTo(self.canvas, 0, 0); xClip(self.canvas, 0, self.width, self.height, 0); xShow(self.canvas); self.offsetx = xLeft(self.container); self.offsety = xTop(self.container); // event handling xAddEventListener(self.container, 'mousedown', self.mouseDown, true); xAddEventListener(self.container, 'mousemove', self.mouseMove, true); xAddEventListener(self.container, 'mouseup', self.mouseUp, true); xAddEventListener(self.container, 'mouseover', self.mouseEnter, true); xAddEventListener(self.container, 'mouseout', self.mouseExit, true); xEnableDrag(self.container, self.mouseDrag, self.mouseDrag, self.mouseDrag); // create the graphics object to use the canvas self.graphics = new jsGraphics(self.canvas.id); self.graphics.setColor(self.color); self.graphics.setStroke(self.thickness); } /** * Reposition the dBox object relative to the anchor. */ this.sync = function() { xMoveTo(self.container, xPageX(self.anchor), xPageY(self.anchor)); self.offsetx = xLeft(self.container); self.offsety = xTop(self.container); } /** * Turn drag panning on. */ this.dragOn = function() { self.box = self.line = self.poly = false; self.drag = true; } /** * Turn drag panning off. */ this.dragOff = function() { self.boxOff(); } /** * Turn box dragging on. */ this.boxOn = function() { self.box = true; self.line = self.drag = self.poly = false; } /** * Turn box dragging off. */ this.boxOff = function() { self.line = self.box = self.drag = self.poly = false; self.x1 = self.x2; self.y1 = self.y2; self.paint(); // user SHOULD provide this handler if (self.resetHandler) self.resetHandler(Math.min(self.x1, self.x2) - self.offsetx, Math.min(self.y1, self.y2) - self.offsety, Math.max(self.x1, self.x2) - self.offsetx, Math.max(self.y1, self.y2) - self.offsety); } /** * Turn line drawing on. */ this.lineOn = function() { self.line = true; self.box = self.drag = false; self.x = new Array(); self.y = new Array(); self.area = self.length = 0; self.paint(); } /** * Turn line drawing off. */ this.lineOff = function() { self.boxOff(); } /** * Turn polygon drawing on. */ this.polyOn = function() { self.poly = true; self.box = self.drag = self.line = false; self.x = new Array(); self.y = new Array(); self.area = self.length = 0; self.paint(); } /** * Turn polygon drawing off. */ this.polyOff = function() { self.boxOff(); } this.reset = function() { self.x1 = self.x2 = (self.width - 1) / 2 + self.offsetx; // center cursor self.y1 = self.y2 = (self.height - 1) / 2 + self.offsety; if (self.resetHandler) self.resetHandler(self.x1, self.y1, self.x1, self.y1); xMoveTo(self.image, 0, 0); // reposition the image self.sync(); self.paint(); self.waiting = false; if (self.busyMessage || self.busyImage) self.busyOff(); } /** * Change the image displayed in the dBox object. * @param {String} url for the new image */ this.setImage = function(url) { self.waiting = true; if (self.busyMessage || self.busyImage) self.busyOn(); self.x1 = self.x2 = (self.width - 1) / 2 + self.offsetx; // center cursor self.y1 = self.y2 = (self.height - 1) / 2 + self.offsety; self.image.src = url; } this.paint = function() { var x, y, w, h; self.graphics.clear(); if (self.drag) { xMoveTo(self.image, (self.x2 - self.x1), (self.y2 - self.y1)); } else { if (self.x1 == self.x2 && self.y1 == self.y2) { if (self.line) { for (var i = 1; i < self.x.length; i++) self.graphics.drawLine(self.x[i - 1] - self.offsetx, self.y[i - 1] - self.offsety, self.x[i] - self.offsetx, self.y[i] - self.offsety); } else if (self.poly) { for (var i = 1; i < self.x.length; i++) self.graphics.drawLine(self.x[i - 1] - self.offsetx, self.y[i - 1] - self.offsety, self.x[i] - self.offsetx, self.y[i] - self.offsety); if (self.x.length > 2) // close the polygon self.graphics.drawLine(self.x[0] - self.offsetx, self.y[0] - self.offsety, self.x[self.x.length - 1] - self.offsetx, self.y[self.x.length - 1] - self.offsety); } } else { if (self.box) { w = Math.abs(self.x1 - self.x2); h = Math.abs(self.y1 - self.y2); x = Math.min(self.x1, self.x2) - self.offsetx; // UL corner of box y = Math.min(self.y1, self.y2) - self.offsety; self.graphics.drawRect(x, y, w, h); } else if (self.line) { for (var i = 1; i < self.x.length; i++) self.graphics.drawLine(self.x[i - 1] - self.offsetx, self.y[i - 1] - self.offsety, self.x[i] - self.offsetx, self.y[i] - self.offsety); } else if (self.poly) { for (var i = 1; i < self.x.length; i++) self.graphics.drawLine(self.x[i - 1] - self.offsetx, self.y[i - 1] - self.offsety, self.x[i] - self.offsetx, self.y[i] - self.offsety); if (self.x.length > 2) // close the polygon self.graphics.drawLine(self.x[0] - self.offsetx, self.y[0] - self.offsety, self.x[self.x.length - 1] - self.offsetx, self.y[self.x.length - 1] - self.offsety); } } } self.graphics.paint(); } this.mouseDown = function(event) { var e = new xEvent(event); self.dragging = true; self.x1 = self.x2 = e.pageX; self.y1 = self.y2 = e.pageY; } this.mouseMove = function(event) { var e = new xEvent(event); var x = e.pageX; var y = e.pageY; if (self.dragging && !self.line && !self.poly) { self.x2 = x; self.y2 = y; if (!self.box && !self.drag) { self.x1 = self.x2; self.y1 = self.y2; } else self.paint(); } if (!self.waiting && self.verbose && self.mouseMoveHandler) self.mouseMoveHandler(x - self.offsetx, y - self.offsety); } this.mouseUp = function(event) { var e = new xEvent(event); var x1, x2, y1, y2; self.dragging = false; if (self.box || self.line || self.drag || self.poly) { self.x2 = e.pageX; self.y2 = e.pageY; if ((Math.abs(self.x1 - self.x2) <= self.jitter) || (Math.abs(self.y1 - self.y2) <= self.jitter)) { self.x2 = self.x1; self.y2 = self.y1; } if (self.drag) { if (self.setBoxHandler) { if (self.x1 == self.x2 && self.y1 == self.y2) { x1 = x2 = self.x1 - self.offsetx; y1 = y2 = self.y1 - self.offsety; } else { x1 = x2 = self.width / 2 - (self.x2 - self.x1); y1 = y2 = self.height / 2 - (self.y2 - self.y1); } self.setBoxHandler(x1, y1, x2, y2); } } else if (self.box) { if (self.setBoxHandler) { x1 = Math.min(self.x1, self.x2) - self.offsetx; x2 = Math.max(self.x1, self.x2) - self.offsetx; y1 = Math.min(self.y1, self.y2) - self.offsety; y2 = Math.max(self.y1, self.y2) - self.offsety; self.setBoxHandler(x1, y1, x2, y2); } } else if (self.line) { self.x.push(self.x2); self.y.push(self.y2); if (self.x.length > 1) { var d = distance(self.x[self.x.length - 1], self.y[self.y.length - 1], self.x[self.x.length - 2], self.y[self.y.length - 2]); self.length += d; if (self.measureHandler) self.measureHandler(d, self.length, self.x.length); // length self.paint(); } else if (self.x.length == 1) { if (self.measureHandler) self.measureHandler(0, 0, 1); // no length, 1 point } } else if (self.poly) { self.x.push(self.x2); self.y.push(self.y2); if (self.x.length > 2) { self.area = area(self.x, self.y); var d = distance(self.x[self.x.length - 1], self.y[self.y.length - 1], self.x[self.x.length - 2], self.y[self.y.length - 2]); self.length += d; if (self.areaHandler) self.areaHandler(self.area, d, self.length, self.x.length); // area and length self.paint(); } else if (self.x.length == 2) { self.length = distance(self.x[self.x.length - 1], self.y[self.y.length - 1], self.x[self.x.length - 2], self.y[self.y.length - 2]); if (self.areaHandler) self.areaHandler(0, self.length, self.length, self.x.length); // no area, but length, 2 points self.paint(); } else if (self.x.length == 1) { if (self.areaHandler) self.areaHandler(0, 0, 0, 1); // no area, no length, 1 point } } } else { self.x2 = self.x1; self.y2 = self.y1; if (self.setBoxHandler) { x1 = Math.min(self.x1, self.x2) - self.offsetx; x2 = Math.max(self.x1, self.x2) - self.offsetx; y1 = Math.min(self.y1, self.y2) - self.offsety; y2 = Math.max(self.y1, self.y2) - self.offsety; self.setBoxHandler(x1, y1, x2, y2); } } } this.mouseEnter = function(event) { self.anchor.style.cursor = self.container.style.cursor = self.cursor; if (self.verbose && self.mouseEnterHandler) self.mouseEnterHandler(); } this.mouseExit = function(event) { self.anchor.style.cursor = self.container.style.cursor = "default"; if (self.verbose && self.mouseExitHandler) self.mouseExitHandler(); } this.mouseDrag = function(event) { } /** * Method will cause dbox to display a busy message during the onload event handler of the main image. * This method can be called in one of three ways: * 1) <string> e.g., "Loading" will display a "Google"-style red box in the upper right corner of the page. * 2) <Image> e.g., Create an Image with the height, width and src properties set. The image will display in the center of the main image. * 3) <null> e.g., Calling this method with anything else will use a busy image using the default image set in the * following global variables: DEFAULT_BUSY_LOADING_IMG_SRC, DEFAULT_BUSY_LOADING_WIDTH, DEFAULT_BUSY_LOADING_HEIGHT. */ this.useBusyMessage = function(obj) { if (typeof obj == "string") { self.busyMessage = obj; } else if (typeof obj == "object" && obj.src && obj.src.search(/gif|png|jpg/i)) { self.busyImage = obj; } else { //use default image var image = new Image(); image.src = DEFAULT_BUSY_LOADING_IMG_SRC; image.width = DEFAULT_BUSY_LOADING_IMG_WIDTH; image.height = DEFAULT_BUSY_LOADING_IMG_HEIGHT; self.busyImage = image; } } } dBox.prototype.busyImage = null; dBox.prototype.busyMessage = null; /** * Method that controls the style of the busy message. This can be overridden. * When the busy message/image is displayed a disaabled zone is placed over the entire * page, essentially preeeventing a users interaction with the page during the loading * process. */ dBox.prototype.busyOn = function() { var disabledZone = xGetElementById("disabled_zone"); if (!disabledZone) { disabledZone = document.createElement('div'); disabledZone.setAttribute('id', 'disabled_zone'); disabledZone.style.position = "absolute"; disabledZone.style.zIndex = "1000"; disabledZone.style.left = "0px"; disabledZone.style.top = "0px"; disabledZone.style.width = "100%"; disabledZone.style.height = "100%"; document.body.appendChild(disabledZone); var messageZone = document.createElement('div'); messageZone.setAttribute('id', 'message_zone'); messageZone.style.position = "absolute"; disabledZone.appendChild(messageZone); if (this.busyMessage) { messageZone.style.top = "0px"; messageZone.style.right = "0px"; messageZone.style.padding = "4px"; messageZone.style.background = "red"; messageZone.style.color = "white"; messageZone.style.fontFamily = "Arial,Helvetica,sans-serif"; var text = document.createTextNode(this.busyMessage); messageZone.appendChild(text); } else { var image = document.createElement('img'); image.src = this.busyImage.src; image.width = this.busyImage.width; image.height = this.busyImage.height; xHide(disabledZone); messageZone.appendChild(image); xMoveTo(messageZone, ((this.width - image.width) / 2) + this.offsetx, ((this.height - image.height) / 2) + this.offsety); } xShow(disabledZone); } else { xShow(disabledZone); } } /** * Method to turn off busy image/message. It gets called after the image has loaded. */ dBox.prototype.busyOff = function() { xHide('disabled_zone'); }
|
||||||||
PREV NEXT | FRAMES NO FRAMES |