/** 
*	Constructor de l'objecte NMap
*	@param {String} mapDiv Referencia al div on ha d'anar el mapa
*	@param {String} mapServerStaticUrl Url del servidor de mapes
*	@param {Number} numberOfLayers Numero de capes que ha de tenir el mapa. Equivaldrà al número de imatges a demanar
*	@param {Boolean} scalebarOn Activa l'escalimetre. Per defecte fals.
*	@param {Boolean} loadingBarOn Activa la animació de loading. En el de situació ens pot interessar desactivar-ho. Per defecte true.
*	@param {String} instanceName Nom de la variable instanciada d'aquest NMap. Molt important passar-la. Per mantenir compatibilitat si no esta definida per defecte suposa un "map".
*	@param {Boolean} ngDrawOn Activa la utilització de les eines de dibuix. Per defecte és fals.
*/
function NMap(mapDiv,mapServerStaticUrl,numberOfLayers,scalebarOn,loadingBarOn,instanceName,ngDrawOn) {
	this.IDENTIFY_TOC=0;
	this.IDENTIFY_POPUP=1;
	this.IDENTIFY_DOJO=2;
	if (arguments.length > 0) this.init(mapDiv,mapServerStaticUrl,numberOfLayers,scalebarOn,loadingBarOn,instanceName,ngDrawOn);
}

/** 
*	constructor
*	@private
*/
NMap.prototype.init = function(mapDiv,mapServerStaticUrl,numberOfLayers,scalebarOn,loadingBarOn,instanceName,ngDrawOn) {
	try {
		if (typeof(numberOfLayers)=='undefined') numberOfLayers=1;
		if (typeof(loadingBarOn)=='undefined') loadingBarOn=true;
		if (typeof(ngDrawOn)=='undefined') ngDrawOn=false;

		this.ngdrawOn=ngDrawOn;

		// variable initialization
		this.width = parseInt((mapDiv.style.width).replace("px",""));
		this.height = parseInt((mapDiv.style.height).replace("px",""));
		this.extent = new NExtent(new NPoint(0,0),new NPoint(0,0));
		//this.scale = new NScaleLevel(6);

		this.widthMeters = this.width / (96 * 39.3701);
		this.heightMeters = this.height / (96 * 39.3701);
		
		// per mantenir compatibilitat, si no passen nom de l'instancia suposem que és "map".
		if (typeof(instanceName)=="undefined") instanceName="map";
		this.mapInstanceName=instanceName;

		this.mapDiv = mapDiv;
		this.mapDiv.style.overflow = "hidden";
		this.loadingOn = loadingBarOn;
		
		var theHtml=new Array();
		theHtml.push( "<div id='mapTop"+this.mapDiv.id+"' class='draggable' name='mapTop' style='z-index:10; display:inline; white-space:nowrap; position:relative; top:0px; left:0px;'>");
		
		// create one div for each layer
		var zIndex=21;
		for (var i=0; i<numberOfLayers; i++) {
			theHtml.push(		"<div id='map"+this.mapDiv.id+i+"' style='position:absolute; z-index:"+zIndex+"; top:0px; left:0px; display:inline; white-space:nowrap; visibility:hidden;'><img id='mapImg"+this.mapDiv.id+i+"' src='' alt='' /></div>");
			zIndex++;
		}
		
		theHtml.push( "		<div id='SPOT"+this.mapDiv.id+"' style='position:absolute; top:0px; left:0px; z-index:255;  visibility:hidden;'></div>");
		theHtml.push( "		<div id='graphics"+this.mapDiv.id+"' style='position:absolute; top:0px; left:0px; z-index:255;'></div>");
		zIndex++;
		theHtml.push( "		<div id='drawBbox"+this.mapDiv.id+"' class='drawBoxStyle' style='z-index:"+zIndex+";'></div>");// per dibuixar-hi un bbox
		theHtml.push( "		<div id='printBbox"+this.mapDiv.id+"' class='printBoxStyle' style='z-index:"+zIndex+"; text-align:center; vertical-align:middle;'></div>");// per dibuixar-hi un bbox
		zIndex++;
		theHtml.push( "		<div id='poisLayer"+this.mapDiv.id+"' style='position:absolute; z-index:"+zIndex+"; top:0px; left:0px;'></div>");// per els pois a dibuixar dinàmicament
		
		
		// *************************************************
		// NGDRAW
		if (this.ngdrawOn) {
			zIndex++;
			theHtml.push("<div id='myCanvas"+this.mapDiv.id+"' style=' width:"+mapDiv.style.width+"; height:"+mapDiv.style.height+"; position:absolute; z-index:196;  left:0px; top:0px; border:1px solid darkgray;filter: alpha(opacity=90);' onmousemove='mouseMove(dibuix, event)' onmousedown='mouseDown(dibuix, event)'><IMG SRC='img/transparentpixel.gif' WIDTH='100%' HEIGHT='100%' BORDER='0' ALT=''><IMG id='img_0' SRC='img/NodeNormal.gif' WIDTH='"+NormalWidthIco+"' HEIGHT='"+NormalHeightIco+"' BORDER='0' ALT='img_0' style='position:absolute; top:0px; left:0px;'><IMG id='imgA' SRC='img/NodeActiu.gif' WIDTH='"+SelecWidthIco+"' HEIGHT='"+SelecHeightIco+"' BORDER='0' ALT='imgA' style='position:absolute;top:0px;left:0px; z-index=1000;'></div>");
			zIndex++;
			theHtml.push("<div id='myTmp"+this.mapDiv.id+"' style=' width:"+mapDiv.style.width+"; height:"+mapDiv.style.height+"; position:absolute; z-index:197;  left:0px; top:0px; border:1px solid darkgray;filter: alpha(opacity=90);' onmousemove='mouseMove(dibuix, event)' onmousedown='mouseDown(dibuix, event)'><IMG SRC='img/transparentpixel.gif'  WIDTH='100%' HEIGHT='100%' BORDER='0' ALT=''></div>");
			zIndex++;
			theHtml.push("<div id='myPoints"+this.mapDiv.id+"' style='position:absolute; z-index:198;  left:0px; top:0px;'></div>");
			zIndex++;
			theHtml.push("<div id='myText"+this.mapDiv.id+"' style='position:absolute; z-index:199;  left:0px; top:0px;'></div>");
		}
				
		// NGDRAW
		// *************************************************		
		
		theHtml.push( "</div>");

		// create one div for each layer
		//zIndex=11;
		zIndex++;
		for (var i=0; i<numberOfLayers; i++) {
			theHtml.push(		"<div id='mapStatic"+this.mapDiv.id+i+"' style='position:absolute; z-index:"+zIndex+"; top:0px; left:0px; display:inline; white-space:nowrap; visibility:hidden;'><img id='mapStaticImg"+this.mapDiv.id+i+"' src='' alt='' /></div>");
			zIndex++;
		}

		theHtml.push("<div id='zoomBox"+this.mapDiv.id+"' class='zoomBoxStyle'></div>");
		theHtml.push("<div id='overviewContainer"+this.mapDiv.id+"'></div>");
		theHtml.push("<div id='loading"+this.mapDiv.id+"' style='position:absolute; top:3px; left:3px; z-index:50; display:inline; visibility:hidden'><img src='img/loading.gif' border='0'/></div>");
		theHtml.push("<div id='scalebar"+this.mapDiv.id+"' style='position:absolute; top:3px; left:3px; z-index:49; display:inline;'></div>");

		// ***************************************************
		// PART AFEGIDA PER NGDRAW
		if (this.ngdrawOn) {
			theHtml.push("<div id='loadingCarrer' style='position:absolute; top:3px; left:3px; z-index:999; display:inline; visibility:hidden'><img src='img/loadingDRAW.gif' border='0'/></div>");
		}
		// ***************************************************	

		this.mapDiv.innerHTML = theHtml.join("");

		// ***************************************************
		// PART AFEGIDA PER NGDRAW
		if (this.ngdrawOn) {
			document.getElementById("loadingCarrer").style.top=Math.floor(this.height/2-15)+"px";
			document.getElementById("loadingCarrer").style.left=Math.floor(this.width/2-90)+"px";
		}
		// ***************************************************	

		// references to layers
		this.mapTop = document.getElementById("mapTop"+this.mapDiv.id);
		this.spotDiv = document.getElementById("SPOT"+this.mapDiv.id);
		this.graphicsDiv = document.getElementById("graphics"+this.mapDiv.id);
		this.overviewContainerDiv = document.getElementById("overviewContainer"+this.mapDiv.id); // quan es un mapa de situació conté la caixa amb la extensio actual

		this.mapLayer = new Array();
		for (var i=0; i<numberOfLayers; i++) {
			var layer=new NMapLayer();
			layer.targetDiv=document.getElementById("map"+this.mapDiv.id+i);
			layer.targetImg=document.getElementById("mapImg"+this.mapDiv.id+i);
			layer.staticDiv=document.getElementById("mapStatic"+this.mapDiv.id+i);
			layer.staticImg=document.getElementById("mapStaticImg"+this.mapDiv.id+i);
			this.mapLayer[i]=layer;
		}

		this.zoomBoxDiv = document.getElementById("zoomBox"+this.mapDiv.id);
		this.drawBboxDiv = document.getElementById("drawBbox"+this.mapDiv.id);
		this.printBboxDiv = document.getElementById("printBbox"+this.mapDiv.id);
		this.poisDiv = document.getElementById("poisLayer"+this.mapDiv.id);
		this.loadingDiv = document.getElementById("loading"+this.mapDiv.id);
		this.loadingDiv.style.top=Math.floor(this.height/2-15)+"px";
		this.loadingDiv.style.left=Math.floor(this.width/2-90)+"px";

		if (scalebarOn==true) {
			this.scalebar="";
			this.scalebarOn=true;
		}

		// prevent IE to stop dragging. Also needed to assign onmousedown="return false;" to the image the div contains.
		document.ondrag=function(){return false; };
		this.mapDiv.ondrag=function(){return false; };

		// assign events to the map
		AttachEvent(this.mapTop,'selectstart',PreventDefault,true);
		
		this.setCurrentTool("PAN");

		// calculate absolute position of div/image in the html
		this.mapPixelsX = findPosX(mapDiv);
		this.mapPixelsY = findPosY(mapDiv);

		// the static url path to map server.
		this.setMapServerStaticUrl(mapServerStaticUrl);
		
		// Layers list used with identify tools
		this.setIdentifyLayers("");
		this.setIdentifyResponseType(this.IDENTIFY_POPUP);

		// Number of map images pending to be loaded
		this.loadingCount=0;

		// Extensio del bbox dibuixat
		this.drawBboxExtent = null;
		this.printPolygon=null;
		this.printPreviewShow=false;
		
		// pois list
		this.pois = new NPois();

		// enable animations on zooms and pan.
		this.zoomAnimActive=false;
		this.panAnimActive=true;

		this.resetToolMeasure();

	}
	catch (e) {
		alert("Error a NMap.init()\n"+e.message);
	}
};


/**
*	Afegeix un mapa de situació.
*	@param {Integer} width Amplada en pixels
*	@param {Integer} height Alçada en pixels
*	@param {String} position La posició on ha d'anar. Valors possibles: TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT
*	@param {Integer} offsetX marge de distància en pixels respecte el costat esquerra o dret segons el paràmetre "position".
*	@param {Integer} offsetY marge de distància en pixels respecte el costat superior o inferior segons el paràmetre "position".
*	@param {String} borderStyle configuració del marge de l'escala. ex: "1px solid red".
*	@param {boolean} startOpen indicador de inicialment visible o ocult.
*	@param {String} serverUrl la url del servidor wms.
*	@param {String} serverFormat format de la imatge.
*	@param {String} serverLayers capes separades per comes a demanar.
*	return {Nmap} retorna la referència al mapa de situació creat.
*/
NMap.prototype.addOverviewMap = function(width, height, position, offsetX, offsetY, borderStyle, startOpen, serverUrl, serverFormat, serverLayers) {
	try {
		var str="";
		str+='<div id="overviewMapDiv2" style="position: absolute; z-index:111; border:3px solid #777777; background:#ffffff;">';
		str+='	<div id="overviewMapDiv" style="position: absolute; width:150px; height:100px; border:0px solid #777777; background:#ffffff;"></div>';
		str+='	<img id="overviewMapImgClose" src="img/overview_close.gif" alt="" style="position:relative; top:0px; left:0px; z-index:112; cursor:pointer;" onclick="javascript:'+this.mapInstanceName+'.hideOverviewMap()"/>';
		str+='</div>';
		str+='<img id="overviewMapImgOpen"  src="img/overview_open.gif" alt="" style="display:none; position:absolute; right:0px; bottom:0px; z-index:112; cursor:pointer;" onclick="javascript:'+this.mapInstanceName+'.showOverviewMap()"/>';
		this.overviewContainerDiv.innerHTML=str;

		if (!startOpen) this.hideOverviewMap();

		this.overviewMapDiv=document.getElementById("overviewMapDiv2");
		this.overviewMapDiv.style.width=width+"px";
		this.overviewMapDiv.style.height=height+"px";
		this.overviewMapDiv.style.border=borderStyle;

		if (position.indexOf("TOP")>-1) this.overviewMapDiv.style.top=offsetY+"px";
		else if (position.indexOf("BOTTOM")>-1) this.overviewMapDiv.style.bottom=offsetY+"px";
		else this.overviewMapDiv.style.bottom=offsetY+"px";

		if (position.indexOf("LEFT")>-1) this.overviewMapDiv.style.left=offsetX+"px";
		else if (position.indexOf("RIGHT")>-1) this.overviewMapDiv.style.right=offsetX+"px";
		else this.overviewMapDiv.style.right=offsetX+"px";


		this.overviewMap=new NMap(document.getElementById("overviewMapDiv"),"",1,false,false,this.mapInstanceName+".overviewMap");

		this.overviewMap.setConfig_epsg(this.getConfig_epsg());
		this.overviewMap.setConfig_transparent(this.getConfig_transparent());
		this.overviewMap.setConfig_userid(this.getConfig_userid());
		var maplayer=this.overviewMap.getLayerByPos(0);
		maplayer.type="wms";
		maplayer.serverUrl=serverUrl;
		maplayer.imageFormat=serverFormat;
		this.overviewMap.getLayerByPos(0).setLayers(serverLayers);
		this.overviewMap.setCurrentTool("NOEVENTS");

		this.overviewMapOn=true;
		
		return this.overviewMap;
	}
	catch (e) {
		alert("Error a NMap.addOverviewMap()\n"+e.message);
	}
};


/**
*	Oculta el mapa de situació.*	@private
*/
NMap.prototype.hideOverviewMap = function() {
	try {
		
		document.getElementById("overviewMapDiv2").style.display="none";
		document.getElementById("overviewMapImgOpen").style.display="block";

	}
	catch (e) {
		alert("Error a NMap.hideOverviewMap()\n"+e.message);
	}
}
/**
*	Mostra el mapa de situació.
*	@private
*/
NMap.prototype.showOverviewMap = function() {
	try {
		document.getElementById("overviewMapDiv2").style.display="block";
		document.getElementById("overviewMapImgOpen").style.display="none";
		document.getElementById("overviewMapDiv2").style.bottom="0px";
		document.getElementById("overviewMapDiv2").style.right="0px";
	}
	catch (e) {
		alert("Error a NMap.showOverviewMap()\n"+e.message);
	}
}

/**
*	NMap debugMapLayers
*	@private
*/
NMap.prototype.debugMapLayers = function() {
	var incW=this.width/this.mapLayer.length;
	var newW=this.width;
	for (var i=0; i<this.mapLayer.length; i++) {
		this.mapLayer[i].targetDiv.style.width=newW+"px";
		this.mapLayer[i].targetDiv.style.overflow="hidden";

		newW=newW-incW;
	}
};

/**
*	Inicialitza l'escalimetre
*	@private
*/
NMap.prototype.initScaleBar = function() {
	
	DebugOut("NMap.initScaleBar",DEBUG);
	this.scaleBarDiv= document.getElementById("scalebar"+this.mapDiv.id);
	this.scaleBarDiv.innerHTML="";
	this.scaleBarDiv.style.visibility="visible";
	this.scaleBarDiv.style.top=Math.floor(this.height-20)+"px";
	this.scaleBarDiv.style.left="0px";
	this.scaleBarDiv.style.width=this.width+ "px";
	this.scaleBarDiv.style.height="20px";
	this.scaleBarDiv.style.backgroundColor="#FFFFFF";
	var opacityValue = 0.8;
	this.scaleBarDiv.style.opacity = opacityValue;//firefox
	this.scaleBarDiv.style.filter="Alpha(opacity="+ (parseFloat(opacityValue) * 100) +");";//iexplorer

	// 1) construct the scalebar
	this.scalebar = new ScaleBar();
	// 2) set any non-default properties
	this.scalebar.minWidth = 90;
	this.scalebar.maxWidth = 120;
	this.scalebar.abbreviateLabel = true;
	this.scalebar.singleLine = true;
	// 3) add the scalebar to the document (optionally specify an id of any element)
	this.scalebar.place("scalebar"+this.mapDiv.id);
	return false;
};

/**
*	Actualitza l'escalimetre amb el valor de l'escala actual.
*	@private
*/
NMap.prototype.updateScaleBar = function() {
	DebugOut("NMap.updateScaleBar",DEBUG);
	// init only in first response
	if (this.scalebarOn && this.scalebar=="") this.initScaleBar();
	if (this.scalebarOn)this.scalebar.update(Math.round(this.extent.getScaleDenominator(this.width)));
	return false;
}


/**
*	Retorna el width de la imatge
*	@return {Integer}
*/
NMap.prototype.getImageWidth = function() {
	return this.width;
};

/**
*	Retorna el height de la imatge
*	@return {Integer}
*/
NMap.prototype.getImageHeight = function() {
	return this.height;
};

/**
*	Configura la URL estatica del servidor. 
*	Si és un WMS i no es posa explicitament la url del servidor wms utilitzarà aquesta.
*/
NMap.prototype.setMapServerStaticUrl = function(url) {
	this.serverStaticUrl=url;
}

/**
*	Retorna la url estatica del servidor
*	@return {String}
*/
NMap.prototype.getMapServerStaticUrl = function() {
	return this.serverStaticUrl;
}

/**
*	Configura la URL per fer la petició d'identify.
*/
NMap.prototype.setMapServerIdentifyUrl = function(url) {
	this.serverIdentifyUrl=url;
}

/**
*	Retorna la url del servidor al que fer la petició de identify.
*	@return {String}
*/
NMap.prototype.getMapServerIdentifyUrl = function() {
	if (typeof(this.serverIdentifyUrl)=="undefined") return this.getMapServerStaticUrl();
	else return this.serverIdentifyUrl;
}

/**
*	Configura la URL per fer la petició d'identify.
*/
NMap.prototype.setIdentifyResponseType = function(type) {
	this.identifyResponseType=type;
}

/**
*	Configura la URL estatica del servidor per generar PDFs. 
*	Utilitzat en crear els pdfs per imprimir.
*/
NMap.prototype.setMapServerPDFUrl = function(url) {
	this.serverPDFUrl=url;
}

/**
*	Retorna la URL utilitzada per generar els pdfs. Si no esta definida retorna la url estàtica de getMapServerStaticUrl().
*	@return {String}
*/
NMap.prototype.getMapServerPDFUrl = function() {
	if (typeof(this.serverPDFUrl)=="undefined") return this.getMapServerStaticUrl();
	else return this.serverPDFUrl;
}

/**
*	Especifica la llista de layers per la petició de identify
*	@param {String} str llista de layers separada per comes.
*/
NMap.prototype.setIdentifyLayers = function(str) {
	this.identifyLayers=str;
}

/**
*	Retorna la llista de layers als que s'ha de fer l'identify.
*	@return {String}
*/
NMap.prototype.getIdentifyLayers = function() {
	return this.identifyLayers;
}

/**
*	Retorna la posició X en pixels dins la plana
*	@return {Integer}
*/
NMap.prototype.getAbsolutePositionX = function() {
	return this.mapPixelsX;
}

/**
*	Retorna la posició X en pixels dins la plana
*	@return {Integer}
*/
NMap.prototype.getAbsolutePositionY = function() {
	return this.mapPixelsY;
}

/**
*	Recalcula els pixels absoluts de la situació del div.
*	@return {Integer}
*/
NMap.prototype.interfaceChanged = function() {
	try {
		DebugOut("Interficie redibuixada...",INFO);
		var center=this.getCenter()
		var scale=this.getScale();
		this.mapPixelsX = findPosX(this.mapDiv);
		this.mapPixelsY = findPosY(this.mapDiv);
		this.width = parseInt((this.mapDiv.style.width).replace("px",""));
		this.height = parseInt((this.mapDiv.style.height).replace("px",""));
		this.widthMeters = this.width / (96 * 39.3701);
		this.heightMeters = this.height / (96 * 39.3701);
		
		this.loadingDiv.style.top=Math.floor(this.height/2-15)+"px";
		this.loadingDiv.style.left=Math.floor(this.width/2-90)+"px";
		this.initScaleBar();

		this.setUseTimeStamp(true);
		this.centerAndZoom(center,scale);

	}
	catch (e) {
		alert("Error a NMap.interfaceChanged()\n"+e.message);
	}
}

/**
*	Canviar l'eina activa.
*	@param {String} toolId Valors possibles: ZOOMIN, ZOOMOUT, PAN, IDENTIFY, DRAWBBOX.
*/
NMap.prototype.setCurrentTool = function(toolId) {
	DebugOut("NMap.setCurrentTool: " + toolId , DEBUG);

	DetachEvent(this.mapTop,'mousedown',StartZoom,false);
	DetachEvent(this.mapTop,'mousedown',StartDrag,false);
	DetachEvent(this.mapTop,'mousedown',StartIdentify,false);
	DetachEvent(this.mapTop,'mousedown',StartDrawBBox,false);
	DetachEvent(this.mapTop,'mousedown',StartMouseClick,false);
	DetachEvent(this.mapTop,'mousedown',StartMeasureDistance,false);


	if ( (toolId=='ZOOMIN') || (toolId=='ZOOMOUT') ) {
		
		this.currentTool = toolId;
		AttachEvent(this.mapTop,'mousedown',StartZoom,false);
		this.mapTop.style.cursor="crosshair";
		
	} else if ( toolId=='PAN' ) {
		
		this.currentTool = toolId;
		AttachEvent(this.mapTop,'mousedown',StartDrag,false);
		this.mapTop.style.cursor="pointer";

	} else if (toolId=='IDENTIFY') {
		
		this.currentTool = toolId;
		AttachEvent(this.mapTop,'mousedown',StartIdentify,false);
		this.mapTop.style.cursor="help";

	} else if (toolId=='DRAWBBOX') {
		
		this.currentTool = toolId;
		AttachEvent(this.mapTop,'mousedown',StartDrawBBox,false);
		this.mapTop.style.cursor="crosshair";

	} else if (toolId=='MOUSECLICK') {
		
		this.currentTool = toolId;
		AttachEvent(this.mapTop,'mousedown',StartMouseClick,false);
		this.mapTop.style.cursor="crosshair";

	} else if (toolId=='MEASURE') {
		
		this.currentTool = toolId;
		AttachEvent(this.mapTop,'mousedown',StartMeasureDistance,false);
		this.mapTop.style.cursor="crosshair";
		
	} else if (toolId=='PRINTCENTER') {
		
		this.currentTool = toolId;
		this.mapTop.style.cursor="default";
		AttachEvent(this.printBboxDiv,'mousedown',StartPrintDrag,false);// ULL: Associem a un div diferent!

	} else if (toolId=='NOEVENTS') {

		this.currentTool = toolId;
		this.mapTop.style.cursor="pointer";

	} else if (toolId=='DIBUIX') {//x marti

	} else alert("NMap.setCurrentTool()\nTool not recognized: " + toolId);
}

/**
*	Retorna l'eina activa
*/
NMap.prototype.getCurrentTool = function() {
	return this.currentTool;
}

/**
*	NMap toString
*	@private
*/
NMap.prototype.toString = function() {
  return "NMap divId:" + this.mapDiv.id ;
};

/**
*	Retorna les coordenades del centre.
*	@return {NPoint}
*/
NMap.prototype.getCenter = function() {
  return this.extent.getCenter();
};

/** 
*	Retorna el denominador de l'escala actual del mapa
*	@return {Float}
*/
NMap.prototype.getScale = function() {
	return this.extent.getScaleDenominator(this.width);
};

/** 
*	Retorna l'extensió actual del mapa
*	@return {NExtent}
*/
NMap.prototype.getExtent = function() {
	return this.extent.clone();
};

/**
*	Fa un reset de la posició del mapa: despres de fer un pan queda desplaçat.
*	@private
*/
NMap.prototype.resetPosition = function() {
  this.mapTop.style.top = 0;
  this.mapTop.style.left = 0;
};

/**
*	Mostra la imatge de loading.
*	@private
*/
NMap.prototype.showLoading = function() {
	if (this.loadingOn == true) {
		this.loadingCount++;
		if (this.loadingCount>0) {
			this.loadingDiv.style.visibility = "visible";
		}
	}
};

/**
*	NMap resetLoading
*	@private
*/
NMap.prototype.resetLoading = function() {
	this.loadingCount=0;

};

/**
*	NMap hideLoading
*	@private
*/
NMap.prototype.hideLoading = function() {
	if (this.loadingOn) {
		this.loadingCount--;
		if (this.loadingCount<=0) {
			DebugOut("hideLoading",DEBUG);
			this.loadingDiv.style.visibility = "hidden";
		}
	}
};

/**
*	Cridat quan una imatge no te cap layer actiu i no s'ha de fer la petició.
*	@private
*/
NMap.prototype.clearMapImage = function(layerTarget) {
	//this.mapLayer[layerTarget].targetDiv.innerHTML="";
	this.mapLayer[layerTarget].targetDiv.style.visibility="hidden";
	this.mapLayer[layerTarget].queryUrl = "";
	DebugOut("("+this.mapInstanceName+") clearMapImage.eventAfterOneLayerLoaded()",INFO);
	this.eventAfterOneLayerLoaded();
};

/**
*	event called when map image has been loaded. 
*	@private
*/
NMap.prototype.eventAfterAllLayersLoadedCall = function() {
	try {
		this.setUseTimeStamp(false);
		this.showDrawBboxExtent();
		this.showPrintBboxExtent(this.printPolygon);
		this.updateScaleBar();
		this.eventAfterAllLayersLoaded();
	}
	catch (e) {
		alert("Error a eventAfterAllLayersLoadedCall\n" + e.message);
	}
}

/**
*	metode a redefinir per l'usuari.
*/
NMap.prototype.eventAfterAllLayersLoaded = function() {}

/**
*	event called when map image has been loaded. 
*	@private
*/
NMap.prototype.eventAfterOneLayerLoaded = function() {
	try {
			
		DebugOut("("+this.mapInstanceName+") eventAfterONELayerLoaded count:" + this.loadingCount,INFO);

		if(this.loadingCount==0) {

			this.showPois();
			this.eventAfterAllLayersLoadedCall();
		}
	
	}
	catch (e) {
		alert("Error a eventAfterOneLayerLoaded\n" + e);
	}
}

/**
*	Event executat abans de fer una petició de identify. A redefinir per l'usuari.
*	Permet tallar l'execució si es retorna un true. Llavors no fa la petició d'identify.
*	@param {Number} pixX coordena x del pixel
*	@param {Number} pixY coordena x del pixel
*	@return {boolean}
*/
NMap.prototype.eventBeforeIdentifyQuery = function(pixX,pixY) {return false;}


/**
*	Event called before map action is done, but just after the target extent is computed. To be redefined by the user.
*	@param {Float} currentScale The current map scale.
*	@param {Float} targetScale The target scale of the query.
*	@param {NExtent} targetExtent The target extent of the map
*/
NMap.prototype.eventBeforeMapAction = function(currentScale,targetScale,targetExtent) {}

/**
*	event called before map action is done.
*	
*	@private
*/
NMap.prototype.eventBeforeMapActionCall = function(targetExtent) {
	try {
		this.hideDrawBboxExtent();
		this.hidePrintBboxExtent();
		this.hidePois();
		this.resetToolMeasure();
		var targetScale;
		try {
			this.wfsTooltipVisibility();
		}
		catch (e) {}
		try {
			targetScale=targetExtent.getScaleDenominator(this.width);
		}
		catch (e) {
			targetScale=null;
		}
		var ret=this.eventBeforeMapAction(this.getScale(),targetScale,targetExtent);
		if (!ret) {
			this.centerPrintBboxExtent(targetExtent);
			if (this.overviewMapOn) {
				this.overviewMap.drawBboxExtent=targetExtent.clone();
				this.overviewMap.centerToExtent(targetExtent.clone(),targetExtent.getScaleDenominator(this.overviewMap.width)*1.5);
			}
		}
		return ret;
	
	}
	catch (e) {
		alert("Error a NMap.eventBeforeMapActionCall()\n"+e.message);
	}
}

/**
*	Called when tool MOUSECLICK is selected and map is clicked. Must be externally defined.
*	@param {Integer} pixX Pixel X
*	@param {Integer} pixY Pixel Y
*	@param {NPoint} coord La coordenada del click
*/
NMap.prototype.eventToolMouseClick = function(pixX,pixY,coord) {alert("eventToolMouseClick: "+pixX+","+pixY+"  coord:"+coord);}

/**
*	Called when tool MOUSECLICK is selected and map is clicked. Must be externally defined.
*	@param {Integer} pixX Pixel X
*	@param {Integer} pixY Pixel Y
*	@param {NPoint} coord La coordenada del click
*	@private
*/
NMap.prototype.eventToolMouseClickCall = function(pixX,pixY) {
	try {
		

		this.eventToolMouseClick(pixX,pixY,this.pixelsToCoords(pixX,pixY));
	
	}
	catch (e) {
		alert("Error a NMap.eventToolMouseClickCall()\n"+e.message);
	}
}


/**
*	Cridat en utilitzar l'eina de MEASURE i fer un event de click sobre el mapa.
*	Ha de ser redefinit per fer alguna acció.
*	@param {Float} distance la distancia en metres
*/
NMap.prototype.eventToolMeasure = function(distance) {}

/**
*	Called when tool MEASURE is selected and map is clicked. Must be externally defined.
*	@param {Integer} pixX Pixel X
*	@param {Integer} pixY Pixel Y
*	@private
*/
NMap.prototype.eventToolMeasureCall = function(pixX,pixY) {
	try {
		if (typeof(this.jg)=="undefined") {
			alert("Error: Per utilitzar l'eina de medir cal incloure les biblioteques a la plana!");
			return;
		}
		var coord=this.pixelsToCoords(pixX,pixY);

		//alert("eventToolMeasureCall: "+pixX+","+pixY+"  coord:"+coord);

		var l=this.measureXArray.length;
		this.measureXArray[l]=pixX;
		this.measureYArray[l]=pixY;
		this.measureCoordArray[l]=coord;
		this.jg.setFont("arial","14px",Font.ITALIC_BOLD);

		if (l>0) {
			this.measureValue+=Math.sqrt(Math.pow(this.measureCoordArray[l].getX()-this.measureCoordArray[l-1].getX(),2)+Math.pow(this.measureCoordArray[l].getY()-this.measureCoordArray[l-1].getY(),2));
			//this.jg.clear();
			this.jg.setColor("#ff0000"); // red
			//this.jg.drawPolyline(this.measureXArray,this.measureYArray);
			this.jg.drawLine(this.measureXArray[l-1],this.measureYArray[l-1],this.measureXArray[l],this.measureYArray[l]);
			
			this.jg.drawString(Math.round(this.measureValue)+" m.",this.measureXArray[l],this.measureYArray[l]);
			this.jg.paint();
		} else {
			this.jg.clear();
			this.jg.setColor("#ff0000"); // red
			this.jg.fillEllipse(this.measureXArray[0],this.measureYArray[0],5,5);
			this.jg.drawString("0 m.",this.measureXArray[0],this.measureYArray[0]);
			this.jg.paint();
		}
		this.eventToolMeasure(this.measureValue);
		//this.eventToolMouseClick(pixX,pixY,);
	
	}
	catch (e) {
		alert("Error a NMap.eventToolMeasureCall()\n"+e.message);
	}
}

/**
*	Elimina les linies de mesurar i inicialitza les variables.
*/
NMap.prototype.resetToolMeasure = function() {
	try {
		this.measureXArray=new Array();
		this.measureYArray=new Array();
		this.measureCoordArray=new Array();
		this.measureValue=0;
		try {
			this.jg.clear();
		}	catch (e) {	}
		this.jg = new jsGraphics(this.graphicsDiv);
		this.jg.clear();
	}
	catch (e) {
	}
}

/**
*	Fa un zoom al punt i escala donades. Utilitzat en fer un pan.
*	@param {NPoint} point La coordenada a centrar
*	@param {Float} scaleDen La escala a mostrar
*	@param {boolean} animLoad indica si es una peticio de pan per mantenir la imatge actual fins que es rebi la resposta. Fals per defecte.
*/
NMap.prototype.centerAndZoom = function(point,scaleDen,animLoad) {
	if (typeof(animLoad)=="undefined") animLoad=false;

	// convert point+scale to bbox . 1cm = 37.93811pixels
	var coordsWidth =  this.widthMeters * scaleDen;
	var coordX0 = parseFloat(point.cX) - coordsWidth/2;
	var coordX1 = parseFloat(point.cX) + coordsWidth/2;
	var coordsHeight =  this.heightMeters * scaleDen;
	var coordY0 = parseFloat(point.cY) - coordsHeight/2;
	var coordY1 = parseFloat(point.cY) + coordsHeight/2;
	var theExtent = new NExtent(new NPoint(coordX0,coordY0), new NPoint(coordX1,coordY1));
	DebugOut("centerAndZoom theExtent" + theExtent,DEBUG);
	if (true==this.eventBeforeMapActionCall(theExtent)) return false;

	this.resetLoading(); // if currently loading reset it
	for (var i=0;i<this.mapLayer.length ;i++ ) {
		var layers=this.getLayerNames(i);
		if (layers!="") {
			if (animLoad) this.beforeQueryPan(i);
			DebugOut("this.mapLayer[i].type: "+this.mapLayer[i].type,INFO);
			if (this.mapLayer[i].type=="wms") {
				var reqUrl = this.getQueryURL(layers,this.getLayerByPos(i).getImageFormat(),this.width,this.height,theExtent,this.getScale());
				this.sendWMSRequest(reqUrl,i);
			} else if (this.mapLayer[i].type=="wfs") {
				this.sendWFSRequest(i,layers,this.width,this.height,theExtent);
			}
		} else this.clearMapImage(i);
	}

	this.extent=theExtent.clone();
  
};


/**
*	Centre a l'extensió passada per parametre. 
*	Si també es passa el paràmetre minScale i l'escala de l'extensió és més petita, llavors es mostra a l'escala del parametre minScale.
*	@param {NExtent} extent la extensio a mostrar
*	@param {Number} minScale la escala mínima a mostrar. Paràmetre opcional.
*/
NMap.prototype.centerToExtent = function(extent, minScale) {
	if (typeof(minScale)=="undefined" || !isFinite(minScale)) minScale=-1;
	DebugOut("centerToExtent INIT: " + extent,DEBUG);
	
	extent.squarePixels(this.width,this.height);
	var newScale=extent.getScaleDenominator(this.width);
	DebugOut("centerToExtent QUADRAT: " + extent,DEBUG);

	if (minScale>0 && newScale<minScale) {
		this.centerAndZoom(extent.getCenter(),minScale,true);
		return;
	}

	if (true==this.eventBeforeMapActionCall(extent)) return false;
	
	this.resetLoading(); // if currently loading reset it
	for (var i=0;i<this.mapLayer.length ;i++ ) {
		var layers=this.getLayerNames(i);

		if (layers!="") {
			if (this.mapLayer[i].type=="wms") {
				var reqUrl = this.getQueryURL(layers,this.getLayerByPos(i).getImageFormat(),this.width,this.height,extent,newScale);
				this.sendWMSRequest(reqUrl,i);
			} else if (this.mapLayer[i].type=="wfs") {
				this.sendWFSRequest(i,layers,this.width,this.height,extent);
			}
		} else this.clearMapImage(i);
	}

	this.extent=extent.clone();

};

/**
*	Centra a les coordenades en pixels passats per parametre.
*	@param {Integer} pixelCenterX Pixel X
*	@param {Integer} pixelCenterY Pixel Y
*/
NMap.prototype.centerToPixels = function(pixelCenterX,pixelCenterY) {

	try {
		var scaleDen = this.getScale();

		var point=this.pixelsToCoords(pixelCenterX,pixelCenterY);

		DebugOut("centerToPixels new centre: " + point ,DEBUG);

		this.centerAndZoom(point,scaleDen,true);
	
	}
	catch (e) {
		alert("Error a NMap.prototype.centerToPixels()\n"+e.message);
	}
};

/**
*	Torna un objecte Npoint amb els píxels transformats a coordenades.
*	@return {NPoint}
*/
NMap.prototype.pixelsToCoords = function(pixelCenterX,pixelCenterY) {
	var scaleDen = this.getScale();
	var coordsWidth =  this.widthMeters * scaleDen;
	var coordsHeight =  this.heightMeters * scaleDen;
	var centerX = this.extent.getMinX() + (pixelCenterX / this.width) * coordsWidth;
	var centerY = this.extent.getMaxY() - (pixelCenterY / this.height) * coordsHeight;
	
	return   new NPoint(centerX,centerY);
};

/**
*	Genera la extensió corresponent a un punt més una escala. Per fer els calculs també necessita les mides de la imatge en pixels.
*	@param {NPoint} point la coordenada del centre.
*	@param {Number} scaleDen el denominador de l'escala
*	@param {Number} pixWidth amplada en pixels de la imatge
*	@param {Number} pixHeight alçada en pixels de la imatge
*	@return {NExtent}
*/
NMap.prototype.getExtentFromPointAndScale = function(point,scaleDen,pixWidth,pixHeight) {

// convert point+scale to bbox . 1cm = 37.93811pixels
	var widthMeters = pixWidth / (96 * 39.3701);
	var heightMeters = pixHeight / (96 * 39.3701);

	var coordsWidth =  widthMeters * scaleDen;
	var coordX0 = parseFloat(point.cX) - coordsWidth/2;
	var coordX1 = parseFloat(point.cX) + coordsWidth/2;

	var coordsHeight =  heightMeters * scaleDen;
	var coordY0 = parseFloat(point.cY) - coordsHeight/2;
	var coordY1 = parseFloat(point.cY) + coordsHeight/2;
	var theExtent = new NExtent(new NPoint(coordX0,coordY0), new NPoint(coordX1,coordY1));
	return theExtent;
}

/**
*	Torna un objecte Npoint amb coordenades a partir dels pixels i de la configuració del mapa passats per param. 
+	@param {Integer} pixX pixel x
+	@param {Integer} pixY pixel y
+	@param {Integer} pixWidth amplada en pixels de la imatge.
+	@param {Integer} pixHeight alçada en pixels de la imatge.
+	@param {NPoint} centerPoint coordenades el centre.
+	@param {Float} scaleDen denominador de l'escala
*	@return {NPoint}
*/
NMap.prototype.pixelsToCoordsWithParams = function(pixX,pixY,pixWidth,pixHeight,centerPoint,scaleDen) {
	try {
		
	var widthMeters = pixWidth / (96 * 39.3701);
	var heightMeters = pixHeight / (96 * 39.3701);
	var coordsWidth =  widthMeters * scaleDen;
	var coordsHeight =  heightMeters * scaleDen;
	var extent=this.getExtentFromPointAndScale(centerPoint,scaleDen,pixWidth,pixHeight);

	var centerX = extent.getMinX() + (pixX / pixWidth) * coordsWidth;
	var centerY = extent.getMaxY() - (pixY / pixHeight) * coordsHeight;

	return   new NPoint(centerX,centerY);
	}
	catch (e) {
		alert("Error a NMap.pixelsToCoordsWithParams()\n"+e.message);
	}
};

/**
*	Retorna un objecte Npoint Amb les coordenades transformades a pixels.
*	@return {NPoint}
*/
NMap.prototype.coordsToPixels = function(coordenadaX,coordenadaY) {
/*
	var centerX = (parseInt(coordenadaX) - this.extent.getMinX()) * this.width / (this.extent.getMaxX() - this.extent.getMinX());
	var centerY = (this.extent.getMaxY() - parseInt(coordenadaY)) * this.height / (this.extent.getMaxY() - this.extent.getMinY());

	return   new NPoint(Math.round(centerX),Math.round(centerY));
*/
	var scaleDen = this.getScale();
	var coordsWidth =  this.widthMeters * scaleDen;
	var coordsHeight =  this.heightMeters * scaleDen;

	var p = (parseFloat(coordenadaX) - this.extent.getMinX());
	var c = p / coordsWidth;
	var centerX = c * this.width;

	var p = (this.extent.getMaxY() - parseFloat(coordenadaY));
	var c = p / coordsHeight;
	var centerY = c * this.height;

  return new NPoint(Math.round(centerX),Math.round(centerY));

}

/**
*	Transforma un valor de metres a pixels de mapa
*	@param {Number} value valor en metres
*	@return {Number}
*/
NMap.prototype.radimetersToradipixels = function(value) {

	var p = (this.extent.getMaxX() - this.extent.getMinX());
	var s = (value * this.width);
	var r = s / p;

	return Math.round(r);

}

/**
*	Fa un zoom in mantinguent el centre. TODO
*	@private
*	@TODO
*	
*/
NMap.prototype.zoomIn = function() {
	alert("NMap.prototype.zoomIn : TODO" );

};

/**
*	Fa un zoom in a partir d'una caixa representada per dos coordenades en pixels.
*/
NMap.prototype.zoomInWithPixelsBox = function(pixBoxX0,pixBoxY0,pixBoxX1,pixBoxY1) {
	try {
			
		DebugOut("zoomInWithPixelsBox: (" + pixBoxX0 + "," + pixBoxY0 + "),(" + pixBoxX1 + "," + pixBoxY1 + ")",DEBUG);

		//----------------------------------------
		// multiplicador (passar pixels->coord)
		var multX = (this.extent.getMaxX() - this.extent.getMinX()) / this.width;    
		var multY = -(this.extent.getMaxY() - this.extent.getMinY()) / this.height;    

		if (pixBoxX0==pixBoxX1 && pixBoxY0==pixBoxY1){
			pixBoxX0=pixBoxX0-(this.width/4);
			pixBoxY0=pixBoxY0-(this.height/4);
			pixBoxX1=pixBoxX1+(this.width/4);
			pixBoxY1=pixBoxY1+(this.height/4);
		}

		// calculem les coordenades del bbox equivalent als pixels
		var bbx1 = Math.round(this.extent.getMinX() + (pixBoxX0 * multX));
		var bby1 = Math.round(this.extent.getMaxY() + (pixBoxY0 * multY));
		var bbx2 = Math.round(this.extent.getMinX() + (pixBoxX1 * multX));
		var bby2 = Math.round(this.extent.getMaxY() + (pixBoxY1 * multY));
		var newExtent = new NExtent(new NPoint(bbx1,bby1),new NPoint(bbx2,bby2));
		DebugOut("zoomInWithPixelsBox: " + newExtent,DEBUG);
		if (true==this.eventBeforeMapActionCall(this.getZoomInTargetExtent(this.width,this.height,newExtent,pixBoxX0,pixBoxY0,pixBoxX1,pixBoxY1))) return false;

		this.resetLoading(); // if currently loading reset it
		for (var i=0;i<this.mapLayer.length ;i++ ) {
			var layers=this.getLayerNames(i);
			if (layers!="") {
				this.beforeQueryZoomIn(i,pixBoxX0,pixBoxY0,pixBoxX1,pixBoxY1);
				
				if (this.mapLayer[i].type=="wms") {
					var reqUrl = this.getQueryURL4ZoomIn(layers,this.getLayerByPos(i).getImageFormat(),this.width,this.height,newExtent,pixBoxX0,pixBoxY0,pixBoxX1,pixBoxY1);
					this.sendWMSRequest(reqUrl,i);
				} else if (this.mapLayer[i].type=="wfs") {
					var newExtentQuad=this.getZoomInTargetExtent(this.width,this.height,newExtent,pixBoxX0,pixBoxY0,pixBoxX1,pixBoxY1);
					this.sendWFSRequest(i,layers,this.width,this.height,newExtentQuad);
				}
			} else this.clearMapImage(i);
		}

		this.extent=this.extentZoomTemp.clone();
		
	}
	catch (e) {
		alert("Error a NMap.zoomInWithPixelsBox()\n"+e.message);
	}
	
};


/**
*	Fa un zoom out a partir d'una caixa representada per dos coordenades en pixels.
*/
NMap.prototype.zoomOutWithPixelsBox = function(pixBoxX0,pixBoxY0,pixBoxX1,pixBoxY1) {
	DebugOut("zoomOutWithPixelsBox: (" + pixBoxX0 + "," + pixBoxY0 + "),(" + pixBoxX1 + "," + pixBoxY1 + ")",DEBUG);

	//----------------------------------------
	// multiplicador (passar pixels->coord)
	var multX = (this.extent.getMaxX() - this.extent.getMinX()) / this.width;
	var multY = -(this.extent.getMaxY() - this.extent.getMinY()) / this.height;

	if (pixBoxX0==pixBoxX1 && pixBoxY0==pixBoxY1){
		pixBoxX0=pixBoxX0-(this.width/4);
		pixBoxY0=pixBoxY0-(this.height/4);
		pixBoxX1=pixBoxX1+(this.width/4);
		pixBoxY1=pixBoxY1+(this.height/4);
	}
	// calculem les coordenades del bbox equivalent a la caixa dels pixels. No és la extensió final: només la extensio de la caixa dibuixada!
	var bbx1 = Math.round(this.extent.getMinX() + (pixBoxX0 * multX));
	var bby1 = Math.round(this.extent.getMaxY() + (pixBoxY0 * multY));
	var bbx2 = Math.round(this.extent.getMinX() + (pixBoxX1 * multX));
	var bby2 = Math.round(this.extent.getMaxY() + (pixBoxY1 * multY));
	var newExtent = new NExtent(new NPoint(bbx1,bby1),new NPoint(bbx2,bby2));
	//DebugOut("zoomInWithPixelsBox extent : " + newExtent,DEBUG);
	if (true==this.eventBeforeMapActionCall(this.getZoomOutTargetExtent(this.width,this.height,newExtent,pixBoxX0,pixBoxY0,pixBoxX1,pixBoxY1))) return false;
	
	this.resetLoading(); // if currently loading reset it
	for (var i=0;i<this.mapLayer.length ;i++ ) {
		var layers=this.getLayerNames(i);
		if (layers!="") {
			//this.beforeQueryZoomOut(i,pixBoxX0,pixBoxY0,pixBoxX1,pixBoxY1);// TODO
			if (this.mapLayer[i].type=="wms") {
				var reqUrl = this.getQueryURL4ZoomOut(layers,this.getLayerByPos(i).getImageFormat(),this.width,this.height,newExtent,pixBoxX0,pixBoxY0,pixBoxX1,pixBoxY1);
				this.sendWMSRequest(reqUrl,i);
			} else if (this.mapLayer[i].type=="wfs") {
				var newExtentQuad = this.getZoomOutTargetExtent(this.width,this.height,newExtent,pixBoxX0,pixBoxY0,pixBoxX1,pixBoxY1);
				this.sendWFSRequest(i,layers,this.width,this.height,newExtentQuad);
			}
		} else this.clearMapImage(i);
	}

	this.extent=this.extentZoomTemp.clone();
	return false;
};


/**
*	Descarrega un zip amb la imatge i la configuració del .map generat al servidor. Només funciona amb el motor de salut
*	@private
*/
NMap.prototype.downloadConfig = function(point,scaleDen) {
	
	// convert point+scale to bbox . 1cm = 37.93811pixels
	var coordsWidth =  this.widthMeters * scaleDen;
	var coordX0 = parseFloat(point.cX) - coordsWidth/2;
	var coordX1 = parseFloat(point.cX) + coordsWidth/2;
	var coordsHeight =  this.heightMeters * scaleDen;
	var coordY0 = parseFloat(point.cY) - coordsHeight/2;
	var coordY1 = parseFloat(point.cY) + coordsHeight/2;
	var theExtent = new NExtent(new NPoint(coordX0,coordY0), new NPoint(coordX1,coordY1));
	DebugOut("downloadConfig() theExtent: " + theExtent,DEBUG);
	//alert("" + coordsWidth + " ----" + point + "\n" + theExtent);

	// Send form encoded data stating that I want to add the specified item to the cart.
	for (var i=0;i<this.mapLayer.length ;i++ ) {
		var layers=this.getLayerNames(i);
		if (layers!="") {
			var reqUrl = this.getGetConfigURL(layers,this.getLayerByPos(i).getImageFormat(),this.width,this.height,theExtent,this.getScale());
			window.open(this.getMapServerStaticUrl() + reqUrl,'');
		} else this.clearMapImage(i);
	}
  
};



/**
*	Fa una crida a un servlet que fusionarà les diferents capes amb imatges i generarà un pdf.
*	tots els paràmetres que es rebin en forma de parelles dels arguments "nomParam", "valorParam" seran passats al servlet.
*/
NMap.prototype.createPDF = function() {
	try {
		if (this.printPolygon==null) {
			alert("Error a NMap.createPDF()\nFalta definir la extensió a imprimir.");
			return;
		}
		var formObj=document.getElementById("printForm");// TODO: parametritzar
		deleteFormFields(formObj);
		
		// Also need to add additional parameters. Available at arguments as pairs of paramName paramValue
		var len=arguments.length;
		for (var i=0;i<len ;i=i+2 ){
			DebugOut("createPDF, params: " + arguments[i] + ": " + arguments[i+1],DEBUG);

			if (arguments[i]=="legendUrl") var legendUrl=arguments[i+1];
			if (arguments[i]=="formatsize") var formatsize=arguments[i+1];
			if (arguments[i]=="formatorientation") var formatorientation=arguments[i+1];
			if (arguments[i]=="formatpages") var formatpages=arguments[i+1];
			if (arguments[i]=="formatWidth") var formatWidth=arguments[i+1];
			if (arguments[i]=="formatHeight") var formatHeight=arguments[i+1];

			addFieldToForm(formObj,"text",arguments[i],escape(arguments[i+1]));
		}

		// 1cm = 37.93811 pixels
		var printWidthMeters = this.printWidthPixels / (96 * 39.3701);
		var printHeightMeters = this.printHeightPixels / (96 * 39.3701);
		var queryExtent = this.printExtentWithoutRotation;
	
		var queryWidth=Math.round(queryExtent.getPixelsWidth(this.printScale));
		var queryHeight=Math.round(queryExtent.getPixelsHeight(this.printScale));
		var scaleDen=this.getScale();

		addFieldToForm(formObj,"text","WIDTH",this.printWidthPixels);//mides que ha de tenir la imatge generada
		addFieldToForm(formObj,"text","HEIGHT",this.printHeightPixels);
		addFieldToForm(formObj,"text","SCALE",this.printScale);
		addFieldToForm(formObj,"text","CENTER",this.printCenter.getX()+","+this.printCenter.getY());
		addFieldToForm(formObj,"text","BACKGROUNDCOLOR",this.printBackColorRGB);
		

		var count=0;
		// podem tenir una sola url d'impressió o utilitzar la configuració actual del mapa.
		if (this.printWMSBase!="" && this.printWMSLayers!="" && this.printWMSFormat!="") {
			var reqUrl = this.printWMSBase+"&"
							 +this.getQueryURL(this.printWMSLayers,this.printWMSFormat,queryWidth,queryHeight,queryExtent,scaleDen);
			addFieldToForm(formObj,"text","l"+count,reqUrl);
			addFieldToForm(formObj,"text","d"+count,"print desc");
			addFieldToForm(formObj,"text","t"+count,0);
			count++;
		} else {
			// creem els parametres de layer(l0,..) estils(s0,...) i transparencia(t0,...)
			for (var i=0;i<this.mapLayer.length ;i++ ) {
				var layer=this.getLayerByPos(i);
				var layers=this.getLayerNames(i);
				if (layers!="") {
					var serverUrl=layer.getServerUrl();
					if (layer.printUrl!="") serverUrl=layer.printUrl;
					
					if (this.mapLayer[i].type=="wms") {						
						var reqUrl = serverUrl + layer.getParameters()+"&"
										 +this.getQueryURL(layers,layer.getImageFormat(),queryWidth,queryHeight,queryExtent,scaleDen);
						addFieldToForm(formObj,"text","l"+count,reqUrl);
						addFieldToForm(formObj,"text","d"+count,layer.desc);
						if (layer.printDPI!="") addFieldToForm(formObj,"text","dpi"+count,layer.printDPI);
					} else if (this.mapLayer[i].type=="wfs") {
						var MAX_FEATURES=10000;
						var reqUrl = serverUrl + "SERVICE=WFS&VERSION=1.0.0&REQUEST=getfeature&HEIGHT="+queryHeight+"&WIDTH="+queryWidth+"&TYPENAME=" + layers + "&maxfeatures=" 
										+MAX_FEATURES + "&OUTPUTFORMAT=gml2&BBOX="+queryExtent.getMinX() + "," + queryExtent.getMinY() + "," + queryExtent.getMaxX() + "," + queryExtent.getMaxY();
						
						var stylesStr=new Array();
						var lArray=layers.split(",");
						for (var j=0; j<lArray.length; j++) {//recorrem layers actius
							for (k=0;k<layer.styles.length ;k++) {
								if (lArray[j] == layer.styles[k][4]) {
									stylesStr.push(layer.styles[k][1]);
								}
							}
						}
						
						addFieldToForm(formObj,"text","l"+count,reqUrl);
						addFieldToForm(formObj,"text","d"+count,layer.desc);
						addFieldToForm(formObj,"text","s"+count,stylesStr.join(","));
						if (layer.printDPI!="") addFieldToForm(formObj,"text","dpi"+count,layer.printDPI);
					}
					addFieldToForm(formObj,"text","t"+count,layer.transparency);
					count++;
				}
			}
		}
		
		try {
			for (var i=0; i<this.coordsArray.count(); i++) {
				addFieldToForm(formObj,"text","l"+count,"coord:"+this.coordsArray.item(i).getCoord().getX()+","+this.coordsArray.item(i).getCoord().getY());
				addFieldToForm(formObj,"text","s"+count,this.coordsArray.item(i).getImagePath());
				count++;
			}
		} catch(e){alert("err coordsArray: "+e.message);} // TODO
		try {
			for (var i=0; i<this.pixelsArray.count(); i++) {
				addFieldToForm(formObj,"text","l"+count,"pixel:"+this.pixelsArray.item(i).getCoord().getX()+","+this.pixelsArray.item(i).getCoord().getY());
				addFieldToForm(formObj,"text","s"+count,this.pixelsArray.item(i).getImagePath());
				count++;
			}
		} catch(e){alert("err pixelsArray: "+e.message);} // TODO
		
		addFieldToForm(formObj,"text","XML",legendUrl);
		addFieldToForm(formObj,"text","XSL","1");
		addFieldToForm(formObj,"text","ANGLE",Math.round(360-this.printAngle));
		
		formObj.action=this.getMapServerPDFUrl();
		formObj.submit();
	}
	catch (e) {
		alert("Error a NMap.createPDF()\n"+e.message);
	}

};

/**
*	Retorna la url de la imatge actual.
*/
NMap.prototype.getCurrentMapImageUrl = function(staticUrl,imageFormat) { 
	if (typeof(staticUrl)=="undefined") staticUrl=this.getMapServerStaticUrl();
	if (typeof(imageFormat)=="undefined") imageFormat="image/gif";
	return staticUrl+this.getQueryURL(this.getLayerNames(),imageFormat,this.width,this.height,this.extent,this.getScale());
};

/**
*	Recarrega la imatge actual. Manté el centre i l'escala.
*/
NMap.prototype.refresh = function() {
	try{
		this.setUseTimeStamp(true);
		this.centerAndZoom(this.getCenter(),this.getScale());
	
	} catch (e) {
		alert("Error a NMap.refresh()\n"+e.message);
	}
};


/**
*	Converteix els pixels de la caixa en coordenades reals i les guarda.
*	@param {Number} pixelX0 Coordenada 1 eix x
*	@param {Number} pixelY0 Coordenada 1 eix y
*	@param {Number} pixelX1 Coordenada 2 eix x
*	@param {Number} pixelY1 Coordenada 2 eix y
*	@private
*/
NMap.prototype.setDrawBboxExtentWithPixels = function(pixelX0,pixelY0,pixelX1,pixelY1) {
	var c0=this.pixelsToCoords(pixelX0,pixelY0);
	var c1=this.pixelsToCoords(pixelX1,pixelY1);

	this.drawBboxExtent = new NExtent(c0,c1);

	this.eventDrawBboxExtentChanged();
};


/**
*	Actualitza les coordenades de la caixa.
*	@param {NExtent} extent la extensió de la caixa
*/
NMap.prototype.setDrawBboxExtent = function(extent) {
	this.drawBboxExtent=extent;
	this.showDrawBboxExtent();

	this.eventDrawBboxExtentChanged();
};

/**
*	Permet configurar els paràmetres de la impressió.
*	@param {Integer} widthPixels amplada de la imatge que s'ha de generar al imprimir.
*	@param {Integer} heightPixels alçada de la imatge que s'ha de generar al imprimir.
*	@param {Integer} scale escala a la que s'ha de imprimir
*	@param {NPoint} center punt on centrar la impressió
*	@param {Integer} angle angle de rotació de la imatge
*	@param {Boolean} showPreview permet activar que es mostri la extensió sobre el mapa.
*	@param {String} printWMSBase per configurar una url com a servidor wms. Si s'especifica, ja no s'utilitza la configuració actual del mapa i les capes.
*	@param {String} printWMSLayers per configurar els layers a demanar a la petició WMS del servidor de printWMSBase.
*	@param {String} printWMSFormat per configurar el format de la imatge demanada.
*	@param {NPois} coordsArray llista de punts en coordenades a dibuixar sobre el mapa.
*	@param {NPois} pixelsArray llista de punts en pixels a dibuixar sobre el mapa.
*	@param {String} printBackColorRGB el color de fons del mapa, en format RGB. Exemple: 255,255,255.
*/
NMap.prototype.setPrintConfig = function(widthPixels,heightPixels,scale,center,angle,showPreview,printWMSBase,printWMSLayers,printWMSFormat,coordsArray,pixelsArray,printBackColorRGB) {

	try {
		if (typeof(showPreview)=="undefined") showPreview=false;

		var coord1=this.pixelsToCoordsWithParams (0,0,widthPixels,heightPixels,center,scale);
		var coord2=this.pixelsToCoordsWithParams (widthPixels,heightPixels,widthPixels,heightPixels,center,scale);
		var extent=new NExtent(coord1,coord2);

		var polygon=extent.getPolygon();
		polygon.rotate(angle*Math.PI/180,center);
		//alert(center+"\n"+scale+"\n"+angle+"\n"+widthPixels+"\n"+heightPixels+"\n"+polygon);
		this.printWidthPixels=widthPixels;
		this.printHeightPixels=heightPixels;
		this.printScale=scale;
		this.printCenter=center;
		this.printAngle=angle;
		this.printPolygon=polygon;
		this.printExtentWithoutRotation=extent;
		this.printPreviewShow=showPreview;
		
		this.showPrintBboxExtent(this.printPolygon);
	
		if (typeof(printWMSBase)=="undefined" || printWMSBase=="" || printWMSBase==null) this.printWMSBase="";
		else this.printWMSBase=printWMSBase;
		if (typeof(printWMSLayers)=="undefined" || printWMSLayers=="" || printWMSLayers==null) this.printWMSLayers="";
		else this.printWMSLayers=printWMSLayers;
		if (typeof(printWMSFormat)=="undefined" || printWMSFormat=="" || printWMSFormat==null) this.printWMSFormat="";
		else this.printWMSFormat=printWMSFormat;

		if (typeof(coordsArray)=="undefined" || coordsArray==null) coordsArray=new NPois();
		if (typeof(pixelsArray)=="undefined" || pixelsArray==null) pixelsArray=new NPois();
		this.coordsArray=coordsArray;
		this.pixelsArray=pixelsArray;
		
		if (typeof(printBackColorRGB)=="undefined" || printBackColorRGB==null) printBackColorRGB="255,255,255";
		this.printBackColorRGB=printBackColorRGB;
	}
	catch (e) {
		alert("Error a NMap.setPrintConfig\n"+ e.message);
	}
};


/**
*	Retorna la extensió del bounding box dibuixat. Si no existeix o no esta definida retorna null.
*	@return {NExtent}
*/
NMap.prototype.getDrawBboxExtent = function() {
	return this.drawBboxExtent;
};

/**
*	Recalcula els pixels del bbox i dibuixa el div a la interfície.
*/
NMap.prototype.showDrawBboxExtent = function() {
	try {

		if (this.drawBboxExtent==null || this.getScale()==0) return;

		var pixel0=this.coordsToPixels(this.drawBboxExtent.getMinX(),this.drawBboxExtent.getMaxY());
		var pixel1=this.coordsToPixels(this.drawBboxExtent.getMaxX(),this.drawBboxExtent.getMinY());

		// box coords and size
		var boxWidth = Math.abs(pixel1.getX()-pixel0.getX());
		var boxHeight = Math.abs(pixel1.getY()-pixel0.getY());

		this.drawBboxDiv.style.left = pixel0.getX()+'px';
		this.drawBboxDiv.style.top = pixel0.getY()+'px';
		this.drawBboxDiv.style.width = boxWidth+'px';
		this.drawBboxDiv.style.height = boxHeight+'px';
		this.drawBboxDiv.style.visibility="visible";
	
	}
	catch (e) {
		alert("Error a NMap.showDrawBboxExtent\n"+ e.message);
	}
};



/**
*	Oculta el div, però no elimina la variable amb el bounding box
*	Utilitzat en les transicions entre mapes(zooms, pans,...)
*	@private
*/
NMap.prototype.hideDrawBboxExtent = function() {
	this.drawBboxDiv.style.visibility="hidden";
};

/**
*	Oculta el div i elimina la variable amb el bounding box.
*/
NMap.prototype.clearDrawBboxExtent = function() {
	this.hideDrawBboxExtent();
	this.drawBboxExtent = null;

	this.eventDrawBboxExtentChanged();
};

/**
*	Metode que s'executa en canviar el bbox. A redifinir per l'usuari
*	- setDrawBboxExtentWithPixels
*	- setDrawBboxExtent
*	- clearDrawBboxExtent
*/
NMap.prototype.eventDrawBboxExtentChanged = function() {};

/**
*	Recalcula els pixels del bbox i dibuixa el div a la interfície.
*/
NMap.prototype.showPrintBboxExtent = function(polygon) {
	try {
		
		if (!this.printPreviewShow || typeof(polygon)=="undefined" || polygon==null) return;

		if (typeof(this.printPreviewColor)=="undefined") fillColor="#ff0000";
		else fillColor=this.printPreviewColor;

		var xPoints=new Array();
		var yPoints=new Array();

		for (var i=0;i<polygon.size();i++ ) {
			var punt=polygon.get(i);
			var pixel=this.coordsToPixels(punt.getX(),punt.getY());
			xPoints.push(pixel.getX());
			yPoints.push(pixel.getY());
		}

		try {
			this.jgPrint.clear();
		}	catch (e) {	}
		this.jgPrint = new jsGraphics(this.printBboxDiv);
		this.jgPrint.clear();


		this.jgPrint.setFont("arial","14px",Font.ITALIC_BOLD);
		this.jgPrint.clear();
		this.jgPrint.setColor(fillColor); // red
		this.jgPrint.fillPolygon(xPoints, yPoints);
		/*this.jgPrint.setColor("#000000");
		this.jgPrint.drawString("baix-esquerra", xPoints[0], yPoints[0]);
		this.jgPrint.drawString("dalt-esquerra", xPoints[1], yPoints[1]);
		this.jgPrint.drawString("dalt-dreta", xPoints[2], yPoints[2]);
		this.jgPrint.drawString("baix-dreta", xPoints[3], yPoints[3]);*/
		this.jgPrint.paint();

		this.printBboxDiv.style.left = '0px';
		this.printBboxDiv.style.top = '0px';
		this.printBboxDiv.style.width = this.width+'px';
		this.printBboxDiv.style.height = this.height+'px';
		this.printBboxDiv.style.visibility="visible";
	
	}
	catch (e) {
		alert("Error a NMap.showPrintBboxExtent\n"+ e.message);
	}
};

/**
*	Recalcula els pixels del bbox i dibuixa el div a la interfície.
*/
NMap.prototype.centerPrintBboxExtent = function(targetExtent) {
	try {
		if (this.printPolygon==null) return;
		
		this.printPolygon.centerTo(targetExtent.getCenter());

	} catch (e) {
		alert("Error a NMap.centerPrintBboxExtent\n"+ e.message);
	}
};

/**
*	Oculta el div, però no elimina la variable amb el bounding box
*	Utilitzat en les transicions entre mapes(zooms, pans,...) per ocultar-ho temporalment.
*	@private
*/
NMap.prototype.hidePrintBboxExtent = function() {
	this.printBboxDiv.style.visibility="hidden";
};


/**
*	Oculta el div i elimina la variable amb el bounding box.
*/
NMap.prototype.clearPrintBboxExtent = function() {
	this.hidePrintBboxExtent();
	this.printPolygon=null;
	this.printPreviewShow=false;

	//this.eventDrawBboxExtentChanged();
};



/**
*	Metode que s'executa en canviar el bbox. A redifinir per l'usuari
*	- setDrawBboxExtentWithPixels
*	- setDrawBboxExtent
*	- clearDrawBboxExtent
*/
NMap.prototype.poiClick = function(identifier) {
	if (this.getCurrentTool()=="NOEVENTS") {
		//alert ('>> :poiClick: ' + identifier);

		var poiCodes = "HOSP_WFS,MSCAP_WFS";
		WFSProxy.getPoiInfo(poiCodes,this.extent.getMinX(),this.extent.getMinY(),this.extent.getMaxX(),this.extent.getMaxY(),this.width,this.height, identifier,loadPoiInfo_response);


	}
	return false;
};




/**
*	Recalcula els pixels dels pois i dibuixa el div a la interfície.
*	@param {boolean} computePixels [default:true] Indica si cal recalcular els pixels a partir de les coordenades del poi. Quan ja hi venen calculades(per ex WFSServer de dwr) es desactiva pq vagi més ràpid.
*/
NMap.prototype.showPois = function(computePixels) {
	if (typeof(computePixels)=="undefined") computePixels=true;

	try {

		//DebugOut("showPois count: "+this.pois.count(),DEBUG);
		if (this.pois.isEmpty()) return false;

		// recalculem pixels
		if (computePixels) this.pois.computePixels(this.extent,this.width,this.height);

		var str="";
		var count=this.pois.count();
		var strArray=new Array();
		for (var i=0;i<count ;i++ ) {
			var poi=this.pois.item(i);
			if (poi.type=="textBubble") {

				strArray.push('<span id="'+poi.identifier+'" style="position: absolute; z-index:999; top: '+(poi.getPixels().getY()-5)+'px; left: '+(poi.getPixels().getX()-35)+'px;">');
				strArray.push('	<span style="display: block; opacity: 0.80; filter:alpha(opacity:80);"  class="tooltip">');
				strArray.push('		<span style="display: block; float:right; margin-top:26px; margin-right:5px; cursor:pointer;" onmousedown="javascript: '+this.mapInstanceName+'.closePoiBubble(event,\''+poi.identifier+'\');"><img src="img/btclose.gif" alt=""/></span>');
				strArray.push('		<span id="'+poi.identifier+'1" style="display: block;" class="top"><font style="display:inline; white-space: normal;border:1px;">'+poi.getTitle()+'</font></span>');
				strArray.push('		<b id="'+poi.identifier+'3" style="display: block; cursor:pointer;" class="bottom"></b>');
				strArray.push('	</span>');
				strArray.push('</span>');

			} else {			
				strArray.push("<img onMouseUp='javascript: "+this.mapInstanceName+".poiClick(\""+poi.identifier+"\");' src='"+poi.getImagePath()+"' alt='' title='"+poi.getTitle()+"' style='cursor:help; position:absolute; left:"+poi.getPixels().getX()+"px; top:"+poi.getPixels().getY()+"px;' />");
			}
		}
		this.poisDiv.style.visibility="visible";
		this.poisDiv.innerHTML=strArray.join("");
		
	}
	catch (e) {
		alert("Error aNMap.showPois()\n"+e.message);
	}
};



/**
*	Click a tancar
*	@param {Event} evt
*	@param {String} poiId identificador del poi.
*/
NMap.prototype.closePoiBubble = function(evt,poiId) {
	try {
		// TODO: no treure'ls tots sinò només el que s'ha clickat.
		this.hidePois();
		this.pois.clear();

		if (!evt && window.event) evt=event;
		evt.cancelBubble=true;
		if (evt.stopPropagation) evt.stopPropagation();
		return false;

	} catch(e) {
		alert("Error a NMap.closePoi()\n"+e.message);
	}
}

/**
*	Oculta el div i elimina la llista de pois.
*/
NMap.prototype.clearPois = function() {
	this.hidePois();
	this.pois.clear();
};

/**
*	Oculta el div.
*	Utilitzat en les transicions entre mapes(zooms, pans,...)
*	@private
*/
NMap.prototype.hidePois = function() {
	this.poisDiv.style.visibility="hidden";
};




/** 
*	Posa un layer virtual/estatic per aquest layer, hi situa la imatge actual del mapa i mante la posició de drag&drop.
*	@private
*/
NMap.prototype.beforeQueryPan = function(targetLayer) {
	try {
		if(!this.panAnimActive) return;

		// posem layer estatic a la mateixa posicio que l'original.
		/*this.mapLayer[targetLayer].staticDiv.style.top=this.mapTop.style.top;
		this.mapLayer[targetLayer].staticDiv.style.left=this.mapTop.style.left;
		this.mapLayer[targetLayer].staticDiv.style.border="0px solid black";
		
		this.mapLayer[targetLayer].staticImg.src=this.mapLayer[targetLayer].queryUrl;
		*/
		this.mapLayer[targetLayer].staticImg.src=this.mapLayer[targetLayer].queryUrl;
		this.mapLayer[targetLayer].staticImg.style.position="relative";
		this.mapLayer[targetLayer].staticImg.style.width=this.width+"px";
		this.mapLayer[targetLayer].staticImg.style.height=this.height+"px";
		this.mapLayer[targetLayer].staticImg.style.top=this.mapTop.style.top;
		this.mapLayer[targetLayer].staticImg.style.left=this.mapTop.style.left;

		// carreguem a l'estatic la imatge de l'original
		
		// posem estatic a visible i original a invisible
		this.mapLayer[targetLayer].staticDiv.style.visibility="visible";
		this.mapLayer[targetLayer].targetDiv.style.visibility="hidden";

	}
	catch (e) {
		alert("Error a beforeQueryPan("+targetLayer+")\n"+e.message);
	}
};


/** 
*	Posa un layer virtual/estatic per aquest layer, hi situa la imatge actual del mapa escalada perque mentre es carregui la nova
+	imatge del mapa, vegi la actual pixelada.
*	@private
*/
NMap.prototype.beforeQueryZoomIn = function(targetLayer,pixBoxX0,pixBoxY0,pixBoxX1,pixBoxY1) {
	DebugOut("NMap.beforeQueryZoom()",DEBUG);
	try {
		if(!this.zoomAnimActive) return;
			
		// muntem un box amb els pixels i el quadrem.
		var pixExtent=new NExtent(new NPoint(pixBoxX0,pixBoxY0),new NPoint(pixBoxX1,pixBoxY1));
		pixExtent.squarePixels(this.getImageWidth(),this.getImageHeight());
		// calculem el multiplicador(la relació entre la imatge actual i la que expandirem.
		var multResizeX=Math.abs(this.getImageWidth()/(pixExtent.getMaxX()-pixExtent.getMinX()));
		var multResizeY=Math.abs(this.getImageHeight()/(pixExtent.getMaxY()-pixExtent.getMinY()));
		// ja podem calcular les mides de la imatge expandida
		var newWidth=Math.floor(this.getImageWidth()*multResizeX);
		var newHeight=Math.floor(this.getImageHeight()*multResizeY);
		// ara calculem les coordenades que corresponen a les (0,0) de la actual.
		var newLeft=Math.floor(-(pixExtent.getMinX())*multResizeX);
		var newTop=Math.floor(-(pixExtent.getMinY())*multResizeY);

		//this.mapLayer[targetLayer].staticDiv.innerHTML='<img id="mapImage" style="position:relative; width:'+newWidth+'px; height:'+newHeight+'px; top:'+newTop+'px; left:'+newLeft+'px;" onmousedown="return false;" src="'+ this.mapLayer[targetLayer].queryUrl +'" />';
		this.mapLayer[targetLayer].staticImg.src=this.mapLayer[targetLayer].queryUrl;
		this.mapLayer[targetLayer].staticImg.style.position="relative";
		this.mapLayer[targetLayer].staticImg.style.width=newWidth+"px";
		this.mapLayer[targetLayer].staticImg.style.height=newHeight+"px";
		this.mapLayer[targetLayer].staticImg.style.top=newTop+"px";
		this.mapLayer[targetLayer].staticImg.style.left=newLeft+"px";
		this.mapLayer[targetLayer].staticImg.onmousedown=function() {return false;};

/*		this.mapLayer[targetLayer].staticDiv.style.top=this.mapTop.style.top;
		this.mapLayer[targetLayer].staticDiv.style.left=this.mapTop.style.left;
		this.mapLayer[targetLayer].staticDiv.style.border="0px solid black";*/

		// posem estatic a visible i original a invisible
		this.mapLayer[targetLayer].staticDiv.style.visibility="visible";
		this.mapLayer[targetLayer].targetDiv.style.visibility="hidden";
		
	}
	catch (e) {
		alert("Error a NMap.beforeQueryZoom()\n"+e.message);
	}
};


/** 
*	Posa un layer virtual/estatic per aquest layer, hi situa la imatge actual del mapa escalada perque mentre es carregui la nova
+	imatge del mapa, vegi la actual pixelada.
*	@private
*	@TODO : PEL ZOOM OUT ENCARA RS'HA D'IMPLEMENTAR.
*/
NMap.prototype.beforeQueryZoomOut = function(targetLayer,pixBoxX0,pixBoxY0,pixBoxX1,pixBoxY1) {
	DebugOut("NMap.beforeQueryZoom()",DEBUG);
	try {
		if(!this.zoomAnimActive) return;
		
		// muntem un box amb els pixels i el quadrem.
		var pixExtent=new NExtent(new NPoint(pixBoxX0,pixBoxY0),new NPoint(pixBoxX1,pixBoxY1));
		pixExtent.squarePixels(this.getImageWidth(),this.getImageHeight());
		// calculem el multiplicador(la relació entre la imatge actual i la que expandirem.
		var multResizeX=Math.abs((pixExtent.getMaxX()-pixExtent.getMinX())/this.getImageWidth());
		var multResizeY=Math.abs((pixExtent.getMaxY()-pixExtent.getMinY())/this.getImageHeight());
		// ja podem calcular les mides de la imatge expandida
		var newWidth=Math.floor(this.getImageWidth()*multResizeX);
		var newHeight=Math.floor(this.getImageHeight()*multResizeY);
		// ara calculem les coordenades que corresponen a les (0,0) de la actual.
		var newLeft=Math.floor((pixExtent.getMinX())*multResizeX);
		var newTop=Math.floor((pixExtent.getMinY())*multResizeY);

		/*alert (	 'image w,h:	' + this.getImageWidth() + " " +this.getImageHeight() 
				+"\nnew mult x,y:	" + multResizeX + " " +multResizeY 
				+"\nnew w,h:		" + newWidth + " " +newHeight 
				+"\nnew left,top:	" + newLeft + " " +newTop );*/

		
		this.mapLayer[targetLayer].staticDiv.innerHTML='<img id="mapImage" style="position:relative; width:'+newWidth+'px; height:'+newHeight+'px; top:'+newTop+'px; left:'+newLeft+'px;" onmousedown="return false;" src="'+ this.mapLayer[targetLayer][L_MAPURL] +'" />';
		this.mapLayer[targetLayer].staticDiv.style.top=this.mapTop.style.top;
		this.mapLayer[targetLayer].staticDiv.style.left=this.mapTop.style.left;
		this.mapLayer[targetLayer].staticDiv.style.border="0px solid black";
		// posem estatic a visible i original a invisible
		this.mapLayer[targetLayer].staticDiv.style.visibility="visible";
		this.mapLayer[targetLayer].targetDiv.style.visibility="hidden";
		
	}
	catch (e) {
		alert("Error a beforeQueryZoom()\n"+e.message);
	}
};



/** 
*	Event cridat quan s'ha rebut la resposta a una petició.
*	@private
*/
NMap.prototype.afterQueryReceived = function(targetLayer) {
	try {
		var l=this.mapLayer[targetLayer];
		l.staticDiv.style.visibility="hidden";
		l.targetDiv.style.visibility="visible";
	}
	catch (e) {
		alert("Error a afterQueryReceived()\n"+e.message);
	}
};


/** 
*	Retorna el layer amb l'id especificat. Si no el troba retorna null. 
*	@param {String} id identificador
*	@return {NMapLayer}
*/
NMap.prototype.getLayerById= function(id) {
	
	var i=0; 
	var trobat=false;
	while (i<this.mapLayer.length && !trobat) {
		if (this.mapLayer[i].id==id) trobat=true;
		else i++;
	}
	if (!trobat) return null;
	else return this.mapLayer[i];
};

/** 
*	Retorna el layer de la posició passada per parametre.
*	@param {Number} pos posició.
*	@return {NMapLayer}
*/
NMap.prototype.getLayerByPos= function(pos) {
	
	return this.mapLayer[pos];
};

/** 
*	Especifica el color a mostrar al dibuixar la caixa de preview d'impressió.
*	@param {String} color 
*/
NMap.prototype.setPrintPreviewColor= function(color) {
	this.printPreviewColor=color;
};


/** 
*	Especifica el color a mostrar al dibuixar la caixa de preview d'impressió.
*	@param {String} color 
*/
NMap.prototype.forceTransparency= function() {
	try {
		for (var i=0; i<this.mapLayer.length ;i++ ) {
			this.mapLayer[i].setTransparency(this.mapLayer[i].transparency);

		}
	}
	catch (e) {
		alert("Error a NMap.forceTransparency()\n"+e.message);
	}
};






/** 
*	Constructor de l'objecte. Representa una capa/imatge del mapa. 
*	@constructor
*/
function NMapLayer() {
  this.init();
}

/** 
*	Metode cridat pel constructor
*	@private
*/
NMapLayer.prototype.init = function() {
	try {
	
		/**
		*	tipus de 
		*/
		this.type="wms";		// tipus de layer. wms, wfs
		this.targetDiv;		// objecte div associat a aquesta capa.
		this.targetImg;		// objecte img a on carregar les imatges.
		this.staticDiv;		// objecte div associat a aquesta capa.
		this.staticImg;		// objecte img a on carregar les imatges.
		this.layersList="";	// llista de layers actius separats per comes.
		this.imageFormat="";	// format de la imatge. Només aplica a wms.
		this.serverUrl="";	// url parcial del servidor wms, wfs.
		this.queryUrl="";		// url de la ultima peticio. Serveix per controlar quan es repeteix una peticio i evitar fer-la.
		this.imgObject="";
		this.id="";				// identificador per aquest layer. Hi posarem el nom del service del toc.
		this.desc="";			// descripció per el layer. Hi posarem el desc del service del toc.
		this.transparency=0;	// tant per cent de transparencia. 0=opac, 100=transparent
		this.styles;			// guardarem estils WFS obtinguts del ncontent
		
		this.printUrl="";		// la url a utilitzar en fer una petició per la impressió.
		this.printDPI="";		// els dpis de la imatge retornada per el printUrl.

		this.poisList = new NPois();

		this.parameters=new Array();// llista de parelles nom/valor que s'afegeixen a la petició: ...&nom=valor

		//TODO: tambe hauria d'anar-hi les configuracions de wms: epsg, transparent, userid(?).
		//TODO: tots els mètodes de get/set que ara estan a NMap haurien de passar a nivell de Layer.

	}
	catch (e) {
		alert("Error a NMapLayer.init()\n"+e.message);
	}

};

/** 
*	Retorna cert si el parametre amb el nom donat existeix.
*	@param {String} name el nom del parametre
*	@return {boolean}
*/
NMapLayer.prototype.parameterExists = function(name) {
	try {
		var i=0;
		var trobat=false;
		while (i<this.parameters.length && !trobat) {
			if (this.parameters[i][0]==name) {
				trobat=true;
			} else i++;
			
		}
		return trobat;
	}
	catch (e) {
		alert("Error a NMapLayer.parameterExists()\n"+e.message);
		return false;
	}
}

/** 
*	Afegeix o substitueix el parametre passat.
*	@param {String} name el nom del parametre
*	@param {String} value el valor del parametre
*/
NMapLayer.prototype.addParameter = function(name,value) {
	try {
		var i=0;
		var trobat=false;
		while (i<this.parameters.length && !trobat) {
			if (this.parameters[i][0]==name) {
				this.parameters[i][1]=value;
				trobat=true;
			} else i++;
		}
		if (!trobat) {
			this.parameters[i]=new Array();
			this.parameters[i][0]=name;
			this.parameters[i][1]=value;
		}
	}
	catch (e) {
		alert("Error a NMapLayer.addParameter()\n"+e.message);
	}
}

/** 
*	Retorna un string amb el conjunt de parametres amb el format &nom=valor&nom=valor&...
*	@return {String}
*/
NMapLayer.prototype.getParameters = function() {
	try {
		var str=new Array();
		for (var i=0;i<this.parameters.length ;i++ ) {
			str.push("&"+this.parameters[i][0]+"="+escape(this.parameters[i][1]));
		}
		return str.join("");
	}
	catch (e) {
		alert("Error a NMapLayer.getParameters()\n"+e.message);
		return "";
	}
}

/** 
*
*/
NMapLayer.prototype.toString = function() {
	return "(NMapLayer) \ntype:"+this.type+"\ntargetDivId:"+this.targetDiv.id+"\nserverUrl:"+this.serverUrl+"\npoilist:"+this.poisList+"\n";
};


/** 
* Permet especificar la transparencia d'un layer
*/
NMapLayer.prototype.setTransparency = function(percent) {
	try {
		this.transparency=percent;
		opacity=(100-parseInt(percent));
		DebugOut("opacity de " + this.id + " === " + opacity);
		this.targetDiv.style.display = "none";

		this.targetDiv.style.opacity = opacity/100;//firefox
		this.targetDiv.style.filter="Alpha(opacity="+ opacity +");";//iexplorer	
		this.staticDiv.style.opacity = opacity/100;//firefox
		this.staticDiv.style.filter="Alpha(opacity="+ opacity +");";//iexplorer	

		this.targetDiv.style.display = "block";

	}
	catch (e) {
		alert("Error a NMapLayer.setTransparency()\n"+e.message);
	}
}

/** 
* Permet especificar l'index z de la imatge.
*/
NMapLayer.prototype.setZIndex = function(value) {
	try {
		this.targetDiv.style.zIndex = value;
		this.staticDiv.style.zIndex = value;
	}
	catch (e) {
		alert("Error a NMapLayer.setZIndex()\n"+e.message);
	}
}

/** 
* Permet especificar els estils WFS d'aquest layer.
*/
NMapLayer.prototype.setStyles = function(styles) {
	try {
		this.styles=styles;
	}
	catch (e) {
		alert("Error a NMapLayer.setStyles()\n"+e.message);
	}
}

/** 
*	Configura les capes a demanar per la imatge d'aquest layer. 
*	@param {String} layersStr Numero de la capa a configurar
*	//@param {String} serverStr URL del servidor WMS a utilitzar per fer la petició d'aquesta capa/imatge
*/
NMapLayer.prototype.setLayers = function(layersStr,serverStr) {

	this.layersList=layersStr;

	if (typeof(serverStr)!="undefined" && serverStr!="") {// pq no ens quedi en undefined quan facin crida sense param
		this.serverUrl=serverStr;
	}
	DebugOut("NMap.NMapLayer LAYERS: "+layersStr + "  ("+this.serverUrl+")",DEBUG);
}

/** 
*	Retorna el servidor per aquest layer.
*	@return {String} url del servidor.
*/
NMapLayer.prototype.getServerUrl = function() {
	return this.serverUrl;
}

/** 
*	Retorna el format de la imatge del layer
*	@return {String}
*/
NMapLayer.prototype.getImageFormat = function() {
	return this.imageFormat;
}