	/**********************************************************************
	*                  JUserMap v0.40b by Michael Loesler                  *
	************************************************************************
	* Copyright (C) 2008 by Michael Loesler, http//derletztekick.com       *
	*                                                                      *
	*                                                                      *
	* This program is free software; you can redistribute it and/or modify *
	* it under the terms of the GNU General Public License as published by *
	* the Free Software Foundation; either version 3 of the License, or    *
	* (at your option) any later version.                                  *
	*                                                                      *
	* This program is distributed in the hope that it will be useful,      *
	* but WITHOUT ANY WARRANTY; without even the implied warranty of       *
	* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
	* GNU General Public License for more details.                         *
	*                                                                      *
	* You should have received a copy of the GNU General Public License    *
	* along with this program; if not, see <http://www.gnu.org/licenses/>  *
	* or write to the                                                      *
	* Free Software Foundation, Inc.,                                      *
	* 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.            *
	*                                                                      *
	 ***********************************************************************/	


		
		/**
		*	Map(id, mapEdge, users, mapTrans, mapTransInv, isInitMode)
		*	@param <String> id ........................... ID des Elternelements (HTML id)
		*	@param Array<Point2D> mapEdge ................ Umringspolygon
		*	@param Array<User> users ..................... Lister der Benutzer
		*	@param <AffinTransformation> mapTrans ...... AffintransformationsObjekt (hin)
		*	@param <AffinTransformation> mapTransInv ... AffintransformationsObjekt (rÃ¼ck)
		*	@param <Boolean> isInitMode .................. Initialisierungsmodus (optional)
		*	@see Point2D
		*	@see User
		*	@see AffinTransformation
		*	@author Michael Loesler - http://derletztekick.com		
		*
		**/	

		function Map(id, mapEdge, users, mapTrans, mapTransInv, mercProj, isInitMode) {
			if (!(this.parentElement = document.getElementById( id )))
				return;
			if (mapTrans == null)
				this.mapTrans = new AffinTransformation();
			else
				this.mapTrans = mapTrans;
			if (mapTransInv == null)
				this.mapTransInv = new AffinTransformation();
			else
				this.mapTransInv = mapTransInv;
			this.editUser = null;
			this.titleEl = document.createElement("div");
			this.minX = 0;
			this.minY = 0;
			this.maxX = 0;
			this.maxY = 0;
			this.finalOpacity = 0.9;
			this.circumRadius = 5;
			this.confirmMsg = "Dieser Punkt scheint nicht innerhalb der definierten Grenzen zu liegen, Position trotzdem hinzufÃ¼gen und speichern?";
			this.mercProj = mercProj||false;
			this.isInitializationMode = isInitMode||false;			
			this.txtArea = null;
			if (this.mercProj){
				if (mapEdge != null)
					for (var i=0; i<mapEdge.length; i++)
						mapEdge[i] = this.mercProj.bl2xy(mapEdge[i]);
				if (users != null)
					for (var i=0; i<users.length; i++)
						if (users[i].pos != null && users[i].pos.isComplete()){
							users[i].pos = this.mercProj.bl2xy(users[i].pos);
						}

			}
			this.mapEdge = mapEdge;
			this.users = new Array();
			
			
			/**
			*   setInitializationMode()
			* 	@return void
			*	@description haengt eine Textarea ins Dokument ein, in der die 
			*	             Koordinaten der geklickten Stelle und deren transformiertes
			*	             Pendante aufgelistet werden. Dient zur Passpunktbestimmung 
			*				 bzw. zum Erstellen des Umringspolygons
			*	@see transMapCoords2GlobalCoords
			*
			**/	
			this.setInitializationMode = function() {
				if (!this.isInitializationMode || !(this.txtArea = document.createElement("textarea")))
					return;
				this.txtArea.id = "JUserMap"
				this.txtArea.rows = 10;
				this.txtArea.cols = 100;
				document.getElementsByTagName("*")[0].appendChild(this.txtArea);
			}
			
			/**
			*   parseCoords()
			* 	@return void
			*	@param <String> str ... String mit Koordinaten
			*	@description zerlegt einen String mit Koordinateninformationen
			*	             in zwei WertepÃ¤rchen x und y und setzt einen Pin
			*	             an diese Stelle
			*	@see Point2D
			*	@see setNewPosition()
			*
			**/				
			this.parseCoords = function(str){
				var newPos = new Point2D();
				if (newPos.parsePoint2D(str))
					if (this.mercProj)
						this.setNewPosition( this.mercProj.bl2xy(newPos) );
					else
						this.setNewPosition( newPos );
			}
			
			/**
			*   showUserLocations()
			* 	@return void
			*	@description zeigt Pins von Usern auf der Karte
			*				 ermitteln den User, der seine Pos Ã¤ndern darf
			*				 fÃ¼gt Event auf Pin hinzu, fÃ¼r ToolTip
			*	@see createIconPin()
			*	@see updateUserPosition()
			*
			**/				
			this.showUserLocations = function() {
				for (var i=0; i<this.users.length; i++){
					if (this.users[i].canChangePos && this.editUser==null)
						this.editUser = this.users[i];
					else
						this.users[i].canChangePos = false;
					if (this.users[i].pos == null)
						continue;
					this.users[i].pin = this.createIconPin();
					if (this.users[i].canChangePos)
						this.users[i].pin.style.zIndex = 11;
					this.updateUserPosition( this.users[i] );
					this.users[i].pin.name = this.users[i].name;
					this.users[i].pin.infoTxt = this.users[i].infoTxt;
					this.users[i].pin.Instanz = this;
					this.users[i].pin.pos = this.users[i].pos;
					//this.users[i].pin.onclick = function(e) {
					this.users[i].pin.onmouseover = function(e) {
													this.Instanz.showTitle(this.pos, this.name, this.infoTxt); 
												};
					this.users[i].pin.onmouseout = function(e) {							
													this.Instanz.closeTitle();
												};
					this.parentElement.appendChild( this.users[i].pin );
				}
			}
			
			/**
			*   showTitle(p, name, infoTxt)
			* 	@return void
			*	@param <Point2D> p ........ Position
			*	@param <String> name ...... Name(n) zur Position p
			*	@param <String> infoTxt ... optionale zusatzinfos
			*	@description setzt Tooltip an Pin-Position P und
			*				 schreibt den oder die Namen und ggf.
			*                zusatzinfos (infTxt) in this.titelEl
			*	@notice mehrere Namen bzw. Zusatzinfos sind durch 
			*	        Komma-Tab (,\t) getrennt werden
			*	@see Point2D		
			*
			**/				
			this.showTitle = function(p, name, infoTxt){
				if (p == null || !p.isComplete())
					return;
				var pT = this.mapTrans.transPoint(p);
				var names = name.split(/,\t+/);
				if (infoTxt == null)
					var infoTxts = new Array();
				else
					var infoTxts = infoTxt.split(/,\t+/);
				var str = "<h1>Information<\/h1><p>";
				for (var i=0; i<names.length; i++){
					str += "<span>" + names[i] + "<\/span>";
					if (infoTxts[i].trim() != "")
						str += " " + infoTxts[i];
					if (i<names.length-1)
						str += "<\/p><p>";
				}
				str += "<\/p>";
				this.titleEl.innerHTML = str;
				this.titleEl.style.top  = parseInt(pT.y+10) + "px";
				this.titleEl.style.left = parseInt(pT.x) + "px";
				this.titleEl.style.visibility = "visible";
				this.fadeIn();
			}
			
			/**
			*   closeTitle()
			* 	@return void
			*	@description schlieÃŸt den offenen ToolTip
			*
			**/				
			this.closeTitle = function(){
				this.titleEl.style.visibility = "hidden";
				this.titleEl.style.top = 0;
				this.titleEl.style.left = 0;
			}
			
			/**
			* fadeIn()
			* @param <Double>opac
			* @return void
			* @uri http://www.dustindiaz.com/sweet-titles/ 
			* @modified Michael Loesler
			* @description Laesst Titel "einfaden", in dem 
			*              es sich selbst aufruft, bis 
			*              this.finalOpacity erreicht ist
			* 
			**/
			this.fadeIn = function (opac) {
				opac = opac || 0;
				var newOpac   = opac + 0.1;
				if (newOpac < this.finalOpacity) {
					this.titleEl.style.opacity = newOpac;
					this.titleEl.style.filter = "alpha(opacity:" + (newOpac * 100) + ")";
					// Anhaengen einer Instanz ans Window-Objekt 
					// damit sich Script selbst aufrufen kann.
					window.MapInstanz = this;
					window.setTimeout(function () {
						this.MapInstanz.fadeIn(newOpac);
					}, 15);

				} else {
					this.titleEl.style.opacity = this.finalOpacity;
					this.titleEl.style.filter = "alpha(opacity:" + (this.finalOpacity * 100) + ")";
				}
			}			
			
			/**
			*   transMapCoords2GlobalCoords(e)
			* 	@return <Point2D> P ... transformierte Kartenposition
			*	@param <Event> e ...... Mausereignis (optional)
			*	@description ermittelt die Klick-Position in der Karte
			*	             und transformiert diese ins Ã¼bergeordnete System
			*	@see Point2D
			*	@see AffinTransformation			
			*
			**/				
			this.transMapCoords2GlobalCoords = function(e){
				e = e||window.event;
				try {
					var x_pos = e.layerX||e.offsetX;
					var y_pos = e.layerY||e.offsetY;
				}catch(err) {
					window.alert("Konnte neue Position nicht bestimmen!");
					return null;
				}
				if (this.isInitializationMode && this.txtArea != null) {
					var p = new Point2D("P", x_pos, y_pos);
					var pT = this.mapTransInv.transPoint(p);
					this.txtArea.value += p.toString()+" \t"+pT.toString();
					if (this.mercProj)
						this.txtArea.value += " \t"+this.mercProj.xy2bl(pT).toString();
					this.txtArea.value += "\r\n";
				}
				return this.mapTransInv.transPoint(new Point2D("P", x_pos, y_pos));
			}
			
			/**
			*   setNewPosition(newPos)
			* 	@return void
			*	@param <Point2D> newPos ... new Position
			*	@description setzt von dem User, der seine Position abÃ¤ndern kann,
			*	             den Pin an die durch newPos Ã¼bergebene Position
			*	@see createIconPin()
			*	@see updateUserPosition()
			*	@see closeTitle()			
			*	@see showTitle()
			*	@see Point2D
			*
			**/				
			this.setNewPosition = function( newPos ) {
				if (this.editUser==null)
					return;
				if (this.mapEdge!=null && !newPos.inPolygon( this.mapEdge ) && !window.confirm( this.confirmMsg )){
					return;
				}
				this.closeTitle();

				var tmpP = new Point2D();
				tmpP.copy( this.editUser.pos );

				if (this.editUser.pos == null) {
					this.editUser.pin = this.createIconPin();
					this.editUser.pin.style.zIndex = 11;
					this.editUser.pin.text = this.editUser.name;
				}
				
				this.editUser.setPos( newPos );
				this.updateUserPosition( this.editUser );

				if (typeof(this.editUser.pin) != "undefined" && this.editUser.pos != null) {
					this.editUser.pin.name = this.editUser.name;
					this.editUser.pin.infoTxt = this.editUser.infoTxt;
					this.editUser.pin.pos  = this.editUser.pos;
					this.editUser.pin.Instanz = this;

					this.editUser.pin.onmouseover = function(e) {
													this.Instanz.showTitle(this.pos, this.name, this.infoTxt); 
												};
					this.editUser.pin.onmouseout = function(e) {							
													this.Instanz.closeTitle();
												};
					this.parentElement.appendChild( this.editUser.pin );
				}

				if (this.editUser.pos == null){
					this.editUser.pos = new Point2D();
					this.editUser.pos.copy( tmpP );
				}
			}
			
			/**
			*   createIconPin()
			* 	@return <ImageObject> pin 
			*	@description erzeugt ein ImageObjekt vom Pin
			*
			**/				
			this.createIconPin = function() {
				var pin = new Image(12,13)
				pin.src = "pin.png";
				pin.style.position = "absolute";
				pin.style.margin = 0;
				pin.style.padding = 0;
				pin.style.border = "none";
				pin.style.zIndex = 10;
				try { pin.style.cursor = "help"; } catch(e){ try { pin.style.cursor = "pointer"; } catch(e){ pin.style.cursor = "hand"; }}
				return pin;
			}
			
			/**
			*   createToolTip()
			* 	@return void
			*	@description erezugt ein DIV fÃ¼r den Tooltip
			*
			**/				
			this.createToolTip = function() {
				this.titleEl.appendChild(document.createTextNode( String.fromCharCode(160) ));
				this.titleEl.id = "JUserMapTitel";
				this.titleEl.style.top = 0;
				this.titleEl.style.left = 0;
				this.titleEl.style.zIndex = 20;
				this.titleEl.style.whiteSpace = "nowrap";
				this.titleEl.style.position = "absolute";
				this.titleEl.style.visibility = "hidden";
			}
			
			/**
			*   updateUserPosition(P)
			* 	@return void
			*	@param <Point2D> P ... neue Position
			*	@description setz den Pin an die Locationstelle des Nutzers
			*	@see Point2D		
			*
			**/				
			this.updateUserPosition = function(user) {
				if (user == null || user.pos == null || !user.pos.isComplete())
					return;
				var tmpP = this.mapTrans.transPoint(user.pos);
				if (tmpP.x<this.minX || tmpP.x>this.maxX || tmpP.y<this.minY || tmpP.y>this.maxY){
					window.alert("Fehler, unzulÃ¤ssige Kartenkoordinaten!");
					user.pos = null;
					return;
				}
				user.pin.style.top  = parseInt(tmpP.y - user.pin.height) + "px";
				user.pin.style.left = parseInt(tmpP.x - user.pin.width)  + "px";
			}
			
			/**
			*   localInArray(u)
			* 	@return <Boolean|Integer> false|index
			*	@param <User> u
			*	@description prÃ¼ft, ob ein Ort bereits schon einmal
			*	             vorkam und der Abstand zwischen dem 
			*                vorhandenen Ort und dem "selben" kleiner
			*                als *this.circumRadius* ist. 
			*                Liefert den Index im Array oder, wenn
			*                der Ort noch nicht vorkam, false
			*	@see User		
			*
			**/	
			this.localInArray = function( u ){
				if (u == null || u.canChangePos || u.pos == null || !u.pos.isComplete())
					return false;
				for (var i=0; i<this.users.length; i++)
					if (this.users[i].pos != null && 
						!this.users[i].canChangePos &&
						this.users[i].pos.pkt.toLowerCase() == u.pos.pkt.toLowerCase() && 
						this.mapTrans.transPoint(u.pos).distance(this.mapTrans.transPoint(this.users[i].pos)) < this.circumRadius)
							return i;
				return false;
			}
			
			/**
			*   getUniqueLocalsUser(users)
			* 	@return void
			*	@param Array<User> users
			*	@description fÃ¼gt Ã¼bergebene User dem Array *this.users* hinzu, 
			*	             wenn deren Ortschaft noch nicht vorhanden ist;
			*	             ansonsten wird nur der Name hinzugefÃ¼gt - Komma-Tab-Trennung
			*   @notice der *editierbare* User wird _immer_ hinzugefuegt auch wenn der 
			*		    Ort schon im Array ist
			*	@see User		
			*	@see localInArray()	
			*	@notice Funktion ist bei groÃŸer Anzahl an User sicher zeitintensiv und verbesserungswÃ¼rdig
			*
			**/	
			this.getUniqueLocalsUser = function(users) {
				if (users == null || users.length < 1)
					return;
				for (var i=0; i<users.length; i++){
					// Bestimme ersten "gueltigen" Nutzer
					if (this.users.length == 0 && users[i] != null && ((users[i].pos != null && users[i].pos.isComplete()) || users[i].canChangePos))
						this.users.push(users[i]);
					// Fuege weitere User an und pruefe mit bereits vorhandenen,
					// ob Ort schon existiert; lege ggf. zusammen
					else if (this.users.length > 0 && users[i] != null && ((users[i].pos != null && users[i].pos.isComplete()) || users[i].canChangePos)){
						var isInArray = this.localInArray(users[i]);
						if (isInArray===false)
							this.users.push(users[i]);
						else {
							this.users[isInArray].name += ",\t"+users[i].name;
							this.users[isInArray].infoTxt += ",\t"+users[i].infoTxt;
						}
					}
				}
			}			
			
			/**
			*   init()
			* 	@return void
			*	@description initialisiert die Klasse
			*				  + FÃ¼gt die Karte dem Elternelement parentElement hinzu
			*				  + Legt einen Eventlistner auf die Karte, um mgl.
			*					neue Positionen von Usern zu ermitteln
			*				  + FÃ¼gt Element fÃ¼r Title an
			*	@see transMapCoords2GlobalCoords()
			*	@see setNewPosition()
			*
			**/	
			this.init = function() {	
				var img = new Image(450,595);
				img.src = "deutschland.png";
				img.Instanz = this; 
				img.onclick = function(e) { 
						var clickPos = this.Instanz.transMapCoords2GlobalCoords( e );
						if (clickPos != null)
							this.Instanz.setNewPosition( clickPos ); 
				};
				this.maxX = img.width;
				this.maxY = img.height;
				this.parentElement.style.display = "block";
				this.parentElement.style.position = "relative";
				this.parentElement.style.width  = img.width  + "px";
				this.parentElement.style.height = img.height + "px";
				this.parentElement.style.padding = 0;
				
				this.createToolTip();
				this.parentElement.appendChild( img );
				this.parentElement.appendChild( this.titleEl );
				
				this.getUniqueLocalsUser(users);
				
				this.showUserLocations();
				if (this.isInitializationMode)
					this.setInitializationMode();
			}			
			
			this.init();
			
			
		}
	
		
		/**
		*	User(name, canChangePos, pos, infoTxt)
		*	@param <String> name ............ Benutzername
		*	@param <boolean> canChangePos ... User darf seine Position Ã¤ndern
		*	@param <Point2D> pos ............ Locationposition
		*	@param <String> infoTxT ......... weitere User Info als (formatierter) String (optional)
		*	@see Point2D
		*	@author Michael Loesler - http://derletztekick.com		
		*
		**/				
		function User(name, canChangePos, pos, infoTxt){
			this.name = name;
			this.canChangePos = canChangePos;
			this.pos = pos;
			this.infoTxt = infoTxt || " "; // ein Leerzeichen fuer den IE *rolleyes*
						
			/**
			*   setPos()
			*	@param <Point2D> p ... neue Position
			*	@return void
			*	@description Setzt neue Locationposition
			*	@see Point2D			
			*
			**/				
			this.setPos = function(p){
				this.pos = p;
			}
			
			/**
			*   toString()
			*	@return <String> s
			*
			**/			
			this.toString = function() {
				return "Name: "+this.name+"\ndarf editieren: "+this.canChangePos+"\nLocation: "+((this.pos==null)?null:this.pos.toString());
			}
		}


		/**
		*	Point2D(pkt, x, y)
		*	@param <String> pkt ... Punktnummer
		*	@param <double> x ..... X-Wert
		*	@param <double> y ..... Y-Wert
		*	@author Michael Loesler - http://derletztekick.com		
		*
		**/
		function Point2D(pkt, x, y){
			this.pkt = pkt;
			this.x = x;
			this.y = y;
			
			/**
			*   parsePoint2D()
			* 	@return <Boolean> isParsed
			*	@param <String> str
			*	@description zerlegt einen String mit Punktinformationen
			*	             in zwei WertepÃ¤rchen x und y und eine Pkt
			*	             und weist sich die Daten selbst (this) zu
			*
			**/				
			this.parsePoint2D = function( str ){
				var regExp = new RegExp(/^\s*(.+?)\s*(\d+\.{1,1}\d+)\s*(\d+\.{1,1}\d+)/);
				if (regExp.test( str )){
					this.pkt = RegExp.$1;
					this.x = parseFloat(RegExp.$2);
					this.y = parseFloat(RegExp.$3);
					return true;
				}
				else
					return false;
			}
			
			/**
			*   equalsIgnoreNumber(p)
			* 	@return <Boolean> isEqualsIgnoreNumber
			*	@param <Point2D> p
			*	@description PrÃ¼ft, ob die Koordinaten identisch sind
			*
			**/	
			this.equalsIgnoreNumber = function(p){
				return this.x == p.x && this.y == p.y
			}
			
			/**
			*   copy()
			* 	@return <this> clonedP
			*	@param <Point2D> P
			*	@description Cloned ein Point2D-Objekt
			*
			**/				
			this.copy = function( p ){
				for (var el in p){
					this[el] = p[el];
				}
			}
			
			/**
			*   isComplete()
			* 	@return Boolean isComplete ... Punkt vollstaendig
			*	@description prueft, ob ein Punkt (this) vollstaendig
			*                ist und gibt true im Erfolgsfall und 
			*                ansonsten false zurÃ¼ck
			*
			**/	
			this.isComplete = function() {
				return !isNaN(this.x) && typeof(this.x) == "number" && !isNaN(this.y) && typeof(this.y) == "number" && this.pkt != null;
			}
			
			/**
			*   distance(p)
			* 	@return <Double> dist ... (lineare) Entfernung zwischen Punkten
			*	@param <Point2D> p ...... Punkt, zu dem Entfernung bestimmt werden soll
			*	@description berechnet den linearen Abstand zwischen zwei
			*                Punkten ueber den Satz des Pythagoras
			*
			**/
			this.distance = function( p ) {
				return Math.sqrt(Math.pow(this.x-p.x,2)+Math.pow(this.y-p.y,2));
			}
			
			/**
			*   toString()
			*	@return <String> s
			*
			**/
			this.toString = function() {
				return this.pkt+"\t"+this.x+"\t"+this.y;
			}
			
			/**
			*   inPolygon()
			* 	@return <boolean> inPolygon
			*	@param Array<Point2D> hull ... Umringspolygon
			*	@description PrÃ¼ft, ob ein Punkt innerhalb (1.0e-003) der durch hull 
			*				 gegebenen HÃ¼lle liegt und gibt true, wenn der Punkt drin,
			*				 und false, wenn der Punkt auÃŸerhalb liegt, zurÃ¼ck.
			*				 Ermittelt die Summe der Richtungswinkel
			*	@see Point2D
			*
			**/				
			this.inPolygon = function( hull ) {
				var ax = hull[hull.length-1].x;
				var ay = hull[hull.length-1].y;
				var alpha = 0.0;
				
				for (var i=0; i<hull.length; i++){
					var dw=0;
					var bx = hull[i].x;
					var by = hull[i].y;
					if (bx == this.x && by == this.y) {
						alpha = Math.PI;
						break;
					}
				
					var sr = Math.atan2((ay-this.y), (ax-this.x));
					var er = Math.atan2((by-this.y), (bx-this.x));
				
					if (sr<0)
						sr += 2.0*Math.PI;
					if (er<0)
						er += 2.0*Math.PI;
					
					if (sr > Math.PI)
						sr -= 2.0*Math.PI;
          
					if (er > Math.PI) 
						er -= 2.0*Math.PI;

					if ((sr < 0 && er < 0) || (sr > 0 && er > 0)) {
						dw = er - sr;
						if (dw > 0)
							dw -= 2.0*Math.PI;
					}
					else {   
						dw = (er-sr+2.0*Math.PI)%(2.0*Math.PI);
						if (dw > 0) 
							dw -= 2.0*Math.PI;
					}
					dw =dw%(2.0*Math.PI);
					if (dw<0)
						dw += 2.0*Math.PI;
				
					if (dw == Math.PI) {
						alpha = 2.0*Math.PI;
						break;
					}
          
					if (dw > Math.PI)
						dw -= 2.0*Math.PI;

					alpha += dw;
					ax = bx;
					ay = by;
				}				
				if (Math.abs(Math.abs(alpha)-2.0*Math.PI) < 1.0E-003)
					return true;
				return false;
			}
		}

		/**
		*	BilinearTransformation(S, Z)
		*	@param Array<Point2D> S ... Punkte im Startsystem
		*	@param Array<Point2D> Z ... Punkte im Zielsystem
		*
		*	BilinearTransformation(a1, a2, a3, a4, b1, b2, b3, b4)
		*	@param <double> a1
		*	@param <double> a2 
		*	@param <double> a3
		*	@param <double> a4  
		*	@param <double> b1 
		*	@param <double> b2 
		*	@param <double> b3 
		*	@param <double> b4 
		*
		*	@see Point2D
		*	@url http://derletztekick.com/software/KoordTrans3D__Java
		*	@author Michael Loesler - http://derletztekick.com
		*
		**/
		function BilinearTransformation(pS, pZ, a3, a4, b1, b2, b3, b4) {
			// Flag; zeigt, ob Trans-Parameter vorhanden sind
			this.hasTransPars = false;
		
			// Punkte aus dem Startsystem = Bezugssystem
			this.pointsS = new Array();
			
			// Punkt in aktueller Karte - Passpunkte zu pointsS
			this.pointsZ = new Array();
			
			// Bilineare Transformationsparameter (default): a1, a2, a3, a4, b1, b2, b3, b4
			this.a1 = 1.0;
			this.a2 = 1.0;
			this.a3 = 1.0;
			this.a4 = 1.0;
			this.b1 = 1.0;
			this.b2 = 1.0;
			this.b3 = 1.0;
			this.b4 = 1.0;

			/**
			*   addPassPoints()
			* 	@return void
			*	@param Array<Point2D> S ... Startsystem
			*	@param Array<Point2D> Z ... Zielsystem
			*	@description Bestimmt Passpunkte aus dem Start- und Zielsystem
			*				 anhand ihrer _individuellen_ Punktnummer
			*	@see Point2D
			*
			**/				
			this.addPassPoints = function(pS, pZ){
				for (var i=0; i<pS.length; i++)
					for (var j=0; j<pZ.length; j++)
						if (pS[i].pkt.toLowerCase() == pZ[j].pkt.toLowerCase()){
							this.pointsS.push(pS[i]);
							this.pointsZ.push(pZ[j]);
							continue;	
						}	
			}
			
			/**
			*   transPoint()
			* 	@return <Point2D> tP ..... trasformierter Punkt P
			*	@param <Point2D> p ....... zu transformieender Punkt
			*	@description transformiert einen Punkt p zu tP mittels eines 
			*				 gegebenen Parametersatzes 
			*	@see Point2D			
			*
			**/			
			this.transPoint = function(p){
				if (!this.hasTransPars || p==null || !p.isComplete())
					return null;
					
				var x = this.a1 + this.a2*p.x + this.a3*p.y + this.a4*p.x*p.y;
				var y = this.b1 + this.b2*p.x + this.b3*p.y + this.b4*p.x*p.y;								
				return new Point2D(p.pkt, x, y);
			}

			/**
			*   calculateTransformationParameters(type)
			*	@param <Char>type ......... x oder y Parameter bestimmen
			* 	@return <boolean>isCalc ... bilinearTransformation
			*	@description Bilineare Transformationsgleichung 
			*			x = a1 + a2*X + a3*Y + a4*X*Y
			*			y = b1 + b2*X + b3*Y + b4*X*Y
			*		    
			*			Gauss-Markov-Modell
			*			
			*			N = A'*A
			*			n = A'*l
			*			       -1
			*			Qxx = N
			*			dx = Qxx*n
			*			X = X+dx
			*	@see Point2D			
			*
			**/
			this.calculateTransformationParameters = function(type){
				// Vektor der Unbekannten
				var X = Math.zeros(4);				
				
				// Desginmatrix A
				var A = Math.zeros(this.pointsS.length,X.length); 
				
				// Vektor der verkÃ¼rzten Beobachtungen
				var l = Math.zeros(this.pointsS.length, 1);
				
				if (type == 'x') {
					X[0] = this.a1;
					X[1] = this.a2;
					X[2] = this.a3;
					X[3] = this.a4;
				}
				else {
					X[0] = this.b1;
					X[1] = this.b2;
					X[2] = this.b3;
					X[3] = this.b4;
				}

				// Aufstellung des Normalgleichungssystem
				var maxX = Number.MAX_VALUE;
				var iteration = 100;
				while(maxX>1.0E-13 && --iteration>0){
					for (var i=0; i<this.pointsS.length; i++){
				
						A[i][0] =  1.0;
						A[i][1] =  this.pointsS[i].x;
						A[i][2] =  this.pointsS[i].y;
						A[i][3] =  this.pointsS[i].x*this.pointsS[i].y;

						if (type == 'x')
							l[i][0] = this.pointsZ[i].x - (X[0]+X[1]*this.pointsS[i].x + X[2]*this.pointsS[i].y + X[3]*this.pointsS[i].x*this.pointsS[i].y);
						else
							l[i][0] = this.pointsZ[i].y - (X[0]+X[1]*this.pointsS[i].x + X[2]*this.pointsS[i].y + X[3]*this.pointsS[i].x*this.pointsS[i].y);

					}
										
					try {
						var N = Math.multi(Math.trans(A),A);				
						var n = Math.multi(Math.trans(A),l);
						var Qxx = Math.inv(N);
						var dx = Math.multi(Qxx, n);
						maxX = dx[0][0];
						for (var i=0; i<X.length; i++){
							X[i] += dx[i][0];
							if (isNaN(X[i]))
								return false;
							maxX = Math.max(maxX,Math.abs(dx[i][0]));
						}
					} catch(err) {
						return false;
					}
				}
				//window.alert(maxX);
				if (type == 'x') {
					this.a1 = X[0];
					this.a2 = X[1];
					this.a3 = X[2];
					this.a4 = X[3];
				}
				else {
					this.b1 = X[0];
					this.b2 = X[1];
					this.b3 = X[2];
					this.b4 = X[3];
				}
				return true;
			}


			/**
			*   toString()
			*	@return <String> s
			*
			**/			
			this.toString = function() {
				if (!this.hasTransPars)
					return "Keine Transformationsparameter vorhanden!";
				return "a1 "+this.a1+"\na2 "+this.a2+"\na3 "+this.a3+"\na4 "+this.a4+"\nb1 "+this.b1+"\nb2 "+this.b2+"\nb3 "+this.b3+"\nb4 "+this.b4;
			}
			
			if (arguments.length == 2) {
				this.addPassPoints(pS, pZ);
				if (!(this.hasTransPars = (this.calculateTransformationParameters('x') && this.calculateTransformationParameters('y')))){
					window.alert("Fehler beim ermitteln der Transformationsparameter!");
				}
			}
			else if (arguments.length == 8){
				this.a1 = parseFloat(pS); 
				this.a2	= parseFloat(pZ); 
				this.a3	= parseFloat(a3); 
				this.a4	= parseFloat(a4); 
				this.b1 = parseFloat(b1); 
				this.b2	= parseFloat(b2); 
				this.b3	= parseFloat(b3); 
				this.b4	= parseFloat(b4); 
				// !!!OHNE!!! PrÃ¼fung der Parameter
				this.hasTransPars = true;		
			}
			else if (arguments.length == 0){
				this.a1 = 1.0;
				this.a2 = 1.0;
				this.a3 = 1.0;
				this.a4 = 1.0;
				this.b1 = 1.0;
				this.b2 = 1.0;
				this.b3 = 1.0;
				this.b4 = 1.0;
				this.hasTransPars = true;		
			}
			else {
				window.alert("Anzahl der Eingangsargumente in AffinTransformation stimmt nicht!");
				return;
			}			
			
		
		}
		

		/**
		*	AffinTransformation(S, Z)
		*	@param Array<Point2D> S ... Punkte im Startsystem
		*	@param Array<Point2D> Z ... Punkte im Zielsystem
		*
		*	AffinTransformation(Mx, My, Rx, Ry, Tx, Ty)
		*	@param <double> Mx ........ MaÃŸstab in X-Richtung
		*	@param <double> My ........ MaÃŸstab in Y-Richtung
		*	@param <double> Rx ........ Rotaionswinkel um X-Achse [RAD]
		*	@param <double> Ry ........ Rotaionswinkel um Y-Achse [RAD]
		*	@param <double> Tx ........ Translation in X-Richtung
		*	@param <double> Ty ........ Translation in Y-Richtung
		*
		*	@see Point2D
		*	@url http://derletztekick.com/software/KoordTrans3D__Java
		*	@author Michael Loesler - http://derletztekick.com
		*
		**/
		function AffinTransformation(pS, pZ, Rx, Ry, Tx, Ty) {
			// Flag; zeigt, ob Trans-Parameter vorhanden sind
			this.hasTransPars = false;
		
			// Punkte aus dem Startsystem = Bezugssystem
			this.pointsS = new Array();
			
			// Punkt in aktueller Karte - Passpunkte zu pointsS
			this.pointsZ = new Array();
			
			// Affin-Transformationsparameter (default): Mx, My, Rx, Ry, Tx, Ty
			this.X = [1.0, 1.0, 0.0, 0.0, 0.0, 0.0];
		

			/**
			*   addPassPoints()
			* 	@return void
			*	@param Array<Point2D> S ... Startsystem
			*	@param Array<Point2D> Z ... Zielsystem
			*	@description Bestimmt Passpunkte aus dem Start- und Zielsystem
			*				 anhand ihrer _individuellen_ Punktnummer
			*	@see Point2D
			*
			**/				
			this.addPassPoints = function(pS, pZ){
				for (var i=0; i<pS.length; i++)
					for (var j=0; j<pZ.length; j++)
						if (pS[i].pkt.toLowerCase() == pZ[j].pkt.toLowerCase()){
							this.pointsS.push(pS[i]);
							this.pointsZ.push(pZ[j]);
							continue;	
						}	
			}
			
			/**
			*   transPoint()
			* 	@return <Point2D> tP ..... trasformierter Punkt P
			*	@param <Point2D> p ....... zu transformieender Punkt
			*	@description transformiert einen Punkt p zu tP mittels eines 
			*				 gegebenen Parametersatzes 
			*	@see Point2D			
			*
			**/			
			this.transPoint = function(p){
				if (!this.hasTransPars || p==null || !p.isComplete())
					return null;

				var R = [[Math.cos(this.X[2]),  -Math.sin(this.X[3])],
				         [Math.sin(this.X[2]),   Math.cos(this.X[3])]];
						 
				var x = R[0][0]*this.X[0]*p.x + R[0][1]*this.X[1]*p.y + this.X[4];
				var y = R[1][0]*this.X[0]*p.x + R[1][1]*this.X[1]*p.y + this.X[5];
			
				return new Point2D(p.pkt, x, y);
			}
			
			/**
			*   calculateTransformationParameters()
			* 	@return <boolean>isCalc
			*	@description in Matrixnotation: PT = T + M * R*P
			*			X = Tx + Mx *  cos( Rx ) -sin( Ry ) * x
			*			Y = Ty + My *  sin( Rx )  cos( Ry ) * y
			*		
			*			Gauss-Markov-Modell
			*			
			*			N = A'*A
			*			n = A'*l
			*			       -1
			*			Qxx = N
			*			dx = Qxx*n
			*			X = X+dx
			*	@see Point2D			
			*
			**/
			this.calculateTransformationParameters = function(){
				// Desginmatrix A
				var A = Math.zeros(2*this.pointsS.length,this.X.length); 
				
				// Vektor der verkÃ¼rzten Beobachtungen
				var l = Math.zeros(2*this.pointsS.length, 1);

				// Aufstellung des Normalgleichungssystem
				var maxX = Number.MAX_VALUE;
				var iteration = 100;
				while(maxX>1.0E-13 && --iteration>0){
					for (var i=0; i<this.pointsS.length; i++){
				
						A[i][0] =  Math.cos(this.X[2])*this.pointsS[i].x;
						A[i][1] = -Math.sin(this.X[3])*this.pointsS[i].y;
						A[i][2] = -Math.sin(this.X[2])*this.X[0]*this.pointsS[i].x;
						A[i][3] = -Math.cos(this.X[3])*this.X[1]*this.pointsS[i].y;
						A[i][4] =  1.0;
						A[i][5] =  0.0;
						l[i][0] =  this.pointsZ[i].x - ((Math.cos(this.X[2])*(this.X[0]*this.pointsS[i].x) - Math.sin(this.X[3])*(this.X[1]*this.pointsS[i].y))+this.X[4]);
          
						A[i+this.pointsS.length][0] =  Math.sin(this.X[2])*this.pointsS[i].x;
						A[i+this.pointsS.length][1] =  Math.cos(this.X[3])*this.pointsS[i].y;
						A[i+this.pointsS.length][2] =  Math.cos(this.X[2])*this.X[0]*this.pointsS[i].x;
						A[i+this.pointsS.length][3] = -Math.sin(this.X[3])*this.X[1]*this.pointsS[i].y;
						A[i+this.pointsS.length][4] =  0.0;
						A[i+this.pointsS.length][5] =  1.0;
						l[i+this.pointsS.length][0] =  this.pointsZ[i].y - ((Math.sin(this.X[2])*(this.X[0]*this.pointsS[i].x) + Math.cos(this.X[3])*(this.X[1]*this.pointsS[i].y))+this.X[5]);
					
					}
										
					try {
						var N = Math.multi(Math.trans(A),A);				
						var n = Math.multi(Math.trans(A),l);
						var Qxx = Math.inv(N);
						var dx = Math.multi(Qxx, n);
						maxX = dx[0][0];
						for (var i=0; i<this.X.length; i++){
							this.X[i] += dx[i][0];
							if (isNaN(this.X[i]))
								return false;
							maxX = Math.max(maxX,Math.abs(dx[i][0]));
						}
					} catch(err) {
						return false;
					}
				}
				//window.alert(maxX);
				return true;
			}
			
			/**
			*   toString()
			*	@return <String> s
			*
			**/			
			this.toString = function() {
				if (!this.hasTransPars)
					return "Keine Transformationsparameter vorhanden!";
				return "Mx "+this.X[0]+"\n"+"My "+this.X[1]+"\n"+"Rx "+this.X[2]+"\n"+"Ry "+this.X[3]+"\n"+"Tx "+this.X[4]+"\n"+"Ty "+this.X[5];
			}
			
			if (arguments.length == 2) {
				this.addPassPoints(pS, pZ);
				if (!(this.hasTransPars = this.calculateTransformationParameters())){
					window.alert("Fehler beim ermitteln der Transformationsparameter!");
				}
			}
			else if (arguments.length == 6){
				this.X = [	parseFloat(pS), 
							parseFloat(pZ), 
							parseFloat(Rx), 
							parseFloat(Ry), 
							parseFloat(Tx), 
							parseFloat(Ty)
						];
				// !!!OHNE!!! PrÃ¼fung der Parameter
				this.hasTransPars = true;		
			}
			else if (arguments.length == 0){
				this.X = [	1.0, 
							1.0, 
							0.0, 
							0.0, 
							0.0, 
							0.0
						];
				this.hasTransPars = true;		
			}
			else {
				window.alert("Anzahl der Eingangsargumente in AffinTransformation stimmt nicht!");
				return;
			}
		}
		
		/**
		*	Query(k,v)
		*	@param <String> k ... SchlÃŸssel
		*	@param <String> v ... Wert
		*	
		*	@author Michael Loesler - http://derletztekick.com
		*
		**/
		
		function Query(k, v){
			this.key = k.trim();
			this.value = encodeURIComponent(v.trim()); //encodeURI(), escape(), encodeURIComponent()
			
			/**
			*   toString()
			*	@return <String> s
			*
			**/				
			this.toString = function(){
				return this.key+"="+this.value+"&";
			}
		}
		
		/**
		*	trim()
		*	@return <String> s ... Zeichenkette
		*	@description entfernt Whitspaces am Anfang und am Ende
		*	             einer Zeichenkette - Prototyp-Erweiterung
		*	             String-Objekt
		*
		**/	
		String.prototype.trim = function(){
			return this.replace(/^\s*|\s*$/g, "");
		}
		
		/**
		*   zeros()
		*	@param <int> o ... Zeilen
		*	@param <int> p ... Spalten
		* 	@return <Array> A 
		*	@description Erzeugt eine Matrix A(o,p), 
		*				 die mit Nullen gefÃ¼llt ist
		*
		**/			
		Math.zeros = function(o,p) {
			var A = new Array(o);
			for (var i=0; i<o; i++) {
				A[i] = new Array(p);
				for (var j=0; j<p; j++)
					A[i][j]=0.0;
			}
			return A;
		}
		
		/**
		*   trans()
		*	@param <Array> A
		* 	@return <Array> AT 
		*	@description Transponiert eine Matrix A
		*				 und gibt diese (AT) zurÃ¼ck
		*
		**/				
		Math.trans = function(A) {
			var AT = Math.zeros(A[0].length, A.length);
			for (var i=0; i<A.length; i++) 
				for (var j=0; j<A[i].length; j++) 
					AT[j][i] = A[i][j];	
			return AT;
		}
		
		/**
		*   multi()
		*	@param <Array> A1
		*	@param <Array> A2
		* 	@return <Array> R = A1*A2 
		*	@description Multipliziert zwei Matrizen A1(o,p), A2(l,k) 
		*				 und gibt das Produkt R(p,l) zurÃ¼ck
		*			 	 
		**/				
		Math.multi = function (A1, A2){
			if (A1[0].length == A2.length){
				var R = Math.zeros(A1.length,A2[0].length); 
				for (var i=0; i<A1.length; i++){
					for (var j=0; j<A2[0].length; j++){
						R[i][j] = 0.0;
						for (var k=0; k<A1[0].length; k++)
							R[i][j] += A1[i][k] * A2[k][j];
					}		
				}
				return R;
			}
			return null;

		}
		
		/**
		*   inv()
		*	@param <Array> A
		*	                 -1
		* 	@return <Array> A                               -1
		*	@description Invertiert die MAtrix A(n,n); A * A  =  E
		*			 	 
		**/			
		Math.inv = function(A) {
			for (var i=0; i<A.length; i++){
				A[i][i] = 1.0/A[i][i];
				for (var k=0; k<A.length; k++){
					if (k==i)
						continue;
					A[i][k] = -(A[i][i]*A[i][k]);
				}
				for (var j=0; j<A.length; j++){
					if (j==i) 
						continue;
					for (var k=0; k<A.length; k++){
						if (k==i)
							continue;
						A[j][k] = A[j][k] + A[i][k]*A[j][i];
					}
				}
				for (var j=0; j<A.length; j++){
					if(j==i)
						continue;
					A[j][i] = A[i][i]*A[j][i];	
				}
			}
			return A;
		}		
		
		/**
		*	asinh(x)
		*	@param <Double> x ........ Argument
		*	@return <Double> asinh ... Funktionswert
		*	@description Erweiterung des Math-Objektes
		*	             um Funktion: asinh()
		*
		**/	
		Math.asinh = function(x) {
			return Math.log(x+Math.sqrt(x*x+1));
		}
		
		/**
		*	sinh(x)
		*	@param <Double> x ....... Argument
		*	@return <Double> sinh ... Funktionswert
		*	@description Erweiterung des Math-Objektes
		*	             um Funktion: sinh()
		*
		**/
		Math.sinh = function(x) {
			return 0.5*(Math.pow(Math.E,x) - Math.pow(Math.E,-x))
		}
		
		/**
		*	cosh(x)
		*	@param <Double> x ....... Argument
		*	@return <Double> cosh ... Funktionswert
		*	@description Erweiterung des Math-Objektes
		*	             um Funktion: cosh()
		*
		**/
		Math.cosh = function(x) {
			return 0.5*(Math.pow(Math.E,x) + Math.pow(Math.E,-x))
		}
		
		/**
		*	tanh(x)
		*	@param <Double> x ....... Argument
		*	@return <Double> tanh ... Funktionswert
		*	@description Erweiterung des Math-Objektes
		*	             um Funktion: tanh()
		*
		**/
		Math.tanh = function(x) {
			return Math.sinh(x)/Math.cosh(x);
		}
				
			
		/**
		*   Request(uri, m, q)
		*	@param <String> uri ....... URI zur Datenbankabfrage
		*	@param <String> m ......... Ã¼bertragungsmethode: POST / GET
		*	@param Array<Query> q ..... Ã¼bergabeparameter
		*	@param <Object> obj ....... Objekt
		*	@param <String> func ...... Funktionsname, der Response verarbeitet als !!!String!!!
		*	@description ermittelt via XMLHttpRequest-Objekt aus einer 
		*	             Datenbank (uri) die gespeicherten Koordinaten
		*	             zu einem Ort (q) und übergibt die dem Objekt (obj)
		*	             und dessen Funktion func --> obj.func( <ResponseText> )
		*	@see Query
		*
		**/	
		function Request(uri,m,q,obj,func){
			if (typeof(obj[func]) == "undefined") {
				window.alert("Fehler, das Objekt besitzt die Ã¼bergebene Funktion nicht!");
				return;
			}
			var httpRequest = false;
			try{
				if (window.XMLHttpRequest) 
					httpRequest = new XMLHttpRequest();
					if (httpRequest.overrideMimeType)
						httpRequest.overrideMimeType('text/plain');
				else if (window.ActiveXObject) {
					try {
						this.httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
					} catch (e) {
						try {
							this.httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
						} catch (e) {
							window.alert("Fehler beim erzeugen des XMLHttpRequest!\n"+e);
							return;
						}
					}
				}
				if (!httpRequest)
					return;
				var qStr = "";	
				if (q instanceof Query)
					qStr = q.toString();
				else if(q.length != "undefined" && q.length > 0)
					for (var i=0; i<q.length; i++)
						qStr += q[i].toString();
				else
					return;
				qStr +=	new Date().getTime();
				httpRequest.abort();
				httpRequest.onreadystatechange = function() {
					if (httpRequest.readyState == 4) {
						obj[func]( decodeURIComponent(httpRequest.responseText) );
						/*
						try {
							obj[func](httpRequest.responseText);
						} catch (e) {
							window.alert("Fehler, das Objekt besitzt die Ã¼bergebene Funktion nicht!\n"+e);
							return;
						}
						*/						
					}
				};
				if (m.toLowerCase() == "post"){
					httpRequest.open("POST", uri, true);
					httpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
					httpRequest.send( qStr );
				}
				else {
					httpRequest.open("GET", uri+"?"+qStr, true);
					httpRequest.send(null);
				}
			}
			catch(err) {
				window.alert("Fehler, die Seite ("+uri+") konnte nicht geladen werden!\n\n"+err);
				return;
			}
		}	
		
		
		/**
		*	MercatorProjection(lamda0)
		*	@param <Double> lamda0 ... geographischer Laenge des Kartenzentrums
		*	@see Point2D
		*	@url http://de.wikipedia.org/wiki/Mercator-Projektion
		*	@author Michael Loesler - http://derletztekick.com		
		*
		**/			
		function MercatorProjection(lamda0) {
			this.lamda0 = lamda0;
		
			/**
			*	xy2bl( p )
			*	@param <Point2D> p .... umzuformender Punkt
			*	@return <Point2D> p ... umgeformter Punkt
			*	@see Point2D
			*	@description Bestimmt zu einem Projektionspunkt
			*	             die geographischen Koordinaten
			*
			**/
			this.xy2bl = function(p) {
				if (p==null || !p.isComplete())
					return null;
				var lamda = p.x + this.lamda0;
				var phi = Math.asin(Math.tanh(p.y))*180.0/Math.PI;
				return new Point2D(p.pkt, lamda, phi);
			}
			
			/**
			*	bl2xy( p )
			*	@param <Point2D> p .... umzuformender Punkt
			*	@return <Point2D> p ... umgeformter Punkt
			*	@see Point2D
			*	@description Bestimmt zu den geographischen
			*	             Koordinaten eines Punktes die 
			*	             Projektion
			*
			**/			
			this.bl2xy = function(p) {
				if (p==null || !p.isComplete())
					return null;
				var x = p.x - this.lamda0;
				var y = Math.asinh(Math.tan(p.y*Math.PI/180.0));
				return new Point2D(p.pkt, x, y);
			}
		}
		

		/**
		*   addOptionElements(points)
		* 	@return void
		*	@param Array<Point2D> points ... Punkte (Ortschaften)
		*	@description fÃ¼r pro Ort ein OPTION-Feld im Formular hinzu
		*	             bzw. aktualisiert die vorhandenen
		*	@see Point2D
		*
		**/	
		function addOptionElements( points ) {
			if (window.f && points.length>0){
				//var selectEl = window.f.elements['ort'];
				var selectEl = window.f.getElementsByTagName("select")[0];
				var optEls = selectEl.getElementsByTagName("option");
				selectEl.style.visibility = "";

				for (var i=0; i<points.length; i++) {
					// erstmal die nutzen, die schon da sind...
					if (i<optEls.length) {
						optEls[i].text  = points[i].pkt;
						optEls[i].value = points[i].toString();
					}
					// dann neue hinzufummeln...
					else {
						var opt   = document.createElement("option");
						selectEl.appendChild( opt );
						opt.text  = points[i].pkt;
						opt.value = points[i].toString();
					}
				}
				// und wenn noch welche da sind, weg damit...
				if (points.length<optEls.length)
					for (var i=optEls.length-1; i>=points.length; i--)
						selectEl.removeChild(optEls[i]);

			}
		
		} 
		
		/**
		*   getLocalityFromDataBase(str)
		* 	@return void
		*	@param <String> str ... Ergebnis der DB-Abfrage
		*	@description Wertet das Ergebnis der DB Abfrag aus
		*	             PrÃ¼ft hierbei, ob der ergebnisstring Zeilenweise
		*				 Punkte enthÃ¼lt und speichert diese
		*	@see Point2D
		*	@see addOptionElements()
		*
		**/			
		function getLocalityFromDataBase( str ) {
			var tmpArr = str.trim().split(/[\r\n]+/);
			if (tmpArr.length == 0)
				return;
			var localPoints = new Array();	
			for (var i=0; i<tmpArr.length; i++){
				var tmpP = new Point2D();
				if (tmpP.parsePoint2D(tmpArr[i].trim()))
					localPoints.push( tmpP );
			}
			if (localPoints.length>0)
				window.addOptionElements( localPoints );
		}
		
		var DOMContentLoaded = false;
		function addContentLoadListener (func) {
			if (document.addEventListener) {
				var DOMContentLoadFunction = function () {
					window.DOMContentLoaded = true;
					func();
				};
				document.addEventListener("DOMContentLoaded", DOMContentLoadFunction, false);
			}
			var oldfunc = (window.onload || new Function());
			window.onload = function () {
				if (!window.DOMContentLoaded) {
					oldfunc();
					func();
				}
			};
		}

