geometry.js

Summary

No overview generated for 'geometry.js'


Class Summary
Canvas a Canvas contains a DivTag that holds graphics rendered by jsGraphics
Geometry a Geometry is a List of Point objects.
GeometryArray a GeometryArray is a List of MultiGeometry objects
GeomType A class representing geometry types "polygon", "line" and "point".
Highlight a Highlight object is jsGraphics rendering of a GeometryArray in various mapframes.
MultiGeometry a MultiGeometry is a List of Geometry objects
Snapping a Snapping object stores is jsGraphics rendering of a GeometryArray in various mapframes.
Wfs_element an array of elements, each consisting of a name/value pair

/* 
* $Id: geometry.js 1388 2007-05-30 12:09:34Z 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. 
*/
// http://www.mapbender.org/index.php/GeometryArray.js


var nameGeometryArray = "GeometryArray";
var nameMultiGeometry = "MultiGeometry";
var nameGeometry = "Geometry";


/**
 * @class A class representing geometry types "polygon", "line" and "point".
 *
 * @constructor
 */
function GeomType(){
	/**
	 * An identifier for polygons. If you want to check if a {@link MultiGeometry}
	 * or {@link Geometry} is a polygon, write
	 * if (someGeometry.geomType == geomType.polygon
	 * 
	 * @type String
	 */
	this.polygon = "polygon";

	/**
	 * An identifier for polygons. If you want to check if a {@link MultiGeometry}
	 * or {@link Geometry} is a line, write
	 * if (someGeometry.geomType == geomType.line
	 * 
	 * @type String
	 */
	this.line = "line";

	/**
	 * An identifier for polygons. If you want to check if a {@link MultiGeometry}
	 * or {@link Geometry} is a point, write
	 * if (someGeometry.geomType == geomType.point
	 * 
	 * @type String
	 */
	this.point = "point";
}
var geomType = new GeomType();


/**
 * @class a {@link GeometryArray} is a {@link List} of {@link MultiGeometry} objects
 *
 * @ extends List
 * @ requires MultiGeometry 
 * @ requires Geometry
 * @ requires Point
 * @ constructor
 */
function GeometryArray(){

	/*
	 * creates a new, empty Multigeometry and adds it to this GeometryArray
	 *
	 * @param {String} geomType a {@link GeomType}
	 */
	this.addMember = function(geomType){
		this.add(new MultiGeometry(geomType));
	};
	
	/**
	 * @ignore
	 */
	this.name = nameGeometryArray;
	
	/**
	 * A {@link List} of {@link MultiGeometry} objects.
	 * 
	 * @type List
	 */
	this.list = [];
}

GeometryArray.prototype = new List();
	
/**
 * gets the j-th {@link Geometry} object of the i-th {@link MultiGeometry} object
 *
 * @param {Integer} i index of the MultiGeometry
 * @param {Integer} j index of the Geometry
 * @type Geometry 
 */
GeometryArray.prototype.getGeometry = function(i,j){
	return this.get(i).get(j);
};

/**
 * gets the k-th Point of the j-th {@link Geometry} object of the i-th {@link MultiGeometry} object
 *
 * @param {Integer} i index of the MultiGeometry
 * @param {Integer} j index of the Geometry
 * @param {Integer} k index of the Point
 * @type Point
 * @returns the Point object at the given indices
 */
GeometryArray.prototype.getPoint = function(i,j,k){
	return this.get(i).get(j).get(k);
};

/**
 * gets an Array of indices; the {@link MultiGeometry} objects at these indices are equal to geom
 *
 * @type Integer[]
 * @param {MultiGeometry} geom 
 * @returns an Array of indices
 */
GeometryArray.prototype.findMultiGeometry = function(geom) {
	var a = [];
	for (var i=0; i < this.count(); i++) {
		if (this.get(i).equals(geom)) {a.push(i);}
	}
	return a;
};
	
/**
 * deletes the j-th {@link Geometry} object of the i-th {@link MultiGeometry} object
 *
 * @param {Integer} i index of the MultiGeometry
 * @param {Integer} j index of the Geometry
 */
GeometryArray.prototype.delGeometry = function(i,j){
	if (this.get(i).del(j) === false) {this.del(i);}
};
	
/**
 * deletes the k-th {@link Point} of the j-th {@link Geometry} object of the i-th {@link MultiGeometry} object
 *
 * @param {Integer} i index of the MultiGeometry
 * @param {Integer} j index of the Geometry
 * @param {Integer} k index of the Point
 */
GeometryArray.prototype.delPoint = function (i,j,k){
	var res = this.get(i).delPoint(j,k);
	if (res === false) {this.del(i);}
};
	
/**
 * closes the current {@link MultiGeometry}. Calls method close of the {@link Geometry} class.
 *
 */
GeometryArray.prototype.close = function(){
	if (!this.get(-1).get(-1).close()) {
		this.delGeometry(-1, -1);
	}
	else {
		if (this.get(-1).get(-1).count() === 0) {this.get(-1).del(-1);}
		if (this.get(-1).count() === 0) {this.del(-1);}
	}
	
};

/**
 * deletes all {@link Point} objects of this {@link GeometryArray} that equal point
 *
 * @param {Point} point
 */
GeometryArray.prototype.delAllPointsLike = function(point){
	var finished = false;
	while (finished === false){
		finished = true;
		for (var i = 0 ; finished === true && i < this.count() ; i++){
			for (var j = 0 ; finished === true && j < this.get(i).count() ; j++){
				for (var k = 0 ; finished === true && k < this.get(i).get(j).count() ; k++){
					if (this.getPoint(i,j,k).equals(point)){
						this.delPoint(i,j,k);
						finished = false;
					}
				}
			} 
		}
	}
};
	
/**
 * updates all {@link Point} objects of this {@link GeometryArray} that equal oldP to newP
 *
 * @param {Point} oldP
 * @param {Point} newP
 */
GeometryArray.prototype.updateAllPointsLike = function(oldP, newP){
	for (var i = 0; i < this.count(); i++){
		this.get(i).updateAllPointsLike(oldP, newP);
	}
};
	
/**
 * @class a MultiGeometry is a List of Geometry objects
 *
 * @ extends List
 * @ requires Geometry
 * @ requires Point
 * @ constructor
 * @ param {String} geomType a geomType
 */
function MultiGeometry(geomType){

	/*
	 * creates a new, empty {@link Geometry} object and adds it to this {@link MultiGeometry}
	 *
	 */
	this.addGeometry = function(){
		this.add(new Geometry(this.geomType));
	};
	
	/**
	 * deletes the {@link Geometry} object at index i; -1 refers to the last {@link Geometry object in the list
	 * overwrites the del function of {@link List}.
	 *
	 * @param {Integer} i index
	 */
	this.del = function(i){
		i = this.getIndex(i);
		if (i !== false){
			var tmpLength = this.count() - 1;
			for (var z = i; z < tmpLength ; z ++){
				this.list[z] = this.list[z+1];
				e[z] = e[z+1];
			}
			this.list.length -= 1;
			if (this.list.length === 0) {return false;}
		}
		return true;
	};

	this.list = [];
	this.e = new Wfs_element();
	this.geomType = geomType;
	this.name = nameMultiGeometry;
}

MultiGeometry.prototype = new List();

/**
 * updates all {@link Point} objects of this {@link MultiGeometry} that equal oldP to newP
 *
 * @param {Point} oldP
 * @param {Point} newP
 */
MultiGeometry.prototype.updateAllPointsLike = function(oldP, newP){
	for (var i = 0; i < this.count(); i++) {
		this.get(i).updateAllPointsLike(oldP, newP);
	}
};

/**
 * gets the bounding box of this {@link MultiGeometry}
 *
 * @return the bounding box
 * @type Array of two Point objects
 */
MultiGeometry.prototype.getBBox = function(){
	var q = this.get(0).get(0);
	var min = cloneObject(q);
	var max = cloneObject(q);
	for(var i=0; i<this.count();i++){
		var pos = this.get(i).getBBox();
		if (pos[0].x < min.x) {min.x = pos[0].x;}
		if (pos[1].x > max.x) {max.x = pos[1].x;}
		if (pos[1].y > max.y) {max.y = pos[1].y;}
		if (pos[0].y < min.y) {min.y = pos[0].y;}
	}
	return [min, max];
};
/**
 * gets the center of the bounding box of this {@link MultiGeometry}.
 *
 * @return the center of the bounding box
 * @type Point
 */
MultiGeometry.prototype.getCenter = function(){
	var tmp = this.getBBox();
	var x = parseFloat(tmp[0].x) + parseFloat((tmp[1].x - tmp[0].x)/2);
	var y = parseFloat(tmp[0].y) + parseFloat((tmp[1].y - tmp[0].y)/2);
	return new Point(x,y);
};

/**
 * gets the total number of {@link Point} objects of this {@link MultiGeometry}.
 *
 * @return number of points
 * @type Integer
 */
MultiGeometry.prototype.getTotalPointCount = function(){ 
	var c = 0;
	for (var i = 0 ; i < this.count(); i++)	{
		c += this.get(i).count();
	}
	return c;
};

/**
 * gets the total number of {@link Point} objects of this {@link MultiGeometry}.
 *
 * @return number of points
 * @type Integer
 */
MultiGeometry.prototype.getPoint = function(j,k){
	return this.get(j).get(k);
};

/**
 * compares this {@link MultiGeometry} object with the {@link MultiGeometry} object multigeom.
 *
 * @param {MultiGeometry} multigeom another multigeometry
 * @return true if he multigeometries match; else false 
 * @type Boolean
 */
MultiGeometry.prototype.equals = function(multigeom) {
	if (this.geomType != multigeom.geomType) {return false;}
	if (this.count() != multigeom.count()) {return false;}
	if (this.getTotalPointCount() != multigeom.getTotalPointCount()) {return false;}
	for (var i=0; i<this.count(); i++) {
		if (!this.get(i).equals(multigeom.get(i))) {return false;}
	}
	return true;
};

/**
 * deletes the j-th {@link Point} object of the i-th {@link Geometry} object of this {@link MultiGeometry} object.
 *
 * @param {Integer} i geometry index
 * @param {Integer} j point index
 * @return true if the deletion succeded; else false.
 * @type Boolean
 */
MultiGeometry.prototype.delPoint = function(i,j){
	var res = this.get(i).del(j);
	if (res === false) {return this.del(i);}
	return true;
};


/**
 * @class a Geometry is a List of Point objects. If it is a polygon, the last point has 
 * to equal the first point.
 *
 * @extends List
 * @requires Point
 * @constructor
 * @param {String} a string representing a geometry type, see @see GeomType.
 */
function Geometry(aGeomtype){

	/**
	 * deletes the {@link Point} object at index i; -1 refers to the last 
	 * {@link Point} object in the list. Overwrites the del function of 
	 * {@link List}.
	 *
	 * @param {Integer} i index
	 * @return false if deletion is not yet finished. It is cascaded to 
	 *         {@link MultiGeometry}. True if the deletion is finished.
	 */
	this.del = function(i){
		i = this.getIndex(i);
		if (i !== false) {
			var tmpLength = this.count()-1;
			
			for (var z = i; z < tmpLength ; z ++){
				this.list[z] = this.list[z+1];
			}
			this.list.length -= 1;
		
			if (this.geomType == geomType.polygon){
				if (i == tmpLength) {this.list[0] = this.list[tmpLength-1];}
				else if (i === 0) {this.list[tmpLength-1] = this.list[0];}
				if (this.list.length == 1){return false;}
			}
			updateDist();
			if(this.list.length === 0) {return false;}
			return true;
		}
		return false;
	};

	/**
	 * adds a {@link Point} object to this {@link Geometry} object.
	 *
	 * @param {Float} x x value of the point
	 * @param {Float} y y value of the point
	 */	
	this.addPointByCoordinates = function(x,y){
		this.add(new Point(x,y));
		updateDist();
	};

	/**
	 * adds a {@link Point} object to this {@link Geometry} object.
	 *
	 * @param {Point} aPoint another point
	 */	
	this.addPoint = function(aPoint){
		this.add(new Point(aPoint.x, aPoint.y));
		updateDist();
	};

	/**
	 * inserts a {@link Point} object at index i of this {@link Geometry} object.
	 *
	 * @param {Point} p another point
	 * @param {Integer} i index
	 */	
	this.addPointAtIndex = function(p,i){
		i = this.getIndex(i);
		if (i !== false){
			for(var z = this.count(); z > i; z--){
				this.list[z] = this.list[z-1];
			}
			this.list[i] = new Point(p.x, p.y);
			updateDist();
		}
	};
	
	/**
	 * Overwrites the {@link Point) object at index i with the {@link Point} object p.
	 *
	 * @private
	 * @param {Point} p another point
	 * @param {Integer} i index
	 */	
	this.updatePointAtIndex = function(p, i){
		i = this.getIndex(i);
		if ((i === 0 || i == this.count()-1) && this.geomType == geomType.polygon){
			this.list[0] = p;
			this.list[this.count()-1] = p;
		}
		else {this.list[i] = p;}
		updateDist();
	};
	
	/**
	 * Updates the {@link Geometry#dist} and {@link Geometry#totaldist}
	 *
	 * @private
	 */	
	var updateDist = function(){
		dist[0] = 0;		
		totaldist[0] = 0;		
		for (var i = 1 ; i < that.count(); i++){
			dist[i] = that.get(i-1).dist(that.get(i));
			totaldist[i] = totaldist[i-1] + dist[i];
		}
	};
	/**
	 * gets the distance between the last and last but one point of this {@link Geometry}.
	 *
	 * @param {Integer} numberOfDigitis round to numberOfDigits (optional)
	 * @return the distance
	 * @type Float
	 */	
	this.getCurrentDist = function(numberOfDigits) {
		if (typeof(numberOfDigits) == "number") {
			return roundToDigits(dist[this.count()-1], numberOfDigits);
		}
		return dist[this.count()-1];
		
	};
	/**
	 * gets the length of the outer rim of this {@link Geometry}.
	 *
	 * @param {Integer} numberOfDigitis round to numberOfDigits (optional)
	 * @return the distance
	 * @type Float
	 */	
	this.getTotalDist = function(numberOfDigits) {
		if (typeof(numberOfDigits) == "number") {
			return roundToDigits(totaldist[this.count()-1], numberOfDigits);
		}
		return totaldist[this.count()-1];
	};
	/**
	 * closes this {@link Geometry}. 
	 *
	 * @return true if the geometry could be closed; otherwise false
	 * @type Boolean
	 */	
	this.close = function(){
		complete = true;
		if (this.geomType == geomType.polygon){
			if (this.count() > 2){
				if (!this.get(0).equals(this.get(-1))) {
					this.addPoint(this.get(0));
				}
			}
			else {return false;}
		}
		if (this.geomType == geomType.line){
			if (this.count() < 2){return false;}
		}
		return true;
	};
	/**
	 * checks if this {@link Geometry} has been closed. 
	 *
	 * @return true if the geometry is closed; otherwise false
	 * @type Boolean
	 */	
	this.isComplete = function() { 
		return complete;
	};
	
	this.list = [];
	var dist = [];
	var totaldist = [];
	var complete = false;

	var that = this;

	this.geomType = aGeomtype;
	this.name = nameGeometry;
}

Geometry.prototype = new List();

/**
 * gets the bounding box of this {@link Geometry}
 *
 * @return the bounding box (array of two Point objects)
 * @type Point[]
 */
Geometry.prototype.getBBox = function(){
	var q = this.get(0);
	var min = cloneObject(q);
	var max = cloneObject(q);
	
	for (var j=0; j<this.count(); j++){
		var pos = this.get(j);
		if (pos.x < min.x) {min.x = pos.x;}
		else if (pos.x > max.x) {max.x = pos.x;}
		if (pos.y < min.y) {min.y = pos.y;}
		else if (pos.y > max.y) {max.y = pos.y;}
	}
	return [min, max];
};

/**
 * updates all {@link Point} objects of this {@link Geometry} that equal oldP to newP
 *
 * @param {Point} oldP
 * @param {Point} newP
 */
Geometry.prototype.updateAllPointsLike = function(oldP, newP){
	var len = this.count();
	for (var i = 0; i < len ; i++){
		if (oldP.equals(this.get(i))){
			if (i>0 && newP.equals(this.get(i-1))){
				this.del(i);
				len--;
				i--;
			}
			else {this.updatePointAtIndex(newP, i);}
		}
	}
};

/**
 * compares this {@link Geometry} object with the {@link Geometry} object geom point by point.
 *
 * @param {Geometry} geom another geometry
 * @return true if he geometries match; else false 
 * @type Boolean
 */
Geometry.prototype.equals = function(geom) {
	if (this.geomType != geom.geomType) {return false;}
	if (this.count() != geom.count()) {return false;}
	for (var i=0; i < this.count(); i++) {
		if (!this.get(i).equals(geom.get(i))) {return false;}
	}
	return true;
};


/**
 * @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;
}


Documentation generated by JSDoc on Wed Aug 8 10:35:22 2007