[Mapbender-commits] r1899 - trunk/mapbender/http/javascripts

svn_mapbender at osgeo.org svn_mapbender at osgeo.org
Thu Dec 6 08:49:27 EST 2007


Author: christoph
Date: 2007-12-06 08:49:26 -0500 (Thu, 06 Dec 2007)
New Revision: 1899

Added:
   trunk/mapbender/http/javascripts/geometry1.1.js
Log:
draft of a new geoemtry class

Added: trunk/mapbender/http/javascripts/geometry1.1.js
===================================================================
--- trunk/mapbender/http/javascripts/geometry1.1.js	                        (rev 0)
+++ trunk/mapbender/http/javascripts/geometry1.1.js	2007-12-06 13:49:26 UTC (rev 1899)
@@ -0,0 +1,1125 @@
+/* 
+* $Id: geometry.js 1750 2007-10-26 11:28:39Z christoph $
+* COPYRIGHT: (C) 2001 by ccgis. This program is free software under the GNU General Public
+* License (>=v2). Read the file gpl.txt that comes with Mapbender for details. 
+*/
+var Geometry = {
+	/**
+	 * A static class representing geometry types.
+	 */
+	Type : {
+		/**
+		 * excludes Linear ring as it is no valid geometry itself
+		 */
+		isValid : function (aGeometry) {
+			try {
+				if (aGeometry.getType() == this.point || 
+					aGeometry.getType() == this.line || 
+					aGeometry.getType() == this.polygon	|| 
+					aGeometry.getType() == this.multigeometry) 
+				{
+					return true;	
+				}
+			}
+			catch (e) {
+				var exc = new Mb_exception ("Geometry.Type.isValid: invalid geometry type for geometry: " + aGeometry + "; " + e);
+			}
+			return false;
+		},
+		
+		isPoint : function (aGeometry) {
+			try {
+				if (aGeometry.getType() == this.point) {
+					return true;	
+				}
+			}
+			catch (e) {
+				var exc = new Mb_exception ("Geometry.Type.isPoint: not a point: geometry: " + aGeometry + "; " + e);
+			}
+			return false;
+		},
+		
+		isLine : function (aGeometry) {
+			try {
+				if (aGeometry.getType() == this.line) {
+					return true;	
+				}
+			}
+			catch (e) {
+				var exc = new Mb_exception ("Geometry.Type.isLine: not a line: geometry: " + aGeometry + "; " + e);
+			}
+			return false;
+		},
+		
+		isPolygon : function (aGeometry) {
+			try {
+				if (aGeometry.getType() == this.polygon) {
+					return true;	
+				}
+			}
+			catch (e) {
+				var exc = new Mb_exception ("Geometry.Type.isPolygon: not a polygon: geometry: " + aGeometry + "; " + e);
+			}
+			return false;
+		},
+		
+		isLinearRing : function (aGeometry) {
+			try {
+				if (aGeometry.getType() == this.linearring) {
+					return true;	
+				}
+			}
+			catch (e) {
+				var exc = new Mb_exception ("Geometry.Type.isLinearRing: not a linear ring: geometry: " + aGeometry + "; " + e);
+			}
+			return false;
+		},
+		
+		point : "point",
+		line : "line",
+		linearring : "linearring",
+		polygon : "polygon",
+		multigeometry : "multigeometry"
+	},
+
+	/**
+	 * A class representing MultiGeometries. A MultiGeometry is a List containing
+	 * Point objects, Line objects, Polygon objects and MultiGeometry objects.
+	 */
+	MultiGeometry : function () {
+
+		this.getType = function () {
+			return Geometry.Type.multipolygon;
+		};
+
+		this.push = function (aGeometry) {
+			if (Geometry.Type.isValid(aGeometry)) {
+				this.add(aGeometry);
+				this.get(-1).onEmpty.register(function () {
+					that.isEmpty();
+				});
+				return true;
+			}
+			return false;
+		};
+		
+		this.pop = function () {
+			return this.remove(-1);
+		};
+		
+		this.remove = function (index) {
+			// TODO: parameter might be a geometry
+			if (this.del(index)) {
+				this.isEmpty();
+				return true;
+			}
+			return false;
+		};
+		
+		/**
+		 * Checks if the multi geometry is empty. If yes, the event onEmpty is triggered.
+		 */
+		this.isEmpty = function () {
+			if (this.count() === 0) {
+				this.onEmpty.trigger();
+				return true;
+			}
+			return false;
+		};
+		
+		this.onEmpty = new MapbenderEvent();
+
+		// this class extends List
+		this.list = [];
+	},
+	
+	/**
+	 * A class representing lines. A line is a List of Point objects
+	 */
+	Line : function () {
+
+		this.getType = function () {
+			return Geometry.Type.line;
+		};
+		
+		this.push = function (aPoint) {
+			if (Geometry.Type.isPoint(aPoint)) {
+				this.add(aPoint);
+				this.get(-1).onEmpty.register(function () {
+					that.rearrangeList();
+				});
+				return true;
+			}
+			return false;
+		};
+		
+		this.remove = function (index) {
+			// TODO: parameter might be a point
+			if (this.del(index)) {
+				return this.isEmpty();
+			}
+			return false;
+		};
+		
+		/**
+		 * remove all invalid points
+		 */
+		this.rearrangeList = function () {
+			var listLength = this.count();
+			for (var i = 0; i < listLength; i++) {
+				if (!this.get(i).isSet()) {
+					this.remove(i);
+					i -= 1;
+					listLength -= 1;
+				}
+			}
+		};
+		
+		this.isEmpty = function () {
+			if (this.count === 0) {
+				this.onEmpty.trigger();
+				return true;
+			}
+			return false;
+		};
+		
+		this.onEmpty = new MapbenderEvent();
+		
+		// this class extends List
+		this.list = [];
+	},
+
+	/**
+	 * A class representing a Polygon. A Polygon consists of an outer boundary (LinearRing)
+	 * and 0..n inner boundaries (InnerBoundaryArray).
+	 */
+	Polygon : function () {
+
+		this.getType = function () {
+			return Geometry.Type.polygon;
+		};
+
+		this.setOuterBoundary = function (aLinearRing) {
+			if (Geometry.Type.isLinearRing(aLinearRing)) {
+				outerBoundary = aLinearRing;
+				outerBoundary.onEmpty.register(function () {
+					that.isEmpty();
+				});
+				return true;
+			}
+			return false;
+		};
+
+		this.removeOuterBoundary = function () {
+			outerBoundary = null;
+			innerBoundaryArray = null;
+			return this.onEmpty.trigger();
+		};
+
+		this.appendInnerBoundary = function (aLinearRing) {
+			innerBoundaryArray.push(aLinearRing);
+		};
+
+		this.removeInnerBoundary = function (index) {
+			return innerBoundaryArray.remove(index);
+		};
+
+		this.isEmpty = function () {
+			if (!Geometry.Type.isLinearRing(outerBoundary)) {
+				return this.onEmpty.trigger();
+			}
+			return false;
+		};
+		
+		var outerBoundary;
+		var innerBoundaryArray = new Geometry.InnerBoundaryArray();
+	},
+	
+	/**
+	 * An InnerBoundaryArray is a List of LinearRing objects.
+	 */
+	InnerBoundaryArray : function () {
+
+		this.push = function (aLinearRing) {
+			if (Geometry.Type.isLinearRing(aLinearRing)) {
+				return this.add(aLinearRing);
+			}
+			return false;
+		};
+
+		this.remove = function (index) {
+			return this.del(index);
+		};
+		
+		this.onEmpty = new MapbenderEvent();
+
+		this.list = [];
+	},
+
+	/**
+	 * A Linear Ring is a List of Point objects. The last Point and the first Point must be identical.
+	 */
+	LinearRing : function () {
+		this.getType = function () {
+			return Geometry.Type.linearring;
+		};
+		
+		this.push = function (aPoint) {
+			if (Geometry.Type.isPoint(aPoint)) {
+				if (!this.isClosed()) {
+					this.add(aPoint);
+					// linear ring with at least 4 points (triangle!)
+					// AND
+					// last point equals first point
+					if (this.count() > 3 && this.get(0).equals(this.get(-1))) {
+						this.close();
+					}
+					this.get(-1).onEmpty.register(function () {
+						that.rearrangeList();
+					});
+					return true;
+				}
+				else {
+					var e = new Mb_warning("Geometry.LinearRing: push: Cannot add point; linear ring is already closed!");
+				}
+			}
+			return false;
+		};
+		
+		this.remove = function (index) {
+			var i = this.getIndex(index);
+			if (i !== false) {
+				// if the first or last point is being deleted,
+				// re-set the first or last point so that the 
+				// ring remains closed
+				if (i === 0 || i === this.getIndex(-1)) {
+					// remove the first point (list is automatically shifted)
+					this.del(0);
+					
+					// check if there are still points remaining
+					if (!this.isEmpty()) {
+						// add the new first point as the last point
+						this.list[this.count()-1] = this.list[0];
+					}
+					return true;
+				}
+				// if it is a point in the middle, just delete it (list is automatically shifted)
+				else {
+					return this.del(i);
+				}
+			}
+			return false;
+		};
+		
+		/**
+		 * Checks if the outer boundary is empty. If yes, the event onEmpty is triggered.
+		 */
+		this.isEmpty = function () {
+			if (this.count() === 0) {
+				this.onEmpty.trigger();
+				return true;
+			}
+			return false;
+		};
+		
+		/**
+		 * remove all invalid points
+		 */
+		this.rearrangeList = function () {
+			var listLength = this.count();
+			for (var i = 0; i < listLength; i++) {
+				if (!this.get(i).isSet()) {
+					this.remove(i);
+					i -= 1;
+					listLength -= 1;
+				}
+			}
+		};
+		
+		this.close = function () {
+			// linear ring with at least 4 points (triangle!)
+			// AND
+			// last point equals first point
+			if (this.count() > 3 && this.get(0).equals(this.get(-1))) {
+				closed = true;
+				return closed;
+			}
+			// linear with at least 3 points (triangle!)
+			// AND
+			// last point DOESNT equal first point
+			else if (this.count() > 2 && !this.get(0).equals(this.get(-1))) {
+				var newPoint = new Geometry.Point(this.get(0).x, this.get(0).y);
+				// push will return to close to set closed to true
+				this.push(newPoint);
+			}
+			else {
+				var e = new Mb_warning("Geometry.LinearRing: close: cannot close linear ring, must have at least 3 points (triangle)");
+			}
+			return false;	
+		};
+		
+		this.isClosed = function () {
+			return closed;
+		};
+		
+		this.onEmpty = new MapbenderEvent();
+		
+		/**
+		 * if the first point and the last point match, and the ring has at least 4 points (triangle!)
+		 * closed is set to true via close
+		 */
+		var closed = false;
+
+		this.list = [];
+	},
+
+	/**
+	 * A Point consists of an x- and a y-coordinate.
+	 */
+	Point : function () {
+		this.getType = function () {
+			return Geometry.Type.point;
+		};
+		
+		this.remove = function () {
+			onEmpty.trigger();
+			this.x = null;
+			this.y = null;
+			this.onEmpty = null;
+		};
+		
+	 	/**
+	 	 * x value of the {@link Point}
+	 	 *
+		 * @type Float
+		 */
+		this.x = parseFloat(x);
+
+	 	/**
+	 	 * y value of the {@link Point}
+		 *
+		 * @type Float
+		 */
+		this.y = parseFloat(y);	
+		
+		this.isSet = function () {
+			return (this.x !== null && this.y !== null);
+		};	
+		
+		this.onEmpty = new MapbenderEvent();
+	}
+};
+
+Geometry.GeometryArray.prototype = new List();
+Geometry.MultiGeometry.prototype = new List();
+Geometry.InnerBoundary.prototype = new List();
+Geometry.LinearRing.prototype = new List();
+Geometry.Line.prototype = new List();
+	
+/**
+ * computes the distance between a {@link Point} p and this {@link Point}
+ *
+ * @param {Point} p the distance between this {@link Point} and the {@link Point} p is computed.
+ * @return {Float} the distance between the two {@link Point} objects.
+ */
+Geometry.Point.prototype.dist = function(p){
+	return Math.sqrt(Math.pow(this.y-p.y,2) + Math.pow(this.x-p.x,2)) ;
+};
+/**
+ * checks if the coordinates of this {@link Point} match the coordinates of a {@link Point} p
+ *
+ * @param {Point} p 
+ * @return {Boolean} true if the two points are equal; elso false
+ */
+Geometry.Point.prototype.equals = function(p){
+	if (this.x == p.x && this.y == p.y) {return true;}
+	return false;
+};
+/**
+ * subtracts a {@link Point} p from this {@link Point}
+ *
+ * @param {Point} p 
+ * @return a new {@link Point} with the difference of the two points
+ */
+Geometry.Point.prototype.minus = function(p){
+	return new Point(this.x-p.x, this.y-p.y);
+};
+/**
+ * adds this {@link Point} to a {@link Point} p
+ *
+ * @param {Point} p 
+ * @return a new {@link Point} with the sum of the two points
+ */
+Geometry.Point.prototype.plus = function(p){
+	return new Point(this.x+p.x, this.y+p.y);
+};
+/**
+ * divides this {@link Point} by a scalar c
+ *
+ * @param {Float} c divisor
+ * @return a new {@link Point} divided by c
+ */
+Geometry.Point.prototype.dividedBy = function(c){
+	if (c !== 0) {
+		return new Point(this.x/c, this.y/c);
+	}
+	var e = new Mb_exception("Point.dividedBy: Division by zero");
+	return false;
+};
+/**
+ * multiplies this {@link Point} by a scalar c
+ *
+ * @param {Float} c factor
+ * @return a new {@link Point} multiplied by c
+ */
+Geometry.Point.prototype.times = function(c){
+	return new Point(this.x*c, this.y*c);
+};
+/**
+ * rounds the coordinates to numOfDigits digits
+ *
+ * @param numOfDigits the coordinate will be rounded to numOfDigits digits
+ * @return a new {@link Point} rounded to numOfDigits digits
+ * @type Point
+ */
+Geometry.Point.prototype.round = function(numOfDigits){
+	return new Point(roundToDigits(this.x, numOfDigits), roundToDigits(this.y, numOfDigits));
+};
+/**
+ * @returns a {String} representation of this Point
+ * @type String
+ */
+Geometry.Point.prototype.toString = function(){
+	return "(" + this.x + ", " + this.y + ")";
+};
+
+
+
+/**
+ * @class an array of elements, each consisting of a name/value pair
+ *
+ * @ constructor
+ */
+function Wfs_element(){
+
+	/**
+	 * returns the number of elements of this {@link Wfs_element} object.
+	 *
+	 * @return the number of elements
+	 * @type Integer
+	 */
+	this.count = function(){
+		return name.length;
+	};
+
+	/**
+	 * returns the name of the element at index i.
+	 *
+	 * @param {Integer} i index
+	 * @return the name
+	 * @type String
+	 */
+	this.getName = function(i){ 
+		if (isValidElementIndex(i)) {return name[i];}
+		return false;
+	};
+	
+	/**
+	 * returns the value of the element at index i.
+	 *
+	 * @param {Integer} i index
+	 * @return the value
+	 */
+	this.getValue = function(i){ 
+		if (isValidElementIndex(i)) {return value[i];}
+		return false;
+	};
+
+	/**
+	 * appends a new element with a given name. If an element with this name exists, it is overwritten.
+	 *
+	 * @param {String} aName the name of the new element
+	 * @param {String} aValue the value of the new element
+	 */
+	this.setElement = function(aName, aValue){ 
+		var i = this.getElementIndexByName(aName);
+		if (i === false) {i = this.count();}
+		name[i] = aName;
+		value[i] = aValue;
+	};
+
+	/**
+	 * checks if an index is valid
+	 *
+	 * @private
+	 * @param {Integer} i an index
+	 * @return true if the index is valid; otherwise false
+	 * @type Boolean
+	 */
+	var isValidElementIndex = function(i){ 
+		if (i>=0 && i<name.length) {return true;}
+		var e = new Mb_exception("class Wfs_element: function isValidElementIndex: illegal element index");
+		return false;
+	};
+	
+	var name  = [];
+	var value = [];
+}
+
+/**
+ * gets the index of the element with a given name.
+ *
+ * @param {String} elementName a name
+ * @return the index of the element; if no element with this name exists, false
+ * @type Integer, Boolean
+ */
+Wfs_element.prototype.getElementIndexByName = function(elementName){
+	for (var j = 0 ; j < this.count() ; j++){
+		if (this.getName(j) == elementName) {return j;}
+	}
+	return false;
+};
+
+/**
+ * gets the value of the element with a given name.
+ *
+ * @param {String} elementName a name
+ * @return the value of the element; if no element with this name exists, false
+ * @type String, Boolean
+ */
+Wfs_element.prototype.getElementValueByName = function(elementName){
+	var i = this.getElementIndexByName(elementName);
+	if (i === false) {return false;}
+	return this.getValue(i);
+};
+
+
+/**
+ * @class a {@link Canvas} contains a {@link DivTag} that holds graphics rendered by {@link jsGraphics}
+ *
+ * @constructor
+ * @requires DivTag
+ * @requires jsGraphics
+ * @requires GeometryArray
+ * @requires MultiGeometry
+ * @requires Geometry
+ * @param {String} aMapFrame name of the target mapframe
+ * @param {String} aTagName name of the target div tag
+ * @param {String} aStyle style of the div tag
+ * @param {Integer} aLineWidth the line width of the jsGraphics output
+ */
+function Canvas(aMapframe, aTagName, aStyle, aLineWidth) {
+	
+	/**
+	 * draws the geometry of the canvas
+	 *
+	 * @param {String} t geometry type (@see GeomType)
+	 * @param {MultiGeometry} g a MultiGeometry object
+	 * @param {String} col a color
+	 * @private
+	 */
+ 	this.drawGeometry = function(t,g,col){ 
+		var mapObjInd = getMapObjIndexByName(mapframe);
+		width = mb_mapObj[mapObjInd].width;
+		height = mb_mapObj[mapObjInd].height;
+		for(var i=0; i < g.count(); i++){
+			if(t==geomType.point) {
+				var p = realToMap(mapframe,g.get(i).get(0));
+				if (p.x + diameter < mb_mapObj[mapObjInd].width && p.x - diameter > 0 &&
+					p.y + diameter < mb_mapObj[mapObjInd].height && p.y - diameter > 0) {
+					drawCircle(p.x-1, p.y-1, diameter,col);
+				}
+			}
+			else if(t==geomType.line || t==geomType.polygon) {
+				for (var j=0; j<g.get(i).count()-1; j++) {
+					var pq = calculateVisibleDash(realToMap(mapframe,g.get(i).get(j)), realToMap(mapframe,g.get(i).get(j+1)), width, height);
+					if (pq) {
+						drawLine([pq[0].x-1, pq[1].x-1], [pq[0].y-1, pq[1].y-1], col);
+					}
+				}
+			}
+			else {
+				var e = new Mb_exception("class Canvas: function drawGeometry: unknown geomType " + t);
+			}
+		}
+	};
+	
+	/**
+	 * checks if the MultiGeometry's bounding box width and height is smaller than minWidth
+	 *
+	 * @private
+	 * @param {MultiGeometry} g a MultiGeometry object
+	 */
+	this.isTooSmall = function(g){
+		var tmp = g.getBBox();
+		var min = realToMap(mapframe,tmp[0]);
+		var max = realToMap(mapframe,tmp[1]);
+		if((Math.abs(max.x - min.x) < minWidth) && (Math.abs(max.y - min.y) < minWidth)) {
+			return true;
+		}
+		return false;
+	};
+	
+	/**
+	 * gets the jsGraphics.
+	 *
+	 * @private
+	 * @return the jsGraphics
+	 * @type jsGraphics
+	 */
+	this.getCanvas = function(){
+		return canvas;
+	};
+	
+	/**
+	 * draws a circle with {@link jsGraphics}.
+	 *
+	 * @private
+	 * @param {Float} x x value of the center
+	 * @param {Float} y y value of the center
+	 * @param {Float} diameter diameter of the circle
+	 * @param {String} color the color of the circle in hex format
+	 */
+	var drawCircle = function(x, y, diameter, color) {
+		canvas.setColor(color);
+		canvas.drawEllipse(x-diameter/2,y-diameter/2,diameter,diameter);
+	};
+
+	/**
+	 * draws a polyline with {@link jsGraphics}.
+	 *
+	 * @private
+	 * @param {Array} x_array array of x values
+	 * @param {Array} y_array array of y values
+	 * @param {String} color the color of the polyline in hex format
+	 */
+	var drawLine = function(x_array, y_array, color) {
+		canvas.setColor(color);
+		canvas.drawPolyline(x_array, y_array);
+	};
+
+	/**
+	 * This is the {@link DivTag} that contains the output by {@link jsGraphics}.
+	 * 
+	 * @type DivTag
+	 */
+	this.canvasDivTag = new DivTag(aTagName, aMapframe, aStyle);
+
+	var that = this;
+
+	var diameter = 8;
+	var minWidth = 8;
+	var lineWidth = aLineWidth;
+	var mapframe = aMapframe;
+	var style = aStyle;
+	var canvas = new jsGraphics(aTagName, window.frames[mapframe]);
+	canvas.setStroke(lineWidth);
+	mb_registerPanSubElement(aTagName);
+}
+
+/**
+ * cleans the canvas by emptying the canvas {@link DivTag}.
+ */
+Canvas.prototype.clean = function () {
+	this.canvasDivTag.clean();
+};
+
+/**
+ * paints all geometries.
+ *
+ * @param {GeometryArray} gA the geometries that will be drawn
+ */
+Canvas.prototype.paint = function(gA) {
+	for (var q = 0; q < gA.count(); q++) {
+		var m = gA.get(q);
+		var t = m.geomType;
+		var col = m.color;
+		if (t == geomType.point) {
+			this.drawGeometry(t,m,col);
+		}
+		else {
+			if (this.isTooSmall(m)){
+				var newMember = new MultiGeometry(geomType.point);
+				newMember.addGeometry();
+				newMember.get(-1).addPoint(m.getCenter());
+				this.drawGeometry(geomType.point,newMember,col);
+			}
+			else{
+				if(t == geomType.line) {this.drawGeometry(t,m, col);}
+				else if(t == geomType.polygon) {this.drawGeometry(t,m,col);}
+				else {
+					var e = new Mb_exception("class Canvas: function paint: unknown geomType" + t);				
+				}
+			}
+		}
+	}
+	this.getCanvas().paint();
+};
+
+/**
+ * @class a {@link Highlight} object is {@link jsGraphics} rendering of a {@link GeometryArray} in various mapframes.
+ *
+ * @constructor
+ * @requires Canvas
+ * @requires GeometryArray
+ * @param {Array} aTargetArray an array of Strings referring to mapframes
+ * @param {String} aTagName the name of the div tags
+ * @param {Object} aStyle the style of the div tags
+ * @param {Integer} the line width of the jsGraphics lines
+ */
+function Highlight(aTargetArray, aTagName, aStyle, aLineWidth) {
+	/**
+	 * removes a {@link MultiGeometry} object from the geometry Array
+	 *
+	 * @param {MultiGeometry} m a MultiGeometry
+	 * @param {String} color a color
+	 */	
+	this.del = function(m, color) {
+		var a = gA.findMultiGeometry(m);
+		var del = false;
+		for (var i=0; i<a.length && del === false; i++) {
+			if (gA.get(a[i]).color == color) {
+				gA.del(a[i]);
+				del = true;
+			}
+		}
+		this.paint();
+	};
+
+	/**
+	 * adds a {@link MultiGeometry} object to the geometry Array
+	 *
+	 * @param {MultiGeometry} m a MultiGeometry
+	 * @param {String} color the color of the highlight
+	 */	
+	this.add = function(m, color) {
+		gA.addCopy(m);
+		if (typeof(color) != 'undefined') {gA.get(-1).color = color;} 
+		else {gA.get(-1).color = lineColor;}
+		this.paint();
+	};
+	
+	/**
+	 * removes all MultiGeometries.
+	 *
+	 */	
+	this.clean = function() {
+		if (gA.count() > 0) {
+			gA = new GeometryArray();
+			this.paint();
+		}
+	};
+
+	/**
+	 * displays the highlight
+	 *
+	 */	
+	this.paint = function() {
+		for (var i=0; i < canvas.length; i++) {
+			if (typeof(canvas[i]) == "object") {canvas[i].clean();}
+		}
+		for (var i=0; i<targets.length; i++){
+			if (typeof(canvas[i]) == 'undefined') {
+				canvas[i] = new Canvas(targets[i], tagname, style, lineWidth);
+			}
+			canvas[i].paint(gA);
+		}
+	};
+
+	var lineWidth = aLineWidth;
+	var tagname = 'mod_gaz_draw'+aTagName;
+	var style = aStyle;
+	var targets = aTargetArray; 
+	var canvas = []; 
+	var gA = new GeometryArray(); 
+	var lineColor = "#ff0000";
+	this.paint();
+}
+
+// ----------------------------------------------------------------------------------------------------
+// Snapping
+// ----------------------------------------------------------------------------------------------------
+/**
+ * @class a {@link Snapping} object stores is {@link jsGraphics} rendering of a {@link GeometryArray} in various mapframes.
+ *
+ * @constructor
+ * @requires GeometryArray
+ * @requires Highlight
+ * @param {String} aTarget name of the mapframe where snapping occurs
+ * @param {String} aTolerance Snapping is activated if the mouse is 
+ *                 within aTolerance pixel distance to the reference point.
+ * @param {String} aColor apparently deprecated?
+ * @param {Integer} aZIndex the z-Index of the {@link jsGraphics} generated by {@link Highlight}.
+ */
+function Snapping(aTarget, aTolerance, aColor, aZIndex){
+
+	/**
+	 * draws a circle to highlight the snapped point.
+	 * 
+	 * @param {Point} center the snapped point.
+	 * @param {Integer} radius radius of the circular highlight.
+	 */
+	this.draw = function(center,radius){ 
+		mG = new MultiGeometry(geomType.point);
+		mG.addGeometry();
+		mG.get(-1).addPoint(center);
+		highlight.add(mG);
+	};
+	this.getTolerance = function() {
+		return tolerance;
+	};
+	this.getTarget = function() {
+		return target;
+	};
+	this.cleanHighlight = function() {
+		return highlight.clean();
+	};
+	this.addPoint = function(aPoint) {
+		coord.push(aPoint);
+	};
+	this.getPointCount = function() {
+		return coord.length;
+	};
+	this.getPoint = function(i) {
+		return coord[i];
+	};
+	this.resetPoints = function() {
+		coord = [];
+	};
+	this.getNearestNeighbour = function(){
+		if (min_i != -1) {return this.getPoint(min_i);}
+		return false;
+	};
+	this.setIndexOfNearestNeighbour = function(i){
+		min_i = i;
+	};
+	this.resetIndexOfNearestNeighbour = function(){
+		min_i = -1;
+	};
+	
+	/**
+	 * @private
+	 */
+	var tolerance = (typeof(aTolerance) == 'undefined') ? 10 : aTolerance;
+
+	/**
+	 * @private
+	 */
+	var zIndex = (typeof(aZIndex) == 'undefined') ? 50 : aZIndex;
+
+	/**
+	 * @private
+	 */
+	var coord = []; 
+
+	/**
+	 * @private
+	 */
+	var min_i = -1;
+
+	/**
+	 * @private
+	 */
+	var target = aTarget;
+
+	/**
+	 * @private
+	 */
+	var lineWidth = 2;
+
+	/**
+	 * @private
+	 */
+	var style = {"position":"absolute", "top":"0px", "left":"0px", "z-index":zIndex};
+
+	/**
+	 * @private
+	 */
+	var highlight = new Highlight([target], "snapping"+Math.round(Math.random()*Math.pow(10,10)), style, lineWidth);
+}
+
+Snapping.prototype.check = function(currPoint){
+	var minDist = false;
+	
+	for (var i = 0 ; i < this.getPointCount() ; i++) {
+
+		var currDist = currPoint.dist(realToMap(this.getTarget(), this.getPoint(i)));
+		if (minDist === false || currDist < minDist) {
+			minDist = currDist;
+			if (minDist < this.getTolerance()) {this.setIndexOfNearestNeighbour(i);}
+		}
+	}
+	if (this.getPointCount() > 0 && minDist > this.getTolerance()) {
+		this.resetIndexOfNearestNeighbour();
+	}
+	this.cleanHighlight();
+	if (this.isSnapped()) {
+		this.draw(this.getNearestNeighbour(), this.getTolerance());
+	}
+};
+
+/**
+ * Stores the points which will have the snapping property. 
+ * 
+ * @param {GeometryArray} geom all points of geom will be stored. May also be a 
+ *                             {@link MultiGeometry} or {@link Geometry}.
+ * @param {Point} point this point is excluded. Useful when moving a point of a 
+ *                      geometry; you don't want to snap against the point you
+ *                      move. Optional.
+ */
+Snapping.prototype.store = function(geom, point){
+	this.resetPoints();
+	this.resetIndexOfNearestNeighbour();
+
+	for (var i = 0 ; i < geom.count(); i++){
+		if (geom.name == nameGeometryArray || geom.name == nameMultiGeometry){
+			for (var j = 0 ; j < geom.get(i).count() ; j++){
+				if (geom.get(i).name == nameMultiGeometry){
+					for (var k = 0 ; k < geom.get(i).get(j).count() ; k++){
+						if ((geom.get(i).get(j).isComplete() === true && typeof(point) == 'undefined') || (typeof(point) != 'undefined' && !geom.get(i).get(j).get(k).equals(point))){
+							this.add(geom.getPoint(i, j, k));
+						}
+					}
+				}
+				else {
+					if ((geom.get(i).isComplete() === true && typeof(point) == 'undefined') || (typeof(point) != 'undefined' && !geom.get(i).get(j).get(k).equals(point))){
+						this.add(geom.getPoint(i, j));
+					}
+				}
+			}
+		}
+		else {
+			if (typeof(point) != 'undefined' && !geom.get(i).get(j).get(k).equals(point)){
+				this.add(geom.get(i));
+			}
+		}
+	}
+};
+
+/**
+ * Determines whether a point is within snapping distance to the mouse cursor
+ * 
+ * @return true if a point is within snapping distance; else false
+ * @type Boolean
+ */
+Snapping.prototype.isSnapped = function(){ 
+	if (this.getNearestNeighbour() !== false) {return true;}
+	return false;
+};
+
+/**
+ * Returns the point that is within snapping distance and closest to the mouse cursor.
+ * 
+ * @return the point (if there is any); else false
+ * @type Point
+ */
+Snapping.prototype.getSnappedPoint = function(){
+	return this.getNearestNeighbour();
+};
+
+/**
+ * Adds the point to the stored points with snapping property.
+ * 
+ * @param {Point} point which receives snapping property.
+ */
+Snapping.prototype.add = function(aPoint){ 
+	this.addPoint(aPoint);
+};
+
+/**
+ * Removes the highlight.
+ */
+Snapping.prototype.clean = function(){
+	this.cleanHighlight();
+};
+
+
+
+// ----------------------------------------------------------------------------------------------------
+// misc. functions
+// ----------------------------------------------------------------------------------------------------
+
+/**
+ * @ignore
+ */
+function calculateVisibleDash (p0, p1, width, height) {
+	if (p0.x > p1.x) {var p_temp = p0; p0 = p1; p1 = p_temp; p_temp = null;}
+	var p = p0; var q = p1; var m; var ix; var iy;
+	if (p1.x != p0.x) {
+		m = -(p1.y-p0.y)/(p1.x-p0.x); 
+		if (p0.x < width && p1.x > 0 && !(p0.y < 0 && p1.y < 0) && !(p0.y > height && p1.y > height) ) {
+			if (p0.x < 0) {
+				iy = p0.y - m*(0-p0.x);
+				if (iy > 0 && iy < height) {p = new Point(0, iy);}
+				else if (iy > height) {
+				    ix = p0.x+((p0.y - height)/m);
+				    if (ix > 0 && ix < width) {p = new Point(ix, height);} else {return false;}
+				}
+				else if (iy < 0) {
+				    ix = p0.x+(p0.y/m);
+				    if (ix > 0 && ix < width) {p = new Point(ix, 0);} else {return false;}
+				}
+				else {return false;}
+			}
+			else if (p0.y >= 0 && p0.y <= height) {p = p0;}
+			else if (p0.y < 0) {
+			    ix = p0.x+(p0.y/m);
+			    if (ix > 0 && ix < width) {p = new Point(ix, 0);} else {return false;}
+			}
+			else if (p0.y > height && m > 0) {
+			    ix = p0.x+((p0.y - height)/m);
+			    if (ix > 0 && ix < width) {p = new Point(ix, height);} else {return false;}
+			}
+			else {return false;}
+			if (p1.x > width) {
+				iy = p1.y - m*(width-p1.x);
+				if (iy > 0 && iy < height) {q = new Point(width, iy);}
+				else if (iy < 0) {
+				    ix = p0.x+(p0.y/m);
+				    if (ix > 0 && ix < width) {q = new Point(ix, 0);} else {return false;}
+				}
+				else if (iy > height) {
+				    ix = p0.x+((p0.y - height)/m);
+				    if (ix > 0 && ix < width) {q = new Point(ix, height);} else {return false;}
+				}
+				else {return false;}
+			}
+			else if (p1.y >= 0 && p1.y <= height) {q = p1;}
+			else if (p1.y < 0) {
+			    ix = p1.x+(p1.y/m);
+			    if (ix > 0 && ix < width) {q = new Point(ix, 0);} else {return false;}
+			}
+			else if (p1.y > height) {
+			    ix = p1.x+((p1.y- height)/m);
+			    if (ix > 0 && ix < width) {q = new Point(ix, height);} else {return false;}
+			}
+		}
+		else {return false;}
+	}
+	else {
+		if (!(p0.y < 0 && p1.y < 0) && !(p0.y > height && p1.y > height)) {
+			if (p0.y < 0) {p = new Point(p0.x, 0);}
+			else if (p0.y > height) {p = new Point(p0.x, height);}
+			else {p = p0;}
+			if (p1.y < 0) {q = new Point(p0.x, 0);}
+			else if (p1.y > height) {q = new Point(p0.x, height);}
+			else {q = p1;}
+		}
+		else {return false;}
+	}
+	return [new Point(Math.round(q.x), Math.round(q.y)), new Point(Math.round(p.x), Math.round(p.y))];
+}
+
+/**
+ * @ignore
+ */
+function objString (a){
+	var z = "";
+	
+	for (attr in a) {
+		var b = a[attr];
+		if (typeof(b) == "object") {z += objString(b);}
+		else {z += attr + " " + b + "\n";alert(attr + " " + b);}
+	}	
+	return z;
+}



More information about the Mapbender_commits mailing list