/** 
 * @fileoverview This file is to be used for creating and managing
 * maps consisting of an array of square tiles.
 */

/**
 * @class A map consisting of an array of square tiles
 * @constructor
 * @requires iwTile
 * @public
 * @param (int) nVisibleWidth Pixel size of the visible part of the map
 * @param (int) nVisibleHeight Pixel size of the visible part of the map
 * @param (int) nTotalTilesX Horizontal number of tiles
 * @param (int) nTotalTilesY Vertical number of tiles
 * @param (int) nCenterMeterX Meter position of the map center
 * @param (int) nCenterMeterY Meter position of the map center
 * @param (int) nTilesize Pixel size of a single map tile
 * @param (int) nTilesizeMeter Meter size of a single map tile
 */
function iwTileset(	nVisibleWidth, nVisibleHeight,
										nTotalTilesX, nTotalTilesY, 
										nCenterMeterX, nCenterMeterY,
										nTilesize, nTilesizeMeter)
{
	this.MAPMODE_MAP		= 0;
	this.MAPMODE_AIR		= 1;
	this.MAPMODE_HYBRID	= 2;
	
	this.CONTROL_NONE		= 0;
	this.CONTROL_DRAG		= 1;
	this.CONTROL_ZOOM		= 2;
	this.CONTROL_SLIDE	= 3;
	this.CONTROL_SELECT	= 4;

	// variables
	this.nVisibleWidth  	= nVisibleWidth;		// size of the visible part of the map
	this.nVisibleHeight 	= nVisibleHeight;
	this.nTotalTilesX			= nTotalTilesX; 		// number of tiles
	this.nTotalTilesY			= nTotalTilesY;
	this.nTilesetStartX		= 0;								// screen position
	this.nTilesetStartY 	= 0;
	this.nCenterMeterX		= nCenterMeterX;		// center position in meters
	this.nCenterMeterY		= nCenterMeterY;
	this.nTilesize				= nTilesize;
	this.nTilesizeMeter 	=	nTilesizeMeter;
	this.nLoadingTiles		= 0;
	this.nMapMode					= 0;
	this.nOverviewFactor	= 0;
	this.nOverviewSize		= 0;
	this.nControlLeft			= this.CONTROL_NONE;
	this.nControlRight		= this.CONTROL_NONE;
	this.nMovedX					= 0;
	this.nMovedY					= 0;
	
	this.sMapServerURL	= "";
	this.bCompletelyVisible = false;
	this.bIsOverview		= false;
	this.bHasOverview		= false;
	this.oAsynchroneTransferObject = new iwAsynchroneTransferObject(N_MAX_REQUESTS);
	this.oZoomObject		= new iwZoomObject(this);
	this.fOnMoveStart		= null;
	this.fOnMoveEnd			= null;
	this.fOnZoomStart		= null;
	this.fOnZoomEnd			= null;
	this.fOnMove				= null;
	this.aTiles					= new Array(nTotalTilesX * nTotalTilesY);
	this.aQueueTiles		= new Array();
	this.aQueueRequests	= new Array();
	this.aQueueSection	= new Array();
	this.aMapElements		= new Array();
	
	// public methods
	this.GetMeterWidth				= iwGetMeterWidth;
	this.GetMeterHeight				= iwGetMeterHeight;
	this.GetMeterPosLeft			= iwGetMeterPosLeft;
	this.GetMeterPosTop				= iwGetMeterPosTop;
	this.GetPixelWidth				= iwGetPixelWidth;
	this.GetPixelHeight				= iwGetPixelHeight;
	this.SetOnMove						= iwSetOnMove;
	this.SetOnMoveStart				= iwSetOnMoveStart;
	this.SetOnMoveEnd					= iwSetOnMoveEnd;
	this.SetOnZoomStart				= iwSetOnZoomStart;
	this.SetOnZoomEnd					= iwSetOnZoomEnd;
	this.SetMapMode						= iwSetMapMode;
	this.SetMapServerURL			= iwSetMapServerURL;
	this.SetLeftMouseButton		= iwSetLeftMouseButton;
	this.SetRightMouseButton	= iwSetRightMouseButton;
	this.SetZoomLevel					= iwSetZoomLevel;
	this.MoveTo								= iwMoveMapTo;
	this.AddCopyright					= iwAddCopyright;
	this.AddOverview					= iwAddOverview;
	this.AddElement						= iwAddElement;
	this.AddToolbox						= iwAddToolbox;
	this.Print								= iwPrint;
	
	// private methods
	this.CreateTiles = iwCreateTiles;
	this.CreateTilesDebug = iwCreateTilesDebug;
	this.PlaceTiles = iwPlaceTiles;
	this.LoadNext = iwLoadNext;
	this.RequestNext = iwRequestNext;
	this.Move = iwMoveMap;
	this.MeterToPixelX = iwMeterToPixelX;
	this.MeterToPixelY = iwMeterToPixelY;
	this.PixelToMeterX = iwPixelToMeterX;
	this.PixelToMeterY = iwPixelToMeterY;
	this.BuildQueue	= iwBuildQueue;
	this.DiscardLeftBorder = iwDiscardLeftBorder;
	this.DiscardRightBorder = iwDiscardRightBorder;
	this.DiscardLowerBorder = iwDiscardLowerBorder;
	this.DiscardUpperBorder = iwDiscardUpperBorder;
	this.GetVisibleTiles = iwGetVisibleTiles;
	this.FindBestTilePosition = iwFindBestTilePosition;
	this.ZoomInLevel = iwZoomInLevel;
	this.ZoomOutLevel = iwZoomOutLevel;
	this.LoadWholeMap = iwLoadWholeMap;
	this.LoadSection = iwLoadSection;
	this.ResetSection = iwResetSection;
	this.LoadNextSection = iwLoadNextSection;
	this.FindLeftCol = iwFindLeftCol;
	this.FindTopRow = iwFindTopRow;
	this.FindBestScalingLevel = iwFindBestScalingLevel;
	this.AddTilesToQueue = iwAddTilesToQueue;
	this.Hide = iwHide;
	this.SendPrepareTilesRequest = iwSendPrepareTilesRequest;
	this.GetMeterExtent	= iwGetMeterExtent;
	this.GetTileImageName = iwGetTileImageName;
	this.AddToolMeasure = iwAddToolMeasure;
	this.AddToolPrint = iwAddToolPrint;
	this.GetLeft = iwGetLeft;
	this.GetTop = iwGetTop;
	this.PrintInit = iwPrintInit;
	
	this.callbackMapResize;	// callback function when map was resized (called in 
								  				// iwresize() defined in mapping.js ) 
	
	// constructor implementation
	//this.oZoomObject.SetOnZoomEffectEnd( function() { iwShowLabels(true); } );
	
	if (bDebugMode)
		this.CreateTilesDebug();
	else
		this.CreateTiles();
	this.aTiles.length = nTotalTilesX * nTotalTilesY;
	for (var nRow = 0; nRow < nTotalTilesY; nRow++)
	{
		for (var nCol = 0; nCol < nTotalTilesX; nCol++)
		{
			var nIndex = nRow * nTotalTilesX + nCol;
			this.aTiles[nIndex] = new iwTile(	this, nCol, nRow, 0, 0, 0, 0);
		}
	}
	this.PlaceTiles();
}

/**
 * Creates the div and img elements to visualize the map
 * and inserts them into the DOM tree
 * @private
 */
function iwCreateTiles()
{
	// if there is an old tile map remove that first from the DOM tree
	var divTileMap = document.getElementById("divTileMap");
	if (divTileMap)
	{
		divTileMap.parentNode.removeChild(divTileMap);
	}
	
	var divTileMap = document.createElement('div');
	divTileMap.id = "divTileMap";

	if (BrowserDetect.browser == "Safari" || BrowserDetect.browser == "Konqueror")
	{
		divTileMap.style.visibility				= "visible"; //for mini and all Tiles
	}
	else
	{
		divTileMap.style.visibility				= "hidden";
	}
	
	divTileMap.style.backgroundColor 	= "#ffffc8";
	divTileMap.style.position 				= "absolute";
	divTileMap.style.left					 		= "0px";
	divTileMap.style.top 							= "0px";
	divTileMap.style.width					 	= this.nVisibleWidth + "px";
	divTileMap.style.height						= this.nVisibleHeight + "px";
	
	for (var iRow = 0; iRow < this.nTotalTilesY; iRow++)
	{
		for (var iCol = 0; iCol < this.nTotalTilesX; iCol++)
		{
			var divTile = document.createElement('div');
			divTile.id = "divTile_" + iCol + "_" + iRow;
			divTile.style.position	= "absolute";
			divTile.style.zIndex		= "1";
			divTile.style.left			= "0px";
			divTile.style.top				= "0px";
			divTile.style.width			= this.nTilesize + "px";
			divTile.style.height		= this.nTilesize + "px";
			divTile.style.visibility = "hidden";
			
			var imgTile 		= document.createElement('img');
			imgTile.id 			= "imgTile_" + iCol + "_" + iRow;
			imgTile.src 		=	"img/blank.gif";
			imgTile.width		= this.nTilesize;
			imgTile.height	= this.nTilesize;
			
			divTile.insertBefore(imgTile, null);
			divTileMap.insertBefore(divTile, null);
		}
	}
	document.getElementById('divClipping').insertBefore(divTileMap, null);
	
	var frmCountClicks = document.createElement('iframe');
	frmCountClicks.id 					= "frmCountClicks";
	frmCountClicks.scrolling		= "no";
	frmCountClicks.width				= "0px";
	frmCountClicks.height				= "0px";
	frmCountClicks.frameborder	= "0";
	frmCountClicks.style.visibility	= "hidden";
	document.getElementById('divClipping').insertBefore(frmCountClicks, null);
}

/**
 * Creates the div and img elements to visualize the map
 * and inserts them into the DOM tree
 * (Debug version with borders around the tiles and labels)
 * @private
 */
function iwCreateTilesDebug()
{
	// if there is an old tile map remove that first from the DOM tree
	var divTileMap = document.getElementById("divTileMap");
	if (divTileMap)
	{
		divTileMap.parentNode.removeChild(divTileMap);
	}
	
	var divTileMap = document.createElement('div');
	divTileMap.id = "divTileMap";
	divTileMap.style.position = "absolute";
	divTileMap.style.left			= "0px";
	divTileMap.style.top 			= "0px";
	divTileMap.style.width		= this.nVisibleWidth + "px";
	divTileMap.style.height		= this.nVisibleHeight + "px";
	divTileMap.style.zIndex		= "0";
	
	var divTileMapBorder = document.createElement('div');
	divTileMapBorder.id = "divTileMapBorder";
	divTileMapBorder.style.border		= "2px solid black";
	divTileMapBorder.style.position = "absolute";
	divTileMapBorder.style.left			= nMainMapOffsetPixelX + "px";
	divTileMapBorder.style.top 			= nMainMapOffsetPixelY + "px";
	divTileMapBorder.style.width		= this.nVisibleWidth + "px";
	divTileMapBorder.style.height		= this.nVisibleHeight + "px";
	divTileMapBorder.style.zIndex		= "3";
	
	divTileMap.insertBefore(divTileMapBorder, null);
	
	for (var iRow = 0; iRow < this.nTotalTilesY; iRow++)
	{
		for (var iCol = 0; iCol < this.nTotalTilesX; iCol++)
		{
			var divTile = document.createElement('div');
			divTile.id = "divTile_" + iCol + "_" + iRow;
			divTile.style.position	= "absolute";
			divTile.style.border		= "1px solid black";
			divTile.style.zIndex		= "1";
			divTile.style.left			= "0px";
			divTile.style.top				= "0px";
			divTile.style.width			= this.nTilesize + "px";
			divTile.style.height		= this.nTilesize + "px";
			
			var imgTile 		= document.createElement('img');
			imgTile.id 			= "imgTile_" + iCol + "_" + iRow;
			imgTile.src 		=	"img/blank.gif";
			imgTile.width		= this.nTilesize;
			imgTile.height	= this.nTilesize;
			
			var divTileLabel = document.createElement('div');
			divTileLabel.style.position	= "absolute";
			divTileLabel.style.left	= "0px";
			divTileLabel.style.top	= "0px";
			divTileLabel.style.zIndex	= "3";
			divTileLabel.style.color	= "black";
			divTileLabel.innerHTML = "imgTile_" + iCol + "_" + iRow;
			
			divTile.insertBefore(imgTile, null);
			divTile.insertBefore(divTileLabel, null);
			divTileMap.insertBefore(divTile, null);
		}
	}
	document.getElementById('divClipping').insertBefore(divTileMap, null);
	
	var frmCountClicks = document.createElement('iframe');
	frmCountClicks.id 					= "frmCountClicks";
	frmCountClicks.scrolling		= "no";
	frmCountClicks.width				= "0px";
	frmCountClicks.height				= "0px";
	frmCountClicks.frameborder	= "0";
	frmCountClicks.style.visibility	= "hidden";
	document.getElementById('divClipping').insertBefore(frmCountClicks, null);
}

/**
 * Calculates the correct position for the tiles depending on visible part of the map
 * @private
 */
function iwPlaceTiles()
{
	// find the best tiles for this map view
	var aTopLeftBorderMeter = new Array(2);
	aTopLeftBorderMeter[0] = this.nCenterMeterX - this.nTotalTilesX / 2 * this.nTilesizeMeter;
	aTopLeftBorderMeter[1] = this.nCenterMeterY + this.nTotalTilesY / 2 * this.nTilesizeMeter;
	this.FindBestTilePosition(aTopLeftBorderMeter);
	
	// compute the screen position for the tiles
	var nMeterOffsetX		 =		aTopLeftBorderMeter[0]
													- (this.nCenterMeterX - this.nTotalTilesX / 2 * this.nTilesizeMeter);
	this.nTilesetStartX	 =		(this.nVisibleWidth / 2)
													- (this.nTotalTilesX * this.nTilesize / 2)
													+ nMeterOffsetX / this.nTilesizeMeter * this.nTilesize;
	
	var nMeterOffsetY		 =		aTopLeftBorderMeter[1]
													- (this.nCenterMeterY + this.nTotalTilesY / 2 * this.nTilesizeMeter);
	this.nTilesetStartY	 =		(this.nVisibleHeight / 2)
													- (this.nTotalTilesY * this.nTilesize / 2)
													- nMeterOffsetY / this.nTilesizeMeter * this.nTilesize;
													
	var nBorderTilesX = (this.nTotalTilesX - (this.nVisibleWidth / this.nTilesize)) / 2;
	var nBorderTilesY = (this.nTotalTilesY - (this.nVisibleHeight / this.nTilesize)) / 2;
	
	this.nMovedX = this.nTilesetStartX + nBorderTilesX * this.nTilesize;
	this.nMovedY = this.nTilesetStartY + nBorderTilesY * this.nTilesize;
	
	// move the tiles to their new position
	for (var nRow = 0; nRow < this.nTotalTilesY; nRow++)
	{
		for (var nCol = 0; nCol < this.nTotalTilesX; nCol++)
		{
			var nIndex = nRow * this.nTotalTilesX + nCol;
			var nPositionX = Math.floor(this.nTilesetStartX + nCol * this.nTilesize);
			var nPositionY = Math.floor(this.nTilesetStartY + nRow * this.nTilesize);
			var nTileCenterMeterX = 	aTopLeftBorderMeter[0]
															+ nCol * this.nTilesizeMeter
														 	+	0.5 * this.nTilesizeMeter;
			var nTileCenterMeterY = 	aTopLeftBorderMeter[1]
															- nRow * this.nTilesizeMeter
															-	0.5 * this.nTilesizeMeter;
			
			this.aTiles[nIndex].MoveTo(nPositionX, nPositionY);
			this.aTiles[nIndex].nCenterMeterX = nTileCenterMeterX;
			this.aTiles[nIndex].nCenterMeterY = nTileCenterMeterY;
		}
	}
}

/**
 * Sends an asynchronous request to the map server to prepare the given tiles
 * @private
 * @param (int) mapcx			Meter position of the map center
 * @param (int) mapcy			Meter position of the map center
 * @param (int) mapdx			Meter size of the map section
 * @param (int) mapdy			Meter size of the map section
 * @param (int) width			Pixel size of the map section
 * @param (int) height			Pixel size of the map section
 * @param (int) tilenx			Horizontal number of tiles
 * @param (int) tileny			Vertical number of tiles
 * @param (int) nTimestamp	Timestamp to identify this request
 */
function iwSendPrepareTilesRequest(	mapcx, mapcy,
																		mapdx, mapdy,
																		width, height,
																		tilenx, tileny,
																		nTimestamp)
{
	var sURL = 			this.sMapServerURL
								+	"GetTileMap.jsp?cmd=PrepareTiles"
								+	"&projc="				+	szMapProjection
								+ "&mapcx="				+ mapcx
								+ "&mapcy="				+ mapcy
								+ "&mapdx="				+ mapdx
								+ "&mapdy="				+ mapdy
								+ "&width="				+ width
								+ "&height="			+ height
								+ "&tilex="				+ this.nTilesize
								+ "&tiley="				+ this.nTilesize
								+ "&tilenx="			+ tilenx
								+ "&tileny="			+ tileny
								+ "&tileprefix="	+ "Tile_" + sSessionId + "_" + nTimestamp
								+ "&layerROUTE="	+ szRoutePrefix
								+ szExtraUrlParm;
	
	if (this.nMapMode == this.MAPMODE_AIR || this.nMapMode == this.MAPMODE_HYBRID)
		sURL += "&pictureformat=jpg&jpgquality=35&maptype=air";
	
	var x1 = mapcx - mapdx / 2;
	var x2 = mapcx + mapdx / 2;
	var y1 = mapcy - mapdy / 2;
	var y2 = mapcy + mapdy / 2;
	var oSectionExtent = new iwRectangle(x1, y1, x2, y2);
	
	var oRequest = new Object();
	oRequest.sURL = sURL;
	oRequest.nTimestamp = nTimestamp;
	oRequest.oSectionExtent = oSectionExtent;
	
	this.aQueueRequests.push(oRequest);
	this.RequestNext();
	
	//this.oAsynchroneTransferObject.PrepareTilesAsynchrone(sURL, nTimestamp, oSectionExtent);
}

function iwRequestNext()
{
	var oMapExtent = oTileset.GetMeterExtent();
	
	while (this.aQueueRequests.length > 0 && this.oAsynchroneTransferObject.HasFreeRequests())
	{
		var oRequest = this.aQueueRequests.pop();
		
		// check if the request has become obsolete and can be ignored
		if (oMapExtent.IsOverlappedBy(oRequest.oSectionExtent))
		{
			this.oAsynchroneTransferObject.PrepareTilesAsynchrone(oRequest.sURL, oRequest.nTimestamp);
		}
	}
}

/**
 * Prepares tiles for the whole map and starts loading them
 * @private
 */
function iwLoadWholeMap()
{
	iwLockControls();
	iwShowLabels(false);
	this.bCompletelyVisible = false;
	this.aQueueTiles.length			= 0;
	this.aQueueRequests.length	= 0;
	this.aQueueSection.length		= 0;

	if (!this.bIsOverview)
	{	
		if (this.nMapMode == this.MAPMODE_MAP)
		{
			iwChangeZoomLevel(MAX_MAPZOOMLEVEL, CHECKMODE_LOADMAP);
		}
		else
		{
			if (this.nTilesizeMeter < A_TILESIZE_METER[nCurrentLevel])
			{
				iwChangeZoomLevel(nCurrentLevel, CHECKMODE_GETINFO);
		
				this.nTilesizeMeter = A_TILESIZE_METER[nCurrentLevel];
				this.PlaceTiles();
			}
			else
			{
				var nMapWidthMeter = this.nTilesizeMeter * this.nVisibleWidth / this.nTilesize;
				var nMapHeightMeter = this.nTilesizeMeter * this.nVisibleHeight / this.nTilesize;
				iwGetMapInfo(nMapWidthMeter, nMapHeightMeter, CHECKMODE_LOADMAP);
			}
		}
	}
	
	// mark all tiles as inactive (waiting for this section to be loaded)
	for (var nTileY = 0; nTileY < this.nTotalTilesY; nTileY++)
	{
		for (var nTileX = 0; nTileX < this.nTotalTilesX; nTileX++)
		{
			this.aTiles[nTileX + nTileY * this.nTotalTilesX].nState = N_STATE_INACTIVE;
		}
	}
	
	// don't split the overview map in sections
	if (this.bIsOverview) 
	{
		this.LoadSection(0, 0, this.nTotalTilesX, this.nTotalTilesY);
		iwShowLabels(true);
	}
	else
	{
		var nLeft				= -1;
		var nMiddleX		= -1;
		var nRight			= -1;
		var nMinLeft		= 10000;
		var nMinMiddleX	= 10000;
		var nMinRight		= 10000;
		for (var nTileX = 0; nTileX < this.nTotalTilesX; nTileX++)
		{
			var nTilePosition = this.aTiles[nTileX].nPositionX;
			if (nTilePosition + this.nTilesize < 0)
			{
				if (nTilePosition < nMinLeft)
				{
					nMinLeft = nTilePosition;
					nLeft = nTileX;
				}
			}
			else if (nTilePosition < this.nVisibleWidth)
			{
				if (nTilePosition < nMinMiddleX)
				{
					nMinMiddleX = nTilePosition;
					nMiddleX = nTileX;
				}
			}
			else if (nTilePosition < nMinRight)
			{
				nMinRight = nTilePosition;
				nRight = nTileX;
			}
		}
		
		var nTop				= -1;
		var nMiddleY		= -1;
		var nBottom			= -1;
		var nMinTop			= 10000;
		var nMinMiddleY	= 10000;
		var nMinBottom	= 10000;
		for (var nTileY = 0; nTileY < this.nTotalTilesY; nTileY++)
		{
			var nTilePosition = this.aTiles[nTileY * this.nTotalTilesX].nPositionY;
			if (nTilePosition + this.nTilesize < 0)
			{
				if (nTilePosition < nMinTop)
				{
					nMinTop = nTilePosition;
					nTop = nTileY;
				}
			}
			else if (nTilePosition < this.nVisibleHeight)
			{
				if (nTilePosition < nMinMiddleY)
				{
					nMinMiddleY = nTilePosition;
					nMiddleY = nTileY;
				}
			}
			else if (nTilePosition < nMinBottom)
			{
				{
					nMinBottom = nTilePosition;
					nBottom = nTileY;
				}
			}
		}
		
		if (nLeft == -1)
			nLeft = nMiddleX; // nLeft = 0;
		
		if (nRight == -1)
			nRight = nLeft; // nRight = this.nTotalTilesX;
		
		if (nTop == -1)
			nTop = nMiddleY; // nTop = 0;
		
		if (nBottom == -1)
			nBottom = nTop; // nBottom = this.nTotalTilesY;
		
		// load the visible section first
		var oSection			= new Object();
		oSection.nLeft		=	nMiddleX;
		oSection.nTop			=	nMiddleY;
		oSection.nTilesX	=	iwIndexDistance(nRight, nMiddleX, this.nTotalTilesX);
		oSection.nTilesY	=	iwIndexDistance(nBottom, nMiddleY, this.nTotalTilesY);
		
		//set init nLocks for Mutex
		mutexCounter.nTiles = oSection.nTilesX * oSection.nTilesY;
		mutexCounter.syncInitLocks();
		
		this.LoadSection(oSection.nLeft, oSection.nTop, oSection.nTilesX, oSection.nTilesY);
		
		
		SetCurrentSliderPos();
		SetMapScalar((this.nVisibleWidth / this.nTilesize) * this.nTilesizeMeter, this.nVisibleWidth);
		iwShowLabels(true);

		var divTileMap = document.getElementById("divTileMap");
		if (divTileMap)
		{
			if (BrowserDetect.browser == "Safari" || BrowserDetect.browser == "Konqueror")
			{
				divTileMap.style.visibility = "visible";
			}
		}
		
		// add the left section to the queue if it is existant
		oSection					= new Object();
		oSection.nLeft		=	nLeft;
		oSection.nTop			=	nTop;
		oSection.nTilesX	=	iwIndexDistance(nMiddleX, nLeft, this.nTotalTilesX);
		oSection.nTilesY	=	this.nTotalTilesY;
		if (oSection.nTilesX > 0 && oSection.nTilesY > 0)
		{
			this.aQueueSection.push(oSection);
			this.ResetSection(oSection.nLeft, oSection.nTop, oSection.nTilesX, oSection.nTilesY);
		}
		
		// add the right section to the queue if it is existant
		oSection					= new Object();
		oSection.nLeft		=	nRight;
		oSection.nTop			=	nTop;
		oSection.nTilesX	=	iwIndexDistance(nLeft, nRight, this.nTotalTilesX);
		oSection.nTilesY	=	this.nTotalTilesY;
		if (oSection.nTilesX > 0 && oSection.nTilesY > 0)
		{
			this.aQueueSection.push(oSection);
			this.ResetSection(oSection.nLeft, oSection.nTop, oSection.nTilesX, oSection.nTilesY);
		}

		// add the upper section to the queue if it is existant
		oSection					= new Object();
		oSection.nLeft		=	nMiddleX;
		oSection.nTop			=	nTop;
		oSection.nTilesX	=	iwIndexDistance(nRight, nMiddleX, this.nTotalTilesX);
		oSection.nTilesY	=	iwIndexDistance(nMiddleY, nTop, this.nTotalTilesY);
		if (oSection.nTilesX > 0 && oSection.nTilesY > 0)
		{
			this.aQueueSection.push(oSection);
			this.ResetSection(oSection.nLeft, oSection.nTop, oSection.nTilesX, oSection.nTilesY);
		}
		
		// add the bottom section to the queue if it is existant
		oSection					= new Object();
		oSection.nLeft		=	nMiddleX;
		oSection.nTop			=	nBottom;
		oSection.nTilesX	=	iwIndexDistance(nRight, nMiddleX, this.nTotalTilesX);
		oSection.nTilesY	=	iwIndexDistance(nTop, nBottom, this.nTotalTilesY);		
		if (oSection.nTilesX > 0 && oSection.nTilesY > 0)
		{
			this.aQueueSection.push(oSection);
			this.ResetSection(oSection.nLeft, oSection.nTop, oSection.nTilesX, oSection.nTilesY);
		}
	}
}

/**
 * Helper function to calculate the distance between two tile indices
 * @param (int) nIndex1 First index
 * @param (int) nIndex2 Second index
 * @param (int) nTotal Number of tiles in a row (horizontal distance) or column (vertical distance)
 * @private
 * @type int
 * @return The number of tiles between the indices
 */
function iwIndexDistance(nIndex1, nIndex2, nTotal)
{
	var nDistance = nIndex1 - nIndex2;
	if (nDistance < 0)
		nDistance += nTotal;
	return nDistance;
}

/**
 * Loads a section of the map
 * @private
 * @param (int) nLeft Column index describing the start of the section
 * @param (int) nTop Row index describing the start of the section
 * @param (int) nTilesX Width of the section in tiles
 * @param (int) nTilesY Height of the section in tiles
 */
function iwLoadSection(nLeft, nTop, nTilesX, nTilesY)
{
	// get a timestamp to identify the tiles on the server
	var nTimestamp = new Date().getTime() + Math.random();
	
	var nCenterMeterX =		this.aTiles[nLeft].nCenterMeterX											// left tile
											- this.nTilesizeMeter / 2																// left map border
											+	(nTilesX * this.nTilesizeMeter) / 2										// map center
	var nCenterMeterY =		this.aTiles[nTop * this.nTotalTilesX].nCenterMeterY		// upper tile
											-	(nTilesY * this.nTilesizeMeter) / 2										// upper map border
											+ this.nTilesizeMeter / 2;															// map center
	
	this.SendPrepareTilesRequest(	nCenterMeterX,
																nCenterMeterY,
																nTilesX * this.nTilesizeMeter,
																nTilesY * this.nTilesizeMeter,
																nTilesX * this.nTilesize,
																nTilesY * this.nTilesize,
																nTilesX,
																nTilesY,
																nTimestamp);
	
	// save the resulting image sources in the tile objects
	for (var nRow = 0; nRow < nTilesY; nRow++)
	{
		for (var nCol = 0; nCol < nTilesX; nCol++)
		{
			var nTileRow = (nRow + nTop)	% this.nTotalTilesY;
			var nTileCol = (nCol + nLeft)	% this.nTotalTilesX;
			var nIndex = nTileRow * this.nTotalTilesX + nTileCol;
			var sTilename = this.GetTileImageName(sSessionId,	nTimestamp, nRow + 1, nCol + 1);
			
			if (this.aTiles[nIndex].nState == N_STATE_INACTIVE)
			{
				this.aTiles[nIndex].nState			= N_STATE_WAITING;
				this.aTiles[nIndex].nTimestamp	= nTimestamp;
				this.aTiles[nIndex].sSource			= sTilename;
			}
		}
	}
}

/**
 * Clears a section of the map
 * @private
 * @param (int) nLeft Column index describing the start of the section
 * @param (int) nTop Row index describing the start of the section
 * @param (int) nTilesX Width of the section in tiles
 * @param (int) nTilesY Height of the section in tiles
 */
function iwResetSection(nLeft, nTop, nTilesX, nTilesY)
{
	for (var nRow = 0; nRow < nTilesY; nRow++)
	{
		for (var nCol = 0; nCol < nTilesX; nCol++)
		{
			var nTileRow = (nRow + nTop)	% this.nTotalTilesY;
			var nTileCol = (nCol + nLeft)	% this.nTotalTilesX;
			var nIndex = nTileRow * this.nTotalTilesX + nTileCol;
			this.aTiles[nIndex].oImg.src = 'img/white.gif';
		}
	}
}

/**
 * Returns the filename for a tile depending on the session, timestamp, index and map mode
 * @private
 * @param (String) sSessionId ID of this HTTP session
 * @param (int) nTimestamp Timestamp of this request
 * @param (int) nRow Index of this tile in this request
 * @param (int) nRow Index of this tile in this request
 * @type String
 * @return The filename
 */
function iwGetTileImageName(sSessionId, nTimestamp, nRow, nCol)
{
	var sExtension;
	
	if (this.nMapMode == this.MAPMODE_MAP)
		sExtension = ".gif";
	else
		sExtension = ".jpg";
	
	return "Tile_" + sSessionId + "_" + nTimestamp + "_" + nRow + "_" + nCol + sExtension;
}

/**
 * Moves the whole map in the given direction
 * @private
 * @param (int) nOffsetX	Number of pixels to move in horizontal direction
 * @param (int) nOffsetY	Number of pixels to move in vertical direction
 */
function iwMoveMap(nOffsetX, nOffsetY)
{
	this.nCenterMeterX -= nOffsetX / this.nTilesize * this.nTilesizeMeter;
	this.nCenterMeterY += nOffsetY / this.nTilesize * this.nTilesizeMeter;
	this.nTilesetStartX += nOffsetX;
	this.nTilesetStartY += nOffsetY;
	
	this.nMovedX += nOffsetX;
	this.nMovedY += nOffsetY;
	
	if (bDragLabels == true)
	{
		var divLabels = document.getElementById("divLabels");
		var nLeft = parseInt(divLabels.style.left) + nOffsetX;
		var nTop = parseInt(divLabels.style.top) + nOffsetY;
		divLabels.style.left = nLeft;
		divLabels.style.top = nTop;

		for (var nAddress = 0; nAddress < nShowAddress; nAddress++)
		{
			var divAddress = document.getElementById("Address" + nAddress);
			if (divAddress)
			{
				var nLeft	= parseInt(divAddress.style.left) + nOffsetX;
				var nTop	= parseInt(divAddress.style.top) + nOffsetY;
				
				divAddress.style.left	= nLeft;
				divAddress.style.top	= nTop;
			}
		}
		
		if (typeof MovePOILayer != 'undefined')
			MovePOILayer(nOffsetX, nOffsetY);
	}
	
	for (var nTile = 0; nTile < this.nTotalTilesX * this.nTotalTilesY; nTile++)
		this.aTiles[nTile].Move(nOffsetX, nOffsetY);
	
	if (this.fOnMove)
		this.fOnMove();
		
	var bDiscarded = false;
	if (this.nMovedX > this.nTilesize)
	{
		var nDiscard = Math.floor(this.nMovedX / this.nTilesize);
		this.DiscardRightBorder(nDiscard);
		this.nMovedX -= (this.nTilesize * nDiscard);
		bDiscarded = true;
	}
	else if (this.nMovedX < -this.nTilesize)
	{
		var nDiscard = - Math.floor(this.nMovedX / this.nTilesize);
		this.DiscardLeftBorder(nDiscard);
		this.nMovedX += (this.nTilesize * nDiscard);
		bDiscarded = true;
	}
	else if (this.nMovedY > this.nTilesize)
	{
		var nDiscard = Math.floor(this.nMovedY / this.nTilesize);
		this.DiscardLowerBorder(nDiscard);
		this.nMovedY -= (this.nTilesize * nDiscard);
		bDiscarded = true;
	}
	else if (this.nMovedY < -this.nTilesize)
	{
		var nDiscard = - Math.floor(this.nMovedY / this.nTilesize);
		this.DiscardUpperBorder(nDiscard);
		this.nMovedY += (this.nTilesize * nDiscard);
		bDiscarded = true;
	}
	
	if (bDiscarded)
	{
		this.aQueueTiles.length = 0;
		this.BuildQueue(this.nVisibleWidth/2, this.nVisibleHeight/2);
		this.Move(0, 0);
	}
}

/**
 * Moves the whole map to the given coordinate (a meter position in LCC Europe)
 * @public
 * @param (int) nPositionX	Horizontal position
 * @param (int) nPositionY	Vertical position
 */
function iwMoveMapTo(nPositionX, nPositionY)
{
	this.nCenterMeterX = nPositionX;
	this.nCenterMeterY = nPositionY;
	
	this.Hide();
	this.PlaceTiles();
	this.LoadWholeMap();
}

/**
 * Discards the tiles on the left border and moves them to the right side instead
 * @private
 * param (int) nBorderNum Number of columns to discard
 */
function iwDiscardLeftBorder(nBorderNum)
{
	var nTimestamp = new Date().getTime() + Math.random();
	
	this.nTilesetStartX += this.nTilesize;
	var nLeftCol	= this.FindLeftCol();
	var nRightCol	= (nLeftCol + this.nTotalTilesX - 1) % this.nTotalTilesX;
	var nTopRow		= this.FindTopRow();
	
	// request a new strip of the map
	var nmapcx = this.aTiles[nRightCol].nCenterMeterX + this.nTilesizeMeter * (1 + 0.50 * (nBorderNum-1) );
	var nmapcy = 		this.aTiles[nTopRow * this.nTotalTilesX].nCenterMeterY
								+ this.nTilesizeMeter / 2
								- (this.nTotalTilesY / 2) * this.nTilesizeMeter;
	
	this.SendPrepareTilesRequest(	nmapcx,
																nmapcy,
																this.nTilesizeMeter * nBorderNum,
																this.nTilesizeMeter * this.nTotalTilesY,
																this.nTilesize * nBorderNum,
																this.nTilesize * this.nTotalTilesY,
																nBorderNum,
																this.nTotalTilesY,
																nTimestamp);
	
	for (var nBorder = 1; nBorder <= nBorderNum; nBorder++)
	{
		// reposition the leftmost tile from all rows
		for (var nRow = 0; nRow < this.nTotalTilesY; nRow++)
		{
			var nTile = (nRow + nTopRow)%this.nTotalTilesY * this.nTotalTilesX + nLeftCol;
			
			if (this.aTiles[nTile].oTimeout)
			{
				window.clearTimeout(this.aTiles[nTile].oTimeout);
				this.aTiles[nTile].oTimeout = null;
			}
			this.aTiles[nTile].nState = N_STATE_WAITING;
			this.aTiles[nTile].oDiv.style.visibility = "hidden";
			this.aTiles[nTile].nCenterMeterX += this.nTotalTilesX * this.nTilesizeMeter;
			this.aTiles[nTile].MoveTo(this.aTiles[nRightCol].nPositionX + this.nTilesize,
																this.aTiles[nTile].nPositionY);
			this.aTiles[nTile].nTimestamp = nTimestamp;
			this.aTiles[nTile].sSource = this.GetTileImageName(sSessionId, nTimestamp, nRow + 1, nBorder);
		}
	
		this.nTilesetStartX += this.nTilesize;
		nLeftCol	= this.FindLeftCol();
		nRightCol	= (nLeftCol + this.nTotalTilesX - 1) % this.nTotalTilesX;
		nTopRow		= this.FindTopRow();
	}
}

/**
 * Discards the tiles on the right border and moves them to the left side instead
 * @private
 * param (int) nBorderNum Number of columns to discard
 */
function iwDiscardRightBorder(nBorderNum)
{
	var nTimestamp = new Date().getTime() + Math.random() + 1;

	this.nTilesetStartX -= this.nTilesize;
	var nLeftCol	= this.FindLeftCol();
	var nRightCol	= (nLeftCol + this.nTotalTilesX - 1) % this.nTotalTilesX;
	var nTopRow		= this.FindTopRow();

	// request a new strip of the map
	var nmapcx = this.aTiles[nLeftCol].nCenterMeterX - this.nTilesizeMeter * (1 + 0.50 * (nBorderNum-1) );
	var nmapcy = 		this.aTiles[nTopRow * this.nTotalTilesX].nCenterMeterY
							+ this.nTilesizeMeter / 2
							- (this.nTotalTilesY / 2) * this.nTilesizeMeter;

	this.SendPrepareTilesRequest(	nmapcx,
																nmapcy,
																this.nTilesizeMeter * nBorderNum,
																this.nTilesizeMeter * this.nTotalTilesY,
																this.nTilesize* nBorderNum,
																this.nTilesize * this.nTotalTilesY,
																nBorderNum,
																this.nTotalTilesY,
																nTimestamp);
	
	for (var nBorder = nBorderNum; nBorder > 0; nBorder--)
	{
		// reposition the rightmost tile from all rows
		for (var nRow = 0; nRow < this.nTotalTilesY; nRow++)
		{
			// run through the tiles from up to down
			var nTile = (nRow + nTopRow)%this.nTotalTilesY * this.nTotalTilesX + nRightCol;
			
			if (this.aTiles[nTile].oTimeout)
			{
				window.clearTimeout(this.aTiles[nTile].oTimeout);
				this.aTiles[nTile].oTimeout = null;
			}
			this.aTiles[nTile].nState = N_STATE_WAITING;
			this.aTiles[nTile].oDiv.style.visibility = "hidden";
			this.aTiles[nTile].nCenterMeterX -= this.nTotalTilesX * this.nTilesizeMeter;
			this.aTiles[nTile].MoveTo(this.aTiles[nLeftCol].nPositionX - this.nTilesize,
																this.aTiles[nTile].nPositionY);
			this.aTiles[nTile].nTimestamp = nTimestamp;
			this.aTiles[nTile].sSource = this.GetTileImageName(sSessionId, nTimestamp, nRow + 1, nBorder);
		}
		
		this.nTilesetStartX -= this.nTilesize;
		nLeftCol	= this.FindLeftCol();
		nRightCol	= (nLeftCol + this.nTotalTilesX - 1) % this.nTotalTilesX;
		nTopRow		= this.FindTopRow();
	}
}

/**
 * Discards the tiles on the lower border and moves them to the upper side instead
 * @private
 * param (int) nBorderNum Number of rows to discard
 */
function iwDiscardLowerBorder(nBorderNum)
{
	var nTimestamp = new Date().getTime() + Math.random();
	
	this.nTilesetStartY -= this.nTilesize;
	var nLeftCol		= this.FindLeftCol();
	var nTopRow			= this.FindTopRow();
	var nBottomRow	= (nTopRow + this.nTotalTilesY - 1) % this.nTotalTilesY;
	
	// request a new strip of the map
	var nmapcx = 		this.aTiles[nLeftCol].nCenterMeterX
								- this.nTilesizeMeter / 2
								+ (this.nTotalTilesX / 2) * this.nTilesizeMeter;
	var nmapcy = 		this.aTiles[nTopRow * this.nTotalTilesX].nCenterMeterY
								+ this.nTilesizeMeter * (1 + 0.50 * (nBorderNum-1) );
	
	this.SendPrepareTilesRequest(	nmapcx,
																nmapcy,
																this.nTilesizeMeter * this.nTotalTilesX,
																this.nTilesizeMeter * nBorderNum,
																this.nTilesize * this.nTotalTilesX,
																this.nTilesize * nBorderNum,
																this.nTotalTilesX,
																nBorderNum,
																nTimestamp);

	for (var nBorder = nBorderNum; nBorder > 0; nBorder--)
	{	
		// reposition the lowest tile from all columns
		for (var nCol = 0; nCol < this.nTotalTilesX; nCol++)
		{
			// run through the tiles from left to right
			var nTile = nBottomRow * this.nTotalTilesX + (nLeftCol + nCol)%this.nTotalTilesX;
			
			if (this.aTiles[nTile].oTimeout)
			{
				window.clearTimeout(this.aTiles[nTile].oTimeout);
				this.aTiles[nTile].oTimeout = null;
			}
			this.aTiles[nTile].nState = N_STATE_WAITING;
			this.aTiles[nTile].oDiv.style.visibility = "hidden";
			this.aTiles[nTile].nCenterMeterY += this.nTotalTilesY * this.nTilesizeMeter;
			this.aTiles[nTile].MoveTo(this.aTiles[nTile].nPositionX,
																this.aTiles[nTopRow * this.nTotalTilesX].nPositionY - this.nTilesize);
			this.aTiles[nTile].nTimestamp = nTimestamp;
			this.aTiles[nTile].sSource = this.GetTileImageName(sSessionId, nTimestamp, nBorder, nCol + 1);
		}
		
		this.nTilesetStartY -= this.nTilesize;
		nLeftCol		= this.FindLeftCol();
		nTopRow			= this.FindTopRow();
		nBottomRow = (nTopRow + this.nTotalTilesY - 1) % this.nTotalTilesY;
	}
}

/**
 * Discards the tiles on the upper border and moves them to the lower side instead
 * @private
 * param (int) nBorderNum Number of rows to discard
 */
function iwDiscardUpperBorder(nBorderNum)
{
	var nTimestamp = new Date().getTime() + Math.random();
	
	this.nTilesetStartY += this.nTilesize;
	var nLeftCol		= this.FindLeftCol();
	var nTopRow			= this.FindTopRow();
	var nBottomRow	= (nTopRow + this.nTotalTilesY - 1) % this.nTotalTilesY;
	
	// request a new strip of the map
	var nmapcx = 		this.aTiles[nLeftCol].nCenterMeterX
								- this.nTilesizeMeter /2
								+ (this.nTotalTilesX / 2) * this.nTilesizeMeter;
	var nmapcy = 		this.aTiles[nBottomRow * this.nTotalTilesX].nCenterMeterY
								- this.nTilesizeMeter * (1 + 0.50 * (nBorderNum-1) );
	
	this.SendPrepareTilesRequest(	nmapcx,
																nmapcy,
																this.nTilesizeMeter * this.nTotalTilesX,
																this.nTilesizeMeter * nBorderNum,
																this.nTilesize * this.nTotalTilesX,
																this.nTilesize * nBorderNum,
																this.nTotalTilesX,
																nBorderNum,
																nTimestamp);

	for (var nBorder = 1; nBorder <= nBorderNum; nBorder++)
	{		
		// reposition the upmost tile from all columns
		for (var nCol = 0; nCol < this.nTotalTilesX; nCol++)
		{
			// run through the tiles from left to right
			var nTile = nTopRow * this.nTotalTilesX + (nLeftCol + nCol)%this.nTotalTilesX;
			
			if (this.aTiles[nTile].oTimeout)
			{
				window.clearTimeout(this.aTiles[nTile].oTimeout);
				this.aTiles[nTile].oTimeout = null;
			}
			this.aTiles[nTile].nState = N_STATE_WAITING;
			this.aTiles[nTile].oDiv.style.visibility = "hidden";
			this.aTiles[nTile].nCenterMeterY -= this.nTotalTilesY * this.nTilesizeMeter;
			this.aTiles[nTile].MoveTo(this.aTiles[nTile].nPositionX,
																this.aTiles[nBottomRow * this.nTotalTilesX].nPositionY + this.nTilesize);
			this.aTiles[nTile].nTimestamp = nTimestamp;
			this.aTiles[nTile].sSource = this.GetTileImageName(sSessionId, nTimestamp, nBorder, nCol + 1);
		}
		
		this.nTilesetStartY += this.nTilesize;
		nLeftCol		= this.FindLeftCol();
		nTopRow			= this.FindTopRow();
		nBottomRow	= (nTopRow + this.nTotalTilesY - 1) % this.nTotalTilesY;
	}
}

/**
 * Returns the index of the currently upmost tile row
 * @private
 * @type int
 * @return The row index
 */
function iwFindTopRow()
{
	var nMinRow	= 0;
	var nMinRowValue = this.aTiles[0].nPositionY;
	
	for (var nRow = 0; nRow < this.nTotalTilesY; nRow++)
	{
		var nIndex = nRow * this.nTotalTilesX;
		
		if (this.aTiles[nIndex].nPositionY < nMinRowValue)
		{
			nMinRow = nRow;
			nMinRowValue = this.aTiles[nIndex].nPositionY;
		}
	}
	
	return nMinRow;
}

/**
 * Returns the index of the currently leftmost tile column
 * @private
 * @type int
 * @return The column index
 */
function iwFindLeftCol()
{
	var nMinCol	= 0;
	var nMinColValue = this.aTiles[0].nPositionX;
	
	for (var nCol = 0; nCol < this.nTotalTilesX; nCol++)
	{
		var nIndex = nCol;
		
		if (this.aTiles[nIndex].nPositionX < nMinColValue)
		{
			nMinCol = nCol;
			nMinColValue = this.aTiles[nIndex].nPositionX;
		}
	}
	return nMinCol;
}

/**
 * Builds the loading queue based on euclidian distance to a given coordinate
 * @private
 * @param (int) nCenterX Pixel coordinate
 * @param (int) nCenterY Pixel coordinate 
 */
function iwBuildQueue(nCenterX, nCenterY)
{
	// Compute the weights
	for (var nTile = 0; nTile < this.aTiles.length; nTile++)
		this.aTiles[nTile].ComputeWeight(nCenterX, nCenterY);
	
	// Build the queue
	this.aQueueTiles.length = 0;
	for (var nTile = 0; nTile < this.aTiles.length; nTile++)
	{
		if (this.aTiles[nTile].nState == N_STATE_QUEUED)
		{
			this.aQueueTiles.push(this.aTiles[nTile]);
			//mutexCounter.syncIncrease();			
		}
	}
	
	this.aQueueTiles.sort(iwCompareTiles);
}

/**
 * Loads the next map section from the queue
 * @private
 */
function iwLoadNextSection()
{
	var oSection = null;
	if (this.aQueueSection.length > 0)
	{
		oSection = this.aQueueSection.pop();
		this.LoadSection(oSection.nLeft, oSection.nTop, oSection.nTilesX, oSection.nTilesY);
	}
}

/**
 * Helper function for sorting the queue
 * @private
 * @param (iwTile) oTile1 First tile to be compared
 * @param (iwTile) oTile2 Second tile to be compared
 * @type float
 * @return The difference of the weights
 */
function iwCompareTiles(oTile1, oTile2)
{
	return(oTile2.nWeight - oTile1.nWeight);
}

/**
 * Loads the next tile from the queue
 * @private
 */
function iwLoadNext()
{
	//console.log("iwLoadNex",  nLocks, bControlsBlocked);
	while (this.aQueueTiles.length > 0 && this.nLoadingTiles < N_MAX_LOADING_TILES)
	{
		var oTile = this.aQueueTiles.pop();
		oTile.LoadTile();
		this.nLoadingTiles++;
		
	}

	//check for all visible tiles are loaded // new Reihfolge
	var aTiles = this.GetVisibleTiles();
	this.bCompletelyVisible = true;
	for (var nTile = 0; nTile < aTiles.length; nTile++)
	{
		if (aTiles[nTile].nState != N_STATE_COMPLETE)
		{
			this.bCompletelyVisible = false;
			break;
		}
	}

	//if (this.aQueueTiles.length == 0 && this.bCompletelyVisible) // mit this.bCompletelyVisible ist auch nicht richtig
	if (this.bCompletelyVisible) // mit this.bCompletelyVisible ist auch nicht richtig
	{
		if (this.oZoomObject.isExistZoomDIVSet())
		{
			// tile loading after a zoom is complete, so remove the tiles for the zoom effect
			if (!this.oZoomObject.bPrepareZoomOnSlider)
			{
				this.oZoomObject.removeZoomDIVSet();
			
				if (this.fOnZoomEnd)
					this.fOnZoomEnd();
			}
		}
		//else //xida test
		{
			iwReleaseControls();
			//console.log("iwLoadNex",  nLocks, bControlsBlocked);
		}
	}
}

/**
 * Adds all waiting tiles with the given timestamp to the queue
 * @private
 * param (int) nTimestamp The timestamp to be added
 */
function iwAddTilesToQueue(nTimestamp)
{
	for (var nTile = 0; nTile < this.aTiles.length; nTile++)
	{
		if (this.aTiles[nTile].nState == N_STATE_WAITING)
		{
			if (this.oAsynchroneTransferObject.bAJAXSupported)
			{
				if (this.aTiles[nTile].nTimestamp == nTimestamp)
				{
					this.aTiles[nTile].nState = N_STATE_QUEUED;
				}
			}
			else
			{
				if (this.aTiles[nTile].nTimestamp <= nTimestamp)
				{
					this.aTiles[nTile].nState = N_STATE_QUEUED;
				}
			}
		}
	}
	this.BuildQueue(this.nVisibleWidth/2, this.nVisibleHeight/2);
	this.LoadNext();
}

/**
 * Chooses the next position on the grid for a given 2d vector
 * @public
 * @param (int[]) aPosition A meter position in LCC Europe
 */
function iwFindBestTilePosition(aPosition)
{	
	var nPosX1 = Math.floor(aPosition[0] / this.nTilesizeMeter) * this.nTilesizeMeter;
	var nPosX2 = Math.ceil(aPosition[0] / this.nTilesizeMeter) * this.nTilesizeMeter;
	if (aPosition[0] - nPosX1 < nPosX2 - aPosition[0])
		aPosition[0] = nPosX1;
	else
		aPosition[0] = nPosX2;
	
	var nPosY1 = Math.floor(aPosition[1] / this.nTilesizeMeter) * this.nTilesizeMeter;
	var nPosY2 = Math.ceil(aPosition[1] / this.nTilesizeMeter) * this.nTilesizeMeter;
	if (aPosition[1] - nPosY1 < nPosY2 - aPosition[1])
		aPosition[1] = nPosY1;
	else
		aPosition[1] = nPosY2;
}

/**
 * Computes the screen coordinate in pixels for a position on the map
 * @public
 * @param (float) nMeterX A horizontal meter position in LCC Europe
 * type int
 * return The pixel position
 */
function iwMeterToPixelX(nMeterX)
{
	var nLeftMeter =	 this.nCenterMeterX 
									- (this.nVisibleWidth / 2) * (this.nTilesizeMeter / this.nTilesize);
	return (nMeterX - nLeftMeter) / this.nTilesizeMeter * this.nTilesize;
}

/**
 * Computes the screen coordinate in pixels for a position on the map
 * @public
 * @param (float) nMeterY A vertical meter position in LCC Europe
 * type int
 * return The pixel position
 */
function iwMeterToPixelY(nMeterY)
{
	var nTopMeter =		 this.nCenterMeterY 
									+ (this.nVisibleHeight / 2) * (this.nTilesizeMeter / this.nTilesize);
	return (nTopMeter - nMeterY) / this.nTilesizeMeter * this.nTilesize;
}

/**
 * Computes the map coordinate in meters for a screen position
 * @public
 * @param (int) nPixelX A horizontal screen position pixels
 * type float
 * return The meter position in LCC Europe
 */
function iwPixelToMeterX(nPixelX)
{
	var nLeftMeter =	 this.nCenterMeterX
									- (this.nVisibleWidth / 2) * (this.nTilesizeMeter / this.nTilesize);
	return(nLeftMeter + nPixelX / this.nTilesize * this.nTilesizeMeter);
}

/**
 * Computes the map coordinate in meters for a screen position
 * @public
 * @param (int) nPixelY A vertical screen position pixels
 * type float
 * return The meter position in LCC Europe
 */
function iwPixelToMeterY(nPixelY)
{
	var nTopMeter =		 this.nCenterMeterY 
									+ (this.nVisibleHeight / 2) * (this.nTilesizeMeter / this.nTilesize);
	return(nTopMeter - nPixelY / this.nTilesize * this.nTilesizeMeter);
}

/**
 * Returns an array with all tiles in the visible part of the map
 * @private
 * type iwTile[]
 * return The visible tiles
 */
function iwGetVisibleTiles()
{
	var aVisibleTiles = new Array(0);
	
	for (var nRow = 0; nRow < this.nTotalTilesY; nRow++)
	{
		for (var nCol = 0; nCol < this.nTotalTilesX; nCol++)
		{
			var nIndex = nRow * this.nTotalTilesX + nCol;
			
			var nTileLeft		= this.aTiles[nIndex].nPositionX;
			var nTileRight	= nTileLeft + this.nTilesize;
			var nTileTop		= this.aTiles[nIndex].nPositionY;
			var nTileBottom	= nTileTop + this.nTilesize;
			
			if (		(		(nTileLeft >= 0 && nTileLeft < this.nVisibleWidth)
								||(nTileRight >= 0 && nTileRight < this.nVisibleWidth))
					&&	(		(nTileTop >= 0 && nTileTop < this.nVisibleHeight)
								||(nTileBottom >= 0 && nTileBottom < this.nVisibleHeight)))
			{
				aVisibleTiles.push(this.aTiles[nIndex]);
			}
		}
	}
	
	return(aVisibleTiles);
}

/**
 * Hides the whole map
 * @public
 */
function iwHide()
{
	for (var nTile = 0; nTile < this.aTiles.length; nTile++)
	{
		this.aTiles[nTile].oDiv.style.visibility = "hidden";
	}
}

function iwFindBestScalingLevel()
{
	//luft
	var nBestFit = 0;
	var nBestFitValue = Math.abs(A_TILESIZE_METER[nMaxZoomLevel] - this.nTilesizeMeter);
	
	var nTempBestFit = 0;
	//for (var nLevel = 0; nLevel < A_TILESIZE_METER.length; nLevel++)
	for (var nLevel = nMaxZoomLevel+1; nLevel < A_TILESIZE_METER.length; nLevel++)
	{
		var nDifference = Math.abs(A_TILESIZE_METER[nLevel] - this.nTilesizeMeter)
		if (nDifference < nBestFitValue)
		{
			nTempBestFit = nLevel;
			nBestFit = nLevel;
			nBestFitValue = nDifference;
		}
	}
	
	if ((nTempBestFit - nMaxZoomLevel) < 0)
		return 0;
	else
		return nTempBestFit - nMaxZoomLevel;
}

function iwZoomInLevel()
{
	//if (bControlsBlocked)
	//	return false;
	if (mutexCounter.getLocks() != 0)
		return false;	
	
	var nScalingLevel = this.FindBestScalingLevel();
	nScalingLevel--;
	this.SetZoomLevel(nScalingLevel);
}

function iwZoomOutLevel()
{
	//if (bControlsBlocked)
	//	return false;
	if (mutexCounter.getLocks() != 0)
		return false;
	
	var nScalingLevel = this.FindBestScalingLevel();
	nScalingLevel++;
	this.SetZoomLevel(nScalingLevel);
}

function iwSetZoomLevel(ScalingLevel)
{
	var nScalingLevel = ScalingLevel + nMaxZoomLevel;
	//if (bControlsBlocked)
	//	return false;
	if (mutexCounter.getLocks() != 0)
		return false;	
	
	if (nScalingLevel >= nMaxZoomLevel && nScalingLevel < A_TILESIZE_METER.length)
	{
		if (oTileset.fOnZoomStart)
			oTileset.fOnZoomStart();
	
		this.nMovedX = 0;
		this.nMovedY = 0;
		
		this.oAsynchroneTransferObject.AbortAllRequests();
		iwLockControls();
		iwShowLabels(false);
		
		this.aQueueTiles.length = 0;
		
		var nFactor = this.nTilesizeMeter / A_TILESIZE_METER[nScalingLevel];
		this.oZoomObject.ZoomByStep(nFactor);
		
		this.nTilesizeMeter =	A_TILESIZE_METER[nScalingLevel];
		
		if (this.bHasOverview)
		{
			var nNewTilesizeMeter = 	this.nTilesizeMeter 
															* this.nOverviewFactor 
															* (this.nVisibleWidth / this.nOverviewSize);
			frames["frmOverview"].ChangeTilesizeMeter(nNewTilesizeMeter);
		}
		
		if (!oTileset.oZoomObject.bCloneTiles)
		{
			oTileset.CreateTiles();
			
			var nIndex = 0;
			for (var iRow = 0; iRow < oTileset.nTotalTilesY; iRow++)
			{
				for (var iCol = 0; iCol < oTileset.nTotalTilesX; iCol++)
				{
					oTileset.aTiles[nIndex].oDiv = document.getElementById("divTile_" + iCol + "_" + iRow);
					oTileset.aTiles[nIndex].oImg = document.getElementById("imgTile_" + iCol + "_" + iRow);
					
					nIndex ++;
				}
			}
		}
		oTileset.Hide();
		oTileset.PlaceTiles();
		this.LoadWholeMap();
	}
}

/**
 * Computes a (horizontal) pixel positon dependant of the map size.
 *
 * Use positive values for a position relative to the upper left corner and 
 * negative values for a position relative to the lower right corner
 * @private
 * @param (int) nPositionX Horizontal position
 */
function iwGetLeft(nPositionX)
{
	if (nPositionX >= 0)
		return nPositionX;
	else
		return this.nVisibleWidth + nPositionX;
}

/**
 * Computes a (vertical) pixel positon dependant of the map size.
 *
 * Use positive values for a position relative to the upper left corner and 
 * negative values for a position relative to the lower right corner
 * @private
 * @param (int) nPositionY Vertical position
 */
function iwGetTop(nPositionY)
{
	if (nPositionY >= 0)
		return nPositionY;
	else
		return this.nVisibleHeight + nPositionY;
}

function iwGetMeterExtent()
{	
	var oTopLeftTile = this.aTiles[this.FindTopRow() * this.nTotalTilesX + this.FindLeftCol()];
	var x1 = oTopLeftTile.nCenterMeterX - this.nTilesizeMeter / 2;
	var y2 = oTopLeftTile.nCenterMeterY + this.nTilesizeMeter / 2;
	var x2 = x1 + this.nTotalTilesX * this.nTilesizeMeter;
	var y1 = y2 - this.nTotalTilesY * this.nTilesizeMeter;
	
	return new iwRectangle(x1, y1, x2, y2);
}

/**
 * Returns the width of the map in meters
 * @public
 * @type float
 * @return The map width
 */
function iwGetMeterWidth()
{
	return this.nTilesizeMeter * (this.nVisibleWidth / this.nTilesize)
}

/**
 * Returns the height of the map in meters
 * @public
 * @type float
 * @return The map height
 */
function iwGetMeterHeight()
{
	return this.nTilesizeMeter * (this.nVisibleHeight / this.nTilesize)
}

/**
 * Returns the meter position of the left map border
 * @public
 * @type int
 */
function iwGetMeterPosLeft()
{
	return this.PixelToMeterX(0);
}

/**
 * Returns the meter position of the upper map border
 * @public
 * @type int
 */
function iwGetMeterPosTop()
{
	return this.PixelToMeterY(0);
}

/**
 * Returns the width of the map in pixels
 * @public
 * @type int
 */
function iwGetPixelWidth() 
{
	return this.nVisibleWidth;
}

/**
 * Returns the height of the map in pixels
 * @public
 * @type int
 */
function iwGetPixelHeight() 
{
	return this.nVisibleHeight;
}

/**
 * Sets a callback function which is called when the map is moved
 * @public
 * @param (function) fCallback The function that is called
 */
function iwSetOnMove(fCallback)
{
	this.fOnMove = fCallback;
}

/**
 * Sets a callback function which is called when a map movement starts
 * @public
 * @param (function) fCallback The function that is called
 */
function iwSetOnMoveStart(fCallback)
{
	this.fOnMoveStart = fCallback;
}

/**
 * Sets a callback function which is called when a map movement is complete
 * @public
 * @param (function) fCallback The function that is called
 */
function iwSetOnMoveEnd(fCallback)
{
	this.fOnMoveEnd = fCallback;
}

/**
 * Sets a callback function which is called when a zoom of the map starts
 * @public
 * @param (function) fCallback The function that is called
 */
function iwSetOnZoomStart(fCallback)
{
	this.fOnZoomStart = fCallback;
}

/**
 * Sets a callback function which is called when a zoom of the map is complete
 * @public
 * @param (function) fCallback The function that is called
 */
function iwSetOnZoomEnd(fCallback)
{
	this.fOnZoomEnd = fCallback;
}

/**
 * Sets the type of the map; supported are map, air and hybrid
 * @public
 * @param (int) nMapMode One of the public member constants MAPMODE_MAP, MAPMODE_AIR and MAPMODE_HYBRID
 */
function iwSetMapMode(nMapMode)
{
	if (nMapMode != this.nMapMode)
	{
		if (nMapMode == this.MAPMODE_MAP)
			ChangeCopyright(true);
		else
			ChangeCopyright(false);
		
		bReloadTiles = false;
		
		// no reload needed when switching from air to hybrid or vice versa
		if (this.nMapMode == this.MAPMODE_MAP || nMapMode == this.MAPMODE_MAP)
			bReloadTiles = true;
		
		this.nMapMode = nMapMode;
		
		if (bReloadTiles)
		{
			this.Hide();
			this.PlaceTiles();
			this.LoadWholeMap();
		}
		else if (nMapMode == this.MAPMODE_AIR)
			iwShowLabels(false);
		else
			iwShowLabels(true);
	}
}

/**
 * Sets the URL to the Java Server Pages
 *
 * If the Java Server Pages (GetTileMap.jsp and iwmapserverproxy_getlabels.jsp) are
 * located in a different place and have to be addressed absolutely you have to
 * set the URL with this function (don't forget a '/' as last character)
 * @public
 * @param (String) sMapServerURL URL to the JSPs
 */
function iwSetMapServerURL(sMapServerURL)
{
	this.sMapServerURL = sMapServerURL;
}

/**
 * Sets the control mode for the left mouse button
 * @public
 * @param (int) nControlMode CONTROL_NONE, CONTROL_DRAG or CONTROL_ZOOM
 */
function iwSetLeftMouseButton(nControlMode)
{
	this.nControlLeft = nControlMode;
}

/**
 * Sets the control mode for the right mouse button
 * @public
 * @param (int) nControlMode CONTROL_NONE, CONTROL_DRAG or CONTROL_ZOOM
 */
function iwSetRightMouseButton(nControlMode)
{
	this.nControlRight = nControlMode;
}

/**
 * Adds a Copyright note to the map.
 * Use positive values for a position relative to the upper left corner and 
 * negative values for a position relative to the lower right corner
 * @public
 * @param (int) nPositionX Horizontal position
 * @param (int) nPositionY Vertical position
 * @param (int) nWidth Width of the copyright
 * @param (int) nHeight Height of the copyright
 * @param (int) nFontSize Font size in pixels
 * @param (String) sBorderStyle CSS definition for the border style
 * @param (String) sText Displayed text
 */
function iwAddCopyright(nPositionX, nPositionY, nWidth, nHeight, nFontSize, sBorderStyle, sText)
{
	// use some default values if no parameters are present
	if (!nPositionX)
		nPositionX = -192;
	if (!nPositionY)
		nPositionY = -26;
	if (!nWidth)
		nWidth = 180;
	if (!nHeight)
		nHeight = 16;
	if (!nFontSize)
		nFontSize = 12;
	if (!sBorderStyle)
		sBorderStyle = "1px outset #DDDDDD";
	if (!sText)
	{
		if (this.nMapMode == this.MAPMODE_MAP)
			sText = sCopyrightMap;
		else
			sText = sCopyrightAir;
	}
	
	var divCopyright = document.createElement('div');
	
	divCopyright.id											= "divCopyright";
	divCopyright.style.position					= "absolute";
	divCopyright.style.width						= nWidth + "px";
	divCopyright.style.height						= nHeight + "px";
	divCopyright.style.left							= this.GetLeft(nPositionX) + "px";
	divCopyright.style.top							= this.GetTop(nPositionY) + "px";
	divCopyright.style.backgroundColor	= "#ffffff";
	divCopyright.style.opacity					= "0.7";
	divCopyright.style.mozOpacity				= "0.7";
	divCopyright.style.filter						= "alpha(opacity=70)";
	divCopyright.style.border						= sBorderStyle;
	divCopyright.style.zIndex						= "4";
	divCopyright.style.textAlign				= "center";
	divCopyright.style.verticalAlign		= "middle";
	divCopyright.style.fontSize					= nFontSize + "px";
	
	divCopyright.innerHTML = sText;
	
	document.getElementById("divClipping").insertBefore(divCopyright, null);
	
	this.AddElement("divCopyright", nPositionX, nPositionY);
}

/**
 * Adds a small overview map to the main map.
 *
 * Use positive values for a position relative to the upper left corner and 
 * negative values for a position relative to the lower right corner
 * @public
 * @param (int) nOverviewFactor Enlargement factor between both maps
 * @param (int) nSize Pixel size of the overview map
 * @param (int) nPositionX Horizontal position
 * @param (int) nPositionY Vertical position
 * @param (String) sBorderStyle A CSS style definition for the border of the overview map
 */
function iwAddOverview(nOverviewFactor, nSize, nPositionX, nPositionY, sBorderStyle)
{
	this.bHasOverview			= true;
	this.nOverviewSize		= nSize;
	this.nOverviewFactor	= nOverviewFactor;
	
	this.AddElement("frmOverview", nPositionX, nPositionY);
	
	// create a new iframe for the overview map
	var iframe = document.createElement('iframe');
	
	iframe.id						= "frmOverview";
	iframe.name					= "frmOverview";
	iframe.scrolling		= "no";
	iframe.width				= nSize + "px";
	iframe.height				= nSize + "px";
	iframe.frameborder	= "0";
	
	iframe.style.position					= "absolute";
	iframe.style.left							= this.GetLeft(nPositionX) + "px";
	iframe.style.top							= this.GetTop(nPositionY) + "px";
	iframe.style.zIndex						= "4";
	iframe.style.border						= sBorderStyle;
	iframe.style.backgroundColor	= "#ffffff";
	
	var	pThis = this;
	var nNewTilesizeMeter = 	this.nTilesizeMeter 
													* this.nOverviewFactor 
													* (this.nVisibleWidth / this.nOverviewSize);
	
	var fOnload = function()	{
															frames["frmOverview"].SetSessionId(sSessionId);
															frames["frmOverview"].SetSize(nSize);
															frames["frmOverview"].SetMainMapSize(pThis.nVisibleWidth, pThis.nVisibleHeight);
															frames["frmOverview"].SetTilesizeMeter(nNewTilesizeMeter);
															frames["frmOverview"].SetPosition(pThis.nCenterMeterX, pThis.nCenterMeterY);
															frames["frmOverview"].SetExtraUrlParam(szExtraUrlParm);
															frames["frmOverview"].SetOverviewFactor(pThis.nOverviewFactor);
															frames["frmOverview"].Initialize();
														};
	
		document.getElementById("divClipping").insertBefore(iframe, null);
		
		if (iframe.attachEvent)
	    iframe.attachEvent("onload", fOnload);
		else
    	iframe.onload = fOnload;
		
		iframe.src			= "overview.html";
}

/**
 * Positions an existing HTML element relative to the main map.
 *
 * Use positive values for a position relative to the upper left corner and 
 * negative values for a position relative to the lower right corner
 * @public
 * @param (String) sElementId Id of the HTML element
 * @param (int) nPositionX Horizontal position
 * @param (int) nPositionY Vertical position
 */
function iwAddElement(sElementId, nPositionX, nPositionY)
{
	this.aMapElements.push([sElementId, nPositionX, nPositionY]);
}

/**
 * Adds a tool box to the main map.
 *
 * Use positive values for a position relative to the upper left corner and 
 * negative values for a position relative to the lower right corner
 * @public
 * @param (int) nPositionX Horizontal position
 * @param (int) nPositionY Vertical position
 */
function iwAddToolbox(nPositionX, nPositionY)
{
	var nNumTools = 2;
	var nWidth = nNumTools * 24 + 2 + 20;
	
	nPositionX -= nWidth;
	
	// create the toolbox
	var divToolbox = document.createElement('div');
	divToolbox.id = 'divToolbox';
	divToolbox.style.position					= 'absolute';
	divToolbox.style.left							= this.GetLeft(nPositionX) + 'px';
	divToolbox.style.top							= this.GetTop(nPositionY) + 'px';
	divToolbox.style.width						= nWidth + 'px';
	divToolbox.style.height						= '26px';
	divToolbox.style.backgroundColor	= '#ffffff';
	divToolbox.style.opacity					= '0.7';
	divToolbox.style.mozOpacity				= '0.7';
	divToolbox.style.filter						= 'alpha(opacity=70)';
	divToolbox.style.border						= '1px outset #DDDDDD';
	divToolbox.style.zIndex						= '4';
	divToolbox.style.textAlign				= 'center';
	document.getElementById('divClipping').insertBefore(divToolbox, null);
	
	this.AddElement("divToolbox", nPositionX, nPositionY);
	
	// add the tools
	this.AddToolMeasure();
	this.AddToolPrint();
}

/**
 * Adds a measuring tool to the tool box.
 * @private
 */
function iwAddToolMeasure()
{
	// Initialize the measurement tool
	createMeasureDivs();

	// add a button to the tool bar
	var imgButton						= document.createElement('img');
	imgButton.id						= "OnOffButton";
	imgButton.name					= "OnOffButton";
	imgButton.src						= "./img/tool_measure_0.gif";
	imgButton.border				= "0";
	imgButton.alt						= "Messwerkzeug";
	imgButton.style.margin	= '2px';
	
	if (imgButton.attachEvent)
	   imgButton.attachEvent("onclick", togleStatus);
	else
    imgButton.onclick = togleStatus;
	var divToolbox = document.getElementById('divToolbox');
	divToolbox.insertBefore(imgButton, null);
}

/**
 * Adds a print tool to the tool box.
 * @private
 */
function iwAddToolPrint()
{
	// add a button to the tool bar
	var imgButton						= document.createElement('img');
	imgButton.id						= "PrintButton";
	imgButton.name					= "PrintButton";	
	imgButton.src						= "./img/tool_print.gif";
	imgButton.border				= "0";
	imgButton.alt						= "Drucken";
	imgButton.style.margin	= '2px';
	
	var oThis = this;
  imgButton.onclick = function() {oThis.Print(); };
	var divToolbox = document.getElementById('divToolbox');
	divToolbox.insertBefore(imgButton, null);
}

function iwPrint(szApplication)
{
	szApplicationName = szApplication;
	var nPaperWidthA4 = 610;
	var nPaperHeightA4 = 430;
	/*if (navigator.appName == "Microsoft Internet Explorer")
	{
		nPaperWidthA4 = 610;
	}
	else
	{
		nPaperWidthA4 = 1000;
	}
	
	var nPrintableWidth = this.nVisibleWidth;
	var nPrintableHeight = this.nVisibleHeight;
	
	if (nPrintableWidth > nPaperWidthA4)
	{
		nPrintableWidth = nPaperWidthA4;
		nPrintableHeight = this.nVisibleHeight / this.nVisibleWidth * nPrintableWidth;
	}
	
	if (nPrintableWidth / nPrintableHeight > nPaperWidthA4 / nPaperHeightA4 )
	{
		nPrintableWidth = nPaperWidthA4;
		nPrintableHeight = this.nVisibleHeight * nPaperHeightA4 / this.nVisibleWidth;
	}
	else
	{
		nPrintableWidth = this.nVisibleWidth * nPaperHeightA4 / this.nVisibleHeight;
		nPrintableHeight = nPaperHeightA4;	
	}*/
	
	var nPrintableWidth = nPaperWidthA4;
	var nPrintableHeight = nPaperHeightA4;
	
			
	nPrintShowWidth = nPrintableWidth + 28; //Platz Scrollbar
	
	var sStyle = "left=100,top=50,width=" + nPrintShowWidth + ",height=550,resizable=yes,status=yes,scrollbars=yes";
  var PrintWindow = window.open("PrintMap.htm", "iwPrintWindow", sStyle);
  
  var oThis = this;
  window.setTimeout(function() {oThis.PrintInit(PrintWindow, nPrintableWidth, nPrintableHeight);}, 500);

  return false;
}

function iwPrintInit(PrintWindow, PrintableWidth, PrintableHeight)
{
	var nPrintableWidth = PrintableWidth;
	var nPrintableHeight = PrintableHeight;

	var PointList = new Array(); 
 	
	/*for (var nIndex = 0; nIndex < nShowAddress; nIndex++) {
		var divAddress = document.getElementById('Address' + nIndex);
		if (divAddress)
			PointList.push(divAddress.cloneNode(true));
	}
	
	for (var nIndex = 0; nIndex < nPOILayerMax; nIndex++) {
		var divPOI = document.getElementById('divPOI' + nIndex);
		if (divPOI)
			PointList.push(divPOI.cloneNode(true));
	}*/
	
	var divScalar = document.getElementById('divScalar');
	if (divScalar)
		PointList.push(divScalar.cloneNode(true));


  var divMapArea			= PrintWindow.document.getElementById('divMapArea');
  var divContentArea	= PrintWindow.document.getElementById('divContentArea');
  var divCopyrightArea = PrintWindow.document.getElementById('divCopyrightArea');
  
  if (divMapArea && divContentArea)
  {
  	var sType = "";
  	
  	if (oTileset.nMapMode == oTileset.MAPMODE_MAP)
  	{
  		sType = 'map'; 
  	}
  	else
  	{
  		sType = 'air';   	
  	}

	iwCountClick(this.GetMeterWidth(), this.GetMeterHeight(), sType);
  	divMapArea.style.width = nPrintableWidth;
  	divMapArea.style.height = nPrintableHeight;
  	
  	var nPrintMeterWidth = this.GetMeterWidth();
  	var nPrintMeterHeight = this.GetMeterHeight();
  	
	if (this.GetMeterWidth() / this.GetMeterHeight() > nPrintableWidth / nPrintableHeight )
	{
		nPrintMeterWidth = this.GetMeterWidth();
		nPrintMeterHeight = this.GetMeterWidth() * nPrintableHeight / nPrintableWidth;
	}
	else
	{
		nPrintMeterWidth = this.GetMeterHeight() * nPrintableWidth / nPrintableHeight;
		nPrintMeterHeight = this.GetMeterHeight();	
	}
  	
	var frmPrintPOIs = document.createElement('iframe');
	frmPrintPOIs.id 					= "frmPrintPOIs";
	frmPrintPOIs.scrolling		= "no";
	frmPrintPOIs.width				= "0px";
	frmPrintPOIs.height				= "0px";
	frmPrintPOIs.frameborder	= "0";
	frmPrintPOIs.style.visibility	= "hidden";
	document.getElementById('divClipping').insertBefore(frmPrintPOIs, null);

  	var sPOIURL =	'GetPOIIdentifyPrint.jsp;jsessionid=' + sSessionId + '?jsessionid='+ sSessionId+ '&cmd=GetIdentifyInfo'
  							+	'&layermaske=maske_gs'
  							+ '&mapcx=' + this.nCenterMeterX
  							+ '&mapcy=' + this.nCenterMeterY
  							+ '&mapdx=' + nPrintMeterWidth
  							+ '&mapdy=' + nPrintMeterHeight
  							+ '&width=' + nPrintableWidth
  							+ '&height=' + nPrintableHeight
  							+ '&projc=lcc_europe'
  							+ '&layerROUTE=' + szRoutePrefix
  							+ '&maptype=' + sType
  							+ '&App=' + szApplicationName
  							+ '&mapw=' + this.GetMeterWidth()
  							+ '&maph=' + this.GetMeterHeight()
  							+ '&vieww=' + this.nVisibleWidth
  							+ '&viewh=' + this.nVisibleHeight
							+ szExtraUrlParm;

	frmPrintPOIs.src = sPOIURL;
  	
  	//var sURL =		'iwmapserverproxy_printmap.jsp?cmd=GetMap'
  	var sURL =		'printPost.jsp;jsessionid=' + sSessionId + '?jsessionid='+ sSessionId+ '&cmd=GetMap'
  							+	'&layermaske=maske_gs'
  							+ '&mapcx=' + this.nCenterMeterX
  							+ '&mapcy=' + this.nCenterMeterY
  							+ '&mapdx=' + nPrintMeterWidth
  							+ '&mapdy=' + nPrintMeterHeight
  							+ '&width=' + nPrintableWidth
  							+ '&height=' + nPrintableHeight
  							+ '&projc=lcc_europe'
  							+ '&layerROUTE=' + szRoutePrefix
  							+ '&maptype=' + sType
  							+ '&App=' + szApplicationName
  							+ '&mapw=' + this.GetMeterWidth()
  							+ '&maph=' + this.GetMeterHeight()
  							+ '&vieww=' + this.nVisibleWidth
  							+ '&viewh=' + this.nVisibleHeight
							+ szExtraUrlParm;
  	
  	divMapArea.innerHTML += '<img width=' +nPrintableWidth+ ' height=' +nPrintableHeight+ ' onload="window.print();" src="' + sURL + '" + style="border:2px solid blue;"/>'; 	

		if (navigator.appName == "Microsoft Internet Explorer")
		{
			var sDiv = '';
	  	for (var nIndex = 0; nIndex<PointList.length; nIndex++)
	  	{
	  		var oDivPoint = PointList[nIndex];
	  		var sDivPointID = oDivPoint.id;
	  		
	  		if (sDivPointID == "divScalar")
	  		{
		  		sDiv +=	 			'<div id=' + sDivPointID 
		  								+ ' style="' 
		  								+ ' overflow: ' + oDivPoint.style.overflow +';' 
		  								+ ' font-size: ' + oDivPoint.style.fontSize +';'
		  								+ ' position: ' + oDivPoint.style.position +';'
		  								+ ' left: ' + parseInt(oDivPoint.style.left) +';'
		  								+ ' top: ' + (nPrintableHeight - 34) +';'
		  								+ ' z-index: ' + oDivPoint.style.zIndex +';'
		  								+ ' visibility: ' + oDivPoint.style.visibility +';'
		  								+ '"'
		  								+ '>' 
		  								+ oDivPoint.innerHTML 
		  								+ '</div>';
	  		}
	  		else
	  		{
		  		sDiv +=	 			'<div id=' + sDivPointID 
		  								+ ' style="' 
		  								+ ' overflow: ' + oDivPoint.style.overflow +';' 
		  								+ ' font-size: ' + oDivPoint.style.fontSize +';'
		  								+ ' position: ' + oDivPoint.style.position +';'
		  								+ ' left: ' + parseInt(oDivPoint.style.left) * nPrintableWidth/this.nVisibleWidth +';'
		  								+ ' top: ' + parseInt(oDivPoint.style.top) * nPrintableWidth/this.nVisibleWidth +';'
		  								+ ' z-index: ' + oDivPoint.style.zIndex +';'
		  								+ ' visibility: ' + oDivPoint.style.visibility +';'
		  								+ '"'
		  								+ '>' 
		  								+ oDivPoint.innerHTML 
		  								+ '</div>';	  		
	  		}
	  	}
	  	divMapArea.innerHTML += sDiv;
	  }
	  else
	  {
	  	for (var nIndex = 0; nIndex<PointList.length; nIndex++)
	  	{
	  		var oDivPoint = PointList[nIndex];
	  		oDivPoint.style.left = parseInt(oDivPoint.style.left) * nPrintableWidth/this.nVisibleWidth;
	  		//oDivPoint.style.top = parseInt(oDivPoint.style.top) * nPrintableWidth/this.nVisibleWidth;
	  		oDivPoint.style.top = 397;
			divMapArea.insertBefore(oDivPoint, null);
		}		  
	  }

  	
	  var divSpecialContent = document.getElementById('divSpecialContent');
	  if (divSpecialContent)
	  {
	  	divContentArea.innerHTML = divSpecialContent.innerHTML;
	  }
	  
	  
	  if (divCopyrightArea)
	  {
	  	var divCopyRight = document.getElementById("CopyRight");
	  	divCopyrightArea.style.position = "absolute";
	  	divCopyrightArea.style.top = parseInt(divMapArea.style.height) + 6;
	  	divCopyrightArea.style.left = 0;
	  	divCopyrightArea.style.width = parseInt(divMapArea.style.width);
	  	divCopyrightArea.style.textAlign = "right";
	  	
		if (oTileset.nMapMode == oTileset.MAPMODE_MAP)	  	
	  		divCopyrightArea.innerHTML = divCopyRight.innerHTML;
	  	else
	  		divCopyrightArea.innerHTML = '©&nbsp;&nbsp; <img src="images/pfeil_grau.gif" border=0 height=9 width=8>2006 <a href="http://www.mapandroute.com" target="New">Map and Route</a>, infoware, Tele Atlas, GeoPunkt, GeoContent';
	  }
	  
  }
  else
  {
  	var oThis = this;
  	window.setTimeout(function() {oThis.PrintInit(PrintWindow, nPrintableWidth, nPrintableHeight);}, 500);
  }
}
