Source: temp/jsdocinputdirs/cercalia.routemap.js

/**
 * @classdesc
 * 
 * RouteMap is the core of Routes. RouteMap creates, modifies and clears route.
 * 
 * @class
 * @constructor
 * @param {cercaliax.RouteMapOptions} options RouteMap options
 */
cercalia.RouteMap = function(options) {

	/**
	 * Class name
	 * @private
	 * @type {string}
	 */
	this.CLASS_NAME_ = "cercalia.RouteMap";

	/**
	 * @private
	 * @type {string}
	 *
	 */
	this.MARKER_ID_ORIGIN_ = "routeMapStopOrigin";

	/**
	 * @private
	 * @type {string}
	 *
	 */
	this.MARKER_ID_STEPS_ = "routeMapStopStep";//S'hi afegeix l'index

	/**
	 * @private
	 * @type {string}
	 *
	 */
	this.MARKER_ID_DESTINATION_ = "routeMapStopDestination";

	/**
	 * @private
	 * @type {cercalia.Map}
	 */
	this.map_ = options.map ? options.map : null;
	
	/**
	 * @private
	 * @type {boolean}
	 */
	this.centerToRoute_ = options.centerToRoute ? options.centerToRoute : true;
	
	/**
	 * @private
	 * @type {boolean}
	 */
	this.centerToRoute_ = options.centerToRoute ? options.centerToRoute : true;
	
	/**
	 * @private
	 * @type {boolean}
	 */
	this.autoCalculateRoute_ = options.autoCalculateRoute ? options.autoCalculateRoute : true;
	
	/**
	 * @private
	 * @type {cercalia.widget.Routing}
	 */
	this.widgetRouting_ = options.widget ? options.widget : null;
	
	/**
	 * @private
	 * @type {cercalia.widget.RoutingReport}
	 */
	this.widgetRoutingReport_ = options.widgetReport ? options.widgetReport : null;
	
	/**
	 * @private
	 * @type {cercaliax.service.RoutingOptions}
	 */
	this.serviceOptions_ = options.serviceOptions ? options.serviceOptions : { version: 2 };
	
	/**
	 * @private
	 * @type {cercalia.service.Routing}
	 */
	this.routingService_ = this.widgetRouting_ && this.widgetRouting_.isLogistics() ? new cercalia.service.LogisticsRouting(this.serviceOptions_) : new cercalia.service.Routing(this.serviceOptions_);
		
	/**
	 * @private
	 * @type {cercalia.ContextMenu}
	 */
	this.contextMenu_;
	
	/**
	 * @private
	 * @type {cercalia.LonLat}
	 */
	this.positionOnStartDrag_ = null
	
	/**
     * @private
     * @type {Array<cercalia.LonLat>}
     */
	this.wayPointsDrag_ = [ ];
	
	/**
     * @private
     * @type {cercalia.Feature}
     */
	this.featureRouteDrag_ = null;
	
	
	/**
	 * @private
	 * @type {Array.<cercalia.Feature>}
	 */
	this.features_ = null;
	
	var self = this;


    //init function
    this.init_();
};

/**
 * @private
 */
cercalia.RouteMap.prototype.init_ = function(){
	var self = this;
	var markerRouteDragId = 'MarkerRouteDrag' + (this.map_ ? this.map_.getId() : '');
	this.markerRouteDrag_ = this.map_ ? this.map_.getMarkerById(markerRouteDragId) : null;

	if( ! this.markerRouteDrag_) {
		/**
	     * @private
	     * @type {cercalia.Marker}
	     */	 
	    this.markerRouteDrag_ = new cercalia.Marker({
	    	id: markerRouteDragId,
	        position: new cercalia.LonLat(0,-85), //fora de marges
	        icon : new cercalia.Icon({
	        	src : cercaliaGlobals.img + '/point.png'
	        }),
	        label: new cercalia.Label({
	        	lightLabel: true,
	        	show : false,
	            text: cercalia.i18n._('Drag to change route'),
	            offsetY: 8,
	            offsetX: 0
	        }),
	        draggable: true,
	        onDragStart: function(marker, lonLat, evt){
	        	this.hideLabel();
	        	this.positionOnStartDrag_ = lonLat;
	            //var stringCoordinates = lonLat.getLon() + "," + lonLat.getLat();
	        	
	        },
	        // onDragMove: function(marker, lonLat, evt){},
	        onDragEnd: function(marker, lonLat, evt){
	        	self.addPointDrag_(marker, lonLat, evt);
	        }
	    });

	    //this.markerRouteDrag_.routing = this;
	    this.markerRouteDrag_.setVisible(false);
	    this.markerRouteDrag_.hideLabel();

	    this.map_.addMarker(this.markerRouteDrag_); 
	}

	var timeoutid = 0;
	
	if(!cercalia.isMobile){
		cercalia.jQuery(self.map_.getMap().getViewport()).on('mousemove', function(e) {
			if (timeoutid) {
				clearTimeout(timeoutid);
				timeoutid = 0;
			}
			timeoutid = setTimeout(function(){self.onMouseMove_(e);}, 20);
		});
	}
};

/**
 * @return {string} ClassName.
 */
cercalia.RouteMap.prototype.getClass = function(){
	return this.CLASS_NAME_;
};

/**
 * @return {cercalia.widget.Routing}
 */
cercalia.RouteMap.prototype.getWidget = function(){
	return this.widgetRouting_;
};

/**
 * @param {cercalia.widget.Routing} widget
 */
cercalia.RouteMap.prototype.setWidget = function(widget){
	this.widgetRouting_ = widget;
	widget.setRouteMap(this);
	
	if(this.widgetRouting_ && this.widgetRouting_.isLogistics() && !this.routingService_){
		this.routingService_ =  new cercalia.service.LogisticsRouting(this.serviceOptions_);
	}
	return this;
};

/**
 * @return {cercalia.widget.RoutingReport}
 */
cercalia.RouteMap.prototype.getWidgetReport = function(){
	return this.widgetRoutingReport_;
};

/**
 * @param {cercalia.widget.RoutingReport} widget
 */
cercalia.RouteMap.prototype.setWidgetReport = function(widget){
	this.widgetRoutingReport_ = widget;
	return this;
};

/**
 * @return {boolean} autoCalculateRoute
 */
cercalia.RouteMap.prototype.getAutoCalculateRoute = function(){
	return this.autoCalculateRoute_;
};

/**
 * @param {boolean} autoCalculateRoute  
 * @return {cercalia.RouteMap} this
 */
cercalia.RouteMap.prototype.setAutoCalculateRoute = function(autoCalculateRoute){
	this.autoCalculateRoute_ = autoCalculateRoute;
	return this;
};

/**
 * Set origin of route
 * @param {cercalia.LonLat|Object} origin
 * @param {boolean} calculateRoute calculateRoute -> true Calcule route; calculateRoute -> false NO Calcule route; calculateRoute -> undefined depends of autoCalculateRoute
 * @param {string} text label text for marker
 */
cercalia.RouteMap.prototype.setOrigin = function(origin, calculateRoute, text){
	if(origin){
		origin.label = text;
		var self = this;
		
		//eliminem els markers drag existents
		this.removeDragStepsFromService_();
		this.removeDragMarkers_();
		
		/** Marker **/
		//eliminem l'existent
		//if(this.routingService_.markerOrigin){			
		var markerOrigin = this.map_.getMarkerById(this.MARKER_ID_ORIGIN_);
		if(markerOrigin) {
	    	this.map_.removeMarkers(markerOrigin);
	        this.routingService_.markerOrigin = null;
		}
		
		if(origin.getClass && origin.getClass() === "cercalia.LonLat"){
			//Nou marker.
			this.routingService_.markerOrigin = new cercalia.Marker({
				id: this.MARKER_ID_ORIGIN_,
		        icon: new cercalia.Icon({src: cercaliaGlobals.img + '/cercalia-marker-icon-A.png', anchor:[12,34]}),
		        position: origin,
		        label : new cercalia.Label({
		        	text: text ? text : cercalia.i18n._('Origin'),offsetY:38
		        }),
		        draggable: true,
	            onDragEnd: function(marker, newPosition, event) {
	                self.dragEndCallback_("o", marker, newPosition, event);
	            }
		    });
			
			this.routingService_.markerOrigin.textLabel = text;
			this.map_.addMarker(this.routingService_.markerOrigin);
		}
		
		//put Origin in service and widget
		this.routingService_.setOrigin(origin);
		if(this.widgetRouting_){
			this.widgetRouting_.setOrigin(origin);
		}

		if(calculateRoute || ( (calculateRoute === undefined || calculateRoute == null)  && this.autoCalculateRoute_)) {
			this.calculateRoute();
		}
	}
	
	return this; // for concat calls
};

/**
 * Set destination of route
 * @param {cercalia.LonLat|Object} destination
 * @param {boolean} calculateRoute calculateRoute -> true Calcule route; calculateRoute -> false NO Calcule route; calculateRoute -> undefined depends of autoCalculateRoute
 * @param {string} text label text for marker
 */
cercalia.RouteMap.prototype.setDestination = function(destination, calculateRoute, text){
	if(destination){
		var self = this;
		destination.label = text;

		//eliminem els markers drag existents
		this.removeDragStepsFromService_();
		this.removeDragMarkers_();
		
		/** Marker **/
		//eliminem l'existent
		var markerDestination = this.map_.getMarkerById(this.MARKER_ID_DESTINATION_);
		if(markerDestination) {
	    	this.map_.removeMarkers(markerDestination);
	        this.routingService_.markerDestination = null;
		}

		
		if(destination.getClass && destination.getClass() === "cercalia.LonLat"){

			//Nou marker.
			this.routingService_.markerDestination = new cercalia.Marker({
				id: this.MARKER_ID_DESTINATION_,
		        icon: new cercalia.Icon({src:cercaliaGlobals.img + '/cercalia-marker-icon-B.png', anchor:[12,34]}),
		        position: destination,
		        label : new cercalia.Label({text : text ? text : cercalia.i18n._('Destination'),offsetY:38}),
		        draggable: true,
	            onDragEnd: function(marker, newPosition, event) {
	                self.dragEndCallback_("d", marker, newPosition, event);
	            }
		    });
			
			this.routingService_.markerDestination.textLabel = text;
			this.map_.addMarker(this.routingService_.markerDestination);
			
		}
		
		//put Destiny in service and widget
		this.routingService_.setDestination(destination);
		if(this.widgetRouting_){
			this.widgetRouting_.setDestination(destination);
		}
		
		if(calculateRoute || ( (calculateRoute === undefined || calculateRoute == null) && this.autoCalculateRoute_)) { 
			this.calculateRoute();
		}
	}
	
	return this; // for concat calls
};

/**
 * Set step of route
 * @param {cercalia.LonLat|Object} step
 * @param {boolean} calculateRoute calculateRoute -> true Calcule route; calculateRoute -> false NO Calcule route; calculateRoute -> undefined depends of autoCalculateRoute
 * @param {string} text label text for marker
 * @param {integer} position
 */
cercalia.RouteMap.prototype.setStep = function(step, calculateRoute, text, position){
	if(step){
		position = position != null ? parseInt(position) : undefined;
		var self = this;
		step.isDragPoint = false;
		step.label = text;
		
      //eliminem els markers drag existents
		this.removeDragStepsFromService_();
		this.removeDragMarkers_();
        
        var steps = self.routingService_.getSteps(); 
        
        if(position !== undefined && position != null){
        	steps[position] = step;
        } else {
        	steps.push(step);
        }
        
		//put stops in service and widget
        this.routingService_.setSteps(steps);
        
        /** Marker **/
		//eliminem l'existent
        if(this.routingService_.markerSteps && this.routingService_.markerSteps.length > 0){

        	for(var i = 0 ; i < steps.length; i++) {
        		var idMarkerAct = this.MARKER_ID_STEPS_ + "_" + i;
        		var markerAct = this.map_.getMarkerById(idMarkerAct);
        		if(markerAct) {
        			this.map_.removeMarkers(markerAct);
        		}
        	}

			//this.map_.removeMarkers(this.routingService_.markerSteps);
			this.routingService_.markerSteps = [ ];
		}else if( this.routingService_.markerSteps === undefined ||  this.routingService_.markerSteps == null){
			this.routingService_.markerSteps = [ ];
		}
		
		//Markers Steps.
        for(var index = 0; index < steps.length; index++) {
        	if(steps[index] && steps[index].getClass && steps[index].getClass() === "cercalia.LonLat"){
		    	if(this.wayPointsDrag_.indexOf(steps[index]) === -1){
		    	    if(steps[index] && steps[index] instanceof cercalia.LonLat){
				        var idMarkerAct = this.MARKER_ID_STEPS_ + "_" + index;
						var markerAct = this.map_.getMarkerById(idMarkerAct);
						if( ! markerAct ) {
			    	    	var marker = new cercalia.Marker({
			    	    		id: idMarkerAct,
					            icon: new cercalia.Icon({src:cercaliaGlobals.img + '/cercalia-marker-icon-' + (parseInt(index)+1) + '.png', anchor:[12,34]}),
					            position: steps[index],
					            label : new cercalia.Label({
					            	text : steps[index].label ? steps[index].label : cercalia.i18n._('Step "%"', (parseInt(index)+1)),offsetY:38
					            }),
						        draggable: true,
					            onDragEnd: function(marker, newPosition, event) {
					                self.dragEndCallback_("s", marker, newPosition, event, marker.stepIndex);
					            },
					            onRightClick: function(marker, evt){
					            	self.onRightClickStepPoint_(marker, evt);
					            }
					        });
			    	    	
			    	    	marker.textLabel = steps[index].label;
			    	    	marker.stepIndex = index;
								    	    

		    	    		this.map_.addMarker(marker);
		    	    		this.routingService_.markerSteps.push(marker);
		    	    	}
				        
		    	    }
		    	}
        	}
        }
        

        /** put step in widget **/
        if(this.widgetRouting_){
			//this.widgetRouting_.setStep(step, position);
        	
        	var countBox = this.widgetRouting_.getCounterBox();
        	if(position != null){
        		var count = this.widgetRouting_.getCounterBox();
        		
        		//creem les capses que necesitem.
        		
        		for(var i = count ; i < (3 + position) ; i++){
        			this.widgetRouting_.addStepStop();
        		}

        		//posem l'step al widget
        		this.widgetRouting_.setStep(step, position + 1);
        		
        	} else {
        		//creem una capsa
	        	this.widgetRouting_.addStepStop();
	            var pos = this.widgetRouting_.getCounterBox();
	            if(pos >= 3) {
	            	//posem l'step al widget
	                this.widgetRouting_.setStep(step, pos-2); // Descontamos origen y destino
	            } else {
	                this.widgetRouting_.setDestination(step);
	            }
        	}
		}
        
        if(calculateRoute || ( (calculateRoute === undefined || calculateRoute == null)  && this.autoCalculateRoute_)) { 
        	this.calculateRoute();
        }
	}
	
	return this; // for concat calls
};

/**
 * @private
 * @param {string} typeMarker - typeMarker indicates type of marker, it should be one of this "o","d","s"
 * @param {cercalia.Marker} marker
 * @param {cercalia.LonLat} lonLat - new lonLat of marker
 * @param {Event} evt
 * @param {number} step - position of step
 */
cercalia.RouteMap.prototype.dragEndCallback_ = function(typeMarker, marker, lonLat, evt, step){
	var self = this;
	switch(typeMarker) {
	    case "o":
	    	self.setOrigin(lonLat, true, marker.textLabel);
	        break;
	    case "d":
	    	self.setDestination(lonLat, true, marker.textLabel);
	        break;
	    case "s":
	    	self.setStep(lonLat, true, marker.textLabel, step, true);
	        break;
	    case "sd":
	    	self.addPointDrag_(marker, lonLat, evt, step, true);
	    	break;
	    default:
	    	console.error("Error typeMarker: typeMarker should be o, d or s");
	        //alert("Error typeMarker");
	}
};

/**
 * @private
 * @param {cercalia.Marker} marker marker drag
 * @param {cercalia.LonLat} lonLat longitud where set the marker
 * @param {ol.Event} evt event
 * @param {boolean} override 
 */
cercalia.RouteMap.prototype.addPointDrag_ = function(marker, lonLat, evt, position, override){

	var self = this;
    if(lonLat) {
    	lonLat.isDragPoint = true;
    	var steps = self.routingService_.getSteps();            
    	
    	if(position !== undefined && position != null && Array.isArray(position) && position.length == 2){
    		self.wayPointsDrag_[position[1]] = lonLat;
    		steps[position[0]] = lonLat;
    	}else{
    		
    		self.wayPointsDrag_.push(lonLat);
            // agafem els steps i els re-ordanem posant el punt
            steps = self.reorderSteps_(steps, lonLat);
    	}
 
        // tornem a posar els steps re-ordenats
        self.routingService_.setSteps(steps);
        
        /** Marker **/
		//eliminem els markers drag existents
        this.removeDragMarkers_();
        
      //Markers Steps.
        var indexDrag = 0;
        for(var index = 0 ; index < steps.length; index++) {
        	
        	if(steps[index].getClass && steps[index].getClass() === "cercalia.LonLat" && self.wayPointsDrag_.indexOf(steps[index]) >= 0){
        		var markerIdAct = self.MARKER_ID_STEPS_ + "_" + index;
        		var markerAct = self.map_.getMarkerById(markerIdAct);
        		if( ! markerAct ) {
	    	    	var marker = new cercalia.Marker({
	    	    		id: markerIdAct,
			            icon: new cercalia.Icon({src : cercaliaGlobals.img + '/point.png'}),
			            position: steps[index],
				        draggable: true,
			            onDragEnd: function(marker, newPosition, event) {
			                self.dragEndCallback_("sd", marker, newPosition, event, marker.stepIndex);
			            },
			            onRightClick: function(marker, evt){
			            	self.onRightClickDragPoint_(marker, evt);
			            }
			        });
	    	    	
	    	    	marker.textLabel = 'Drag point ' + index;
	    	    	marker.stepIndex = [index, indexDrag];
	    	    	this.map_.addMarker(marker);
	    	    	if(!this.routingService_.markerStepsDrag){
						this.routingService_.markerStepsDrag = [ ];
					}
			        this.routingService_.markerStepsDrag.push(marker);
			        indexDrag++;
			    }
        	}
        	
        }

        if(this.widgetRouting_){
        	var position = position ? parseInt(position[0]) : cercalia.Util.positionOfFeature(this.features_, this.featureRouteDrag_);
			this.widgetRouting_.setStepDrag(lonLat, position, override);
		}
        
        this.calculateRoute();
	}
	
	marker.setVisible(false); //un vagada hem fet tot amagem el marker.
	self.markerRouteDrag_.hideLabel();
	return this;
};

/**
 * Calcule the route 
 * @param {function} callback is called when route has been calculated
 */
cercalia.RouteMap.prototype.calculateRoute = function(callback){
	if(this.canCalculateRoute_()) {
		var self = this;
		this.showOverLayLoading_();
		
		if(this.widgetRouting_){
			
			//posem les opcions de logistics.

			if(this.widgetRouting_.isLogistics() ){
				this.routingService_ = this.widgetRouting_.addLogisticsInService(this.routingService_);
			} else {
				//posem les opcions de la ruta (si l'usuari ho ha indicat)
				this.routingService_ = this.widgetRouting_.addOptionsRouteInService(this.routingService_);
			}
		}		
		this.routingService_.setReporting(this.widgetRouting_!=null || this.widgetRoutingReport_!=null);
		
    	this.routingService_.calculateRoute(function (data) {
    		self.callbackRoutingFn_(data); 
    		
    		//async
    		if(callback){
    			setTimeout(function(){ callback(data); }, 0);
    		}
    	},
    	function(data){
    		self.hideOverLayLoading_();
    	});
    	
       this.setRemoveOptionInContextMenu_();
	}
	
	return this; // for concat calls
};

/**
 * @private
 * @return {boolean} return true if routeService has Origin and Destination, otherwise false.
 */
cercalia.RouteMap.prototype.canCalculateRoute_ = function(){
	return this.hasOrigin() && this.hasDestination();
};

/**
 * @return {boolean} return true if routeService has Origin, otherwise false.
 */
cercalia.RouteMap.prototype.hasOrigin = function(){
	return this.routingService_.getOrigin() != null;
};


/**
 * @return {boolean} return true if routeService has Destination, otherwise false.
 */
cercalia.RouteMap.prototype.hasDestination = function(){
	return this.routingService_.getDestination() != null;
};

/**
 * @private
 * @param {Object} data Cercalia data (json)
 */
cercalia.RouteMap.prototype.callbackRoutingFn_ = function (data) {
	
	var self = this;
	
    this.hideOverLayLoading_();
    
    var wkt = this.routingService_.getWKT();
    
    if(this.features_){
        this.map_.removeFeatures(this.features_);
        this.features_ = [ ];
    }
    
    if(this.routingService_.markerOrigin){
    	//this.map_.removeMarkers(this.routingService_.markerOrigin);
    	this.map_.removeMarkers(this.map_.getMarkerById(this.MARKER_ID_ORIGIN_));
        this.routingService_.markerOrigin = null;
    }
    
    if(this.routingService_.markerDestination){
    	//this.map_.removeMarkers(this.routingService_.markerDestination);
    	this.map_.removeMarkers(this.map_.getMarkerById(this.MARKER_ID_DESTINATION_));
        this.routingService_.markerDestination = null;
    }
    
    if(this.routingService_.markerSteps && this.routingService_.markerSteps.length > 0) {
        for(var index = 0; index < this.routingService_.markerSteps.length; index++) {
        	this.map_.removeMarkers(this.routingService_.markerSteps[index]);
        }
        this.routingService_.markerSteps = [];
    }
    
    //eliminem els markers drag existents
    this.removeDragMarkers_();
    
    var arrayStage = this.routingService_.getStages();
    
    if( !this.features_ ){
		this.features_ = []; 
	}
    
    for(var i = 0 ; i < arrayStage.length ; i++ ){
    	var wktStage = arrayStage[ i ].wkt.value;
	    var featureStage = new cercalia.Feature( { wkt : wktStage } );
	    this.features_.push( featureStage );
	    this.map_.addFeature( featureStage );
    }
    
	var arrMarkers = []
	if(this.routingService_.getOrigin() && this.routingService_.getOrigin() instanceof cercalia.LonLat) { 
		var markerIdAct = this.MARKER_ID_ORIGIN_;
		var markerAct = this.map_.getMarkerById(markerIdAct);
		if( ! markerAct ) {
			var text = this.routingService_.getOrigin().label;
			this.routingService_.markerOrigin = new cercalia.Marker({
				id: markerIdAct,
				icon: new cercalia.Icon({src: cercaliaGlobals.img + '/cercalia-marker-icon-A.png', anchor:[12,34]}),
			  	position: this.routingService_.getOrigin(),
			  	label : new cercalia.Label({text: text ? text : cercalia.i18n._('Origin'),offsetY:38}),
			 	draggable: true,
		       	onDragEnd: function(marker, newPosition, event) {
		        	self.dragEndCallback_("o", marker, newPosition, event);
		     	}
			});
			
			arrMarkers.push(this.routingService_.markerOrigin);
		}
	}
	    
	if(this.routingService_.getDestination() && this.routingService_.getDestination() instanceof cercalia.LonLat) {
		var markerIdAct = this.MARKER_ID_DESTINATION_;
		var markerAct = this.map_.getMarkerById(markerIdAct);
		if( ! markerAct ) {
			var text = this.routingService_.getDestination().label;
			this.routingService_.markerDestination = new cercalia.Marker({
				id: markerIdAct,
				icon: new cercalia.Icon({src:cercaliaGlobals.img + '/cercalia-marker-icon-B.png', anchor:[12,34]}),
				position: this.routingService_.getDestination(),
				label : new cercalia.Label({text : text ? text : cercalia.i18n._('Destination'),offsetY:38}),
				draggable: true,
		        onDragEnd: function(marker, newPosition, event) {
		        	self.dragEndCallback_("d", marker, newPosition, event);
		    	}
			});
			    
			arrMarkers.push(this.routingService_.markerDestination);
		}
	}
	    
	this.routingService_.markerSteps = [ ];
	this.routingService_.markerStepsDrag = [ ];
	var steps = this.routingService_.getSteps();
	var stepNumber = 0;
	var stepDragNumber = 0;
	for(var index = 0 ; index < steps.length; index++) {
	    	
		//si no tenim guardat el step a la llista de punts de pass (no stops) el pintem
		if(  !steps[index].isDragPoint && this.wayPointsDrag_.indexOf(steps[index]) < 0){
	    	if(steps[index] && steps[index] instanceof cercalia.LonLat) {
	    		var text = steps[index].label;
		        var markerIdAct = this.MARKER_ID_STEPS_ + "_" + index;
				var markerAct = this.map_.getMarkerById(markerIdAct);

				if(!markerAct){
		    		var marker = new cercalia.Marker({
		    			id: markerIdAct,
		    			icon: new cercalia.Icon({src:cercaliaGlobals.img + '/cercalia-marker-icon-' + (parseInt(stepNumber)+1) + '.png', anchor:[12,34]}),
		    			position: steps[index],
		    			label : new cercalia.Label({text: text ? text : cercalia.i18n._('Step "%"', (parseInt(stepNumber)+1)),offsetY:38}),
						draggable: true,
						onDragEnd: function(marker, newPosition, event) {
				        	self.dragEndCallback_("s", marker, newPosition, event, marker.stepIndex);
				      	},
			            onRightClick: function(marker, evt){
			            	self.onRightClickStepPoint_(marker, evt);
			            }
		    		});
				        
		    	  	marker.stepIndex = stepNumber;
		    	  	arrMarkers.push(marker); 
		    	  	
				 	this.routingService_.markerSteps.push(marker);
				 }
	    	}
	    	stepNumber++;
	    }else{
	    	//actualitzem el punt
	    	if(steps[index] && steps[index] instanceof cercalia.LonLat) {
	    		if(data.cercalia.route.stoplist 
	    				&& data.cercalia.route.stoplist.stop 
	    				&& data.cercalia.route.stoplist.stop.length > 2){
	    			
	    			var lon = data.cercalia.route.stoplist.stop[parseInt(index) + 1].mo.coord.x;
	    			var lat = data.cercalia.route.stoplist.stop[parseInt(index) + 1].mo.coord.y;
	    			steps[index].setLon(lon);
	    			steps[index].setLat(lat);
	    		}
	    		
		        var markerIdAct = this.MARKER_ID_STEPS_ + "_" + index;
				var markerAct = this.map_.getMarkerById(markerIdAct);
				if( ! markerAct ) {
		    		var text = steps[index].label;
		    		var marker = new cercalia.Marker({
		    			id: markerIdAct,
			            icon: new cercalia.Icon({src : cercaliaGlobals.img + '/point.png'}),
		    			position: steps[index],
						draggable: true,
						onDragEnd: function(marker, newPosition, event) {
				        	self.dragEndCallback_("sd", marker, newPosition, event, marker.stepIndex);
				      	},
			            onRightClick: function(marker, evt){
			            	self.onRightClickDragPoint_(marker, evt);
			            }
		    		});
				        
		    	  	marker.stepIndex = [index,stepDragNumber];
				 	this.routingService_.markerStepsDrag.push(marker);
				 }
	    	}
	    	stepDragNumber++;
	    	
	    }
	}
	    
	//concatenem els markers
	if(this.routingService_.markerSteps){
		this.map_.addMarkers(this.routingService_.markerSteps);
		//arrMarkers = arrMarkers.concat(this.routingService_.markerSteps);
	}
	if(this.routingService_.markerStepsDrag){
		this.map_.addMarkers(this.routingService_.markerStepsDrag);
		//arrMarkers = arrMarkers.concat(this.routingService_.markerStepsDrag);
	}
	
	if(arrMarkers){
		this.map_.addMarkers(arrMarkers);
	}
	    
	//centrem en la extensio dels markers
	if(this.centerToRoute_) {
		setTimeout(function(){
			self.map_.centerToMarkers(arrMarkers);
		}, 50);
	}
    
    if(this.widgetRouting_) {
		this.widgetRouting_.createReport(data);
	}
	
    if(this.widgetRoutingReport_){
    	this.widgetRoutingReport_.clean();
    	this.widgetRoutingReport_.createReport(data);
    }
    
};

/**
 * Shows "Loading overlay"
 * @private
 */
cercalia.RouteMap.prototype.showOverLayLoading_ = function(){
	this.map_.showLoading();
};

/**
 * Hide "Loading overlay"
 * @private 
 */
cercalia.RouteMap.prototype.hideOverLayLoading_ = function(){
	this.map_.hideLoading();
};

/**
 * @return {boolean} centerToRoute
 */
cercalia.RouteMap.prototype.getCenterToRoute = function(){
	return this.centerToRoute_;
};

/**
 * Center to the router
 * @param {boolean} centerToRoute
 */
cercalia.RouteMap.prototype.setCenterToRoute = function(centerToRoute){
	this.centerToRoute_ = centerToRoute;
	return this;
};

/**
 * Check if mouse is in Route Feature
 * @private
 * @param {Event} e
 */
cercalia.RouteMap.prototype.onMouseMove_ = function(e) {
	var self = this;
	var pixel = self.map_.getMap().getEventPixel(e.originalEvent);
	var hit = self.map_.getMap().forEachFeatureAtPixel(pixel, function(feature, layer) {

		if( layer && layer.getProperties()  && cercalia.LayerEnumeration.FeatureLayer == layer.getProperties().name ) {
			
			//Mirem si hi ha una feature de la Ruta.
			if(self.routingService_ && self.features_) {
				var isFeatureDraw = false;
				
				if(self.features_ instanceof Array){
					isFeatureDraw = cercalia.Util.positionOfFeature(self.features_ , feature) >= 0;
				} else {
					isFeatureDraw = self.features_.getFeature() == feature;
				}
		        		
				if( isFeatureDraw ){
					self.featureRouteDrag_ = feature;
					self.markerRouteDrag_.setVisible( true );
					self.markerRouteDrag_.showLabel(); //tmb mostrem el Label
					self.markerRouteDrag_.setPosition( self.getEventCoordinate_( e ) );
				} else {
					if( ! self.markerRouteDrag_.isDragging() ) {
						self.markerRouteDrag_.setVisible(false);
						self.markerRouteDrag_.hideLabel();
					}
				}
			} else {
				if( ! self.markerRouteDrag_.isDragging() ) {
					self.markerRouteDrag_.setVisible(false);
					self.markerRouteDrag_.hideLabel();
				}
			}
		}
	        
		return true;
	},
	undefined,
	function(layer) { 
		return [cercalia.LayerEnumeration.FeatureLayer].indexOf(layer.get('name')) !== -1; //Filtramos solo para los layers que nos interese
	});

	if (!hit && !self.markerRouteDrag_.isDragging() && self.markerRouteDrag_.isVisible()) {
		self.markerRouteDrag_.setVisible(false);
		self.markerRouteDrag_.hideLabel();
	}
};


/**
 * @private
 * @param {event} evt
 * @return {cercalia.LonLat} return LonLat of Event
 */
cercalia.RouteMap.prototype.getEventCoordinate_ = function(evt){
	var coord = this.map_.getMap().getEventCoordinate(evt.originalEvent);
	var coordGeo = ol.proj.transform(coord, this.map_.getProjectionCode(), 'EPSG:4326');
	return new cercalia.LonLat(coordGeo[0], coordGeo[1]);
};

/**
 * @private
 * @param {Array.<cercalia.LonLat>|Array.<cercaliax.service.RoutingStep>} steps
 * @param {cercalia.LonLat} coordDrag
 * @return {Array.<cercalia.LonLat>|Array.<cercaliax.service.RoutingStep>}
 */
cercalia.RouteMap.prototype.reorderSteps_ = function(steps, coordDrag){
	var position = cercalia.Util.positionOfFeature(this.features_, this.featureRouteDrag_);
	return cercalia.Util.addObjectInArray(steps, coordDrag, position);
};

/**
 * Remove route
 * @private
 */
cercalia.RouteMap.prototype.removeRoute_ = function () {
	if(this.routingService_){
		if(this.features_){
			this.map_.removeFeatures(this.features_);
		}
		
		if(this.routingService_.markerOrigin){
			this.map_.removeMarkers(this.routingService_.markerOrigin);
		}
		
		if(this.routingService_.markerDestination){
			this.map_.removeMarkers(this.routingService_.markerDestination);
		}
			
		if(this.routingService_.markerSteps && this.routingService_.markerSteps.length>0){
			this.map_.removeMarkers(this.routingService_.markerSteps);
		}
		
		//eliminem els markers drag existents
		this.removeDragStepsFromService_();
		this.removeDragMarkers_();
	}
	
	this.routingService_ = new cercalia.service.Routing(this.serviceOptions_); // NO CAL CREAR-NE UNA ALTRA!
	this.removeRemoveOptionInContextMenu_();
	
	if(this.widgetRouting_) {
		this.widgetRouting_.clean();
	}
};

/**
 * Clear route (Remove route feature and route markers).
 * 
 */
cercalia.RouteMap.prototype.removeRoute = function() {
	this.removeRoute_();
	return this; // for concat calls
};

/**
 * Put 'Remove route' in contextMenu
 * @private
 */
cercalia.RouteMap.prototype.setRemoveOptionInContextMenu_ = function() {
    if(this.map_.getContextMenuOverlay() && !this.map_.getContextMenuOverlay().existOption()) {
    	var self = this;
    	this.map_.contextMenuOverlay_.addOption('cleanRoute', 'Clean the route', function () { self.removeRoute_(); }, 4);
    }
    
    return this;
};

/**
 * Remove 'Remove route' in contextMenu
 * @private
 */
cercalia.RouteMap.prototype.removeRemoveOptionInContextMenu_ = function() {
	if(this.map_.getContextMenuOverlay()){
		this.map_.getContextMenuOverlay().removeOption('cleanRoute');
	}
};

/**
 * Syncronize routeMap with widget. Widget fills RouteMap.
 * @param {cercalia.widget.Routing} widget
 * @return {cercalia.RouteMap}
 */
cercalia.RouteMap.prototype.sincronize = function(widget) {
	
	var stops = this.widgetRouting_.getStops();

	this.removeRoute_();
	if(!this.widgetRouting_){
		this.setWidget(widget);
	}
	
	//set Origen
	var org = stops[0];
	if(org && org.coord){
		var origin =  new cercalia.LonLat(org.coord.x, org.coord.y);
		this.setOrigin(origin, false, org.label);
	}
	
	//set steps
	for(var i = 1 ; i < stops.length -1 ; i++){
		if(stops[i] && stops[i].coord){
			var step = new cercalia.LonLat(stops[i].coord.x, stops[i].coord.y);
			this.setStep(step, false, stops[i].label, i - 1);
		}
	}
	
	//set Destination
	var dest = stops[stops.length -1];
	if(dest && dest.coord){
		var destination = new cercalia.LonLat(dest.coord.x, dest.coord.y);
		this.setDestination(destination,false, dest.label);
	}

	if(widget && widget.getOptionsRoute){
		//If realtime, active TrafficAffectations
		var optionsRoute = widget.getOptionsRoute();
		var trafficControl = this.map_.getControlsByName(cercalia.MapControls.Traffic);
		if(trafficControl) {
			if(optionsRoute.weight == 'realtime') {
				if( trafficControl.isTrafficOptionEnabled(cercalia.MapControls.TrafficOptions.AFFECTATIONS) ){
					trafficControl.enable(cercalia.MapControls.TrafficOptions.AFFECTATIONS);
				}
			}
		}
	}
	
	return this; // for concat calls
};

/**
 * @private
 * @return {cercalia.RouteMap}
 */
cercalia.RouteMap.prototype.removeDragMarkers_ = function() {
	//eliminem els markers drag existents
	if(this.routingService_.markerStepsDrag && this.routingService_.markerStepsDrag.length > 0){
		this.map_.removeMarkers(this.routingService_.markerStepsDrag);
		this.routingService_.markerStepsDrag = [ ];
	}else if( this.routingService_.markerStepsDrag === undefined ||  this.routingService_.markerStepsDrag == null){
		this.routingService_.markerStepsDrag = [ ];
	}
	return this;
};

/**
 * @private
 * @return {cercalia.RouteMap}
 */
cercalia.RouteMap.prototype.removeDragStepsFromService_ = function() {
	var oldSteps = this.routingService_.getSteps();
	var steps = [ ];
	
	/** RESET DELS DRAG POINTS **/
	//remove dragSteps
	for (index = 0 ; index < oldSteps.length; index++){
		if(oldSteps[index].isDragPoint === undefined || oldSteps[index].isDragPoint == null  || !oldSteps[index].isDragPoint){
			steps.push(oldSteps[index]);
		}
	}
	this.wayPointsDrag_ = [ ];
	this.routingService_.setSteps(steps);
	return this;
};

/**
 * @private
 * @return {cercalia.RouteMap}
 */
cercalia.RouteMap.prototype.onRightClickDragPoint_ = function(marker, evt){
	var self = this;
	this.hideContextMenu();
	
	//Obrir Context menu amb opció de eliminar el marker
	var contextMenu = new cercalia.ContextMenu({id: "contextmenu_" + marker.getId(), map: this.map_});
	
	contextMenu.addOption('removePoint', 'Remove point', function () { 
		var steps = self.routingService_.getSteps();
		
		var index = marker.stepIndex[0];
		var indexDragArray = marker.stepIndex[1];
		var lonlat = steps[index];
			
		steps.splice(index, 1);
		 
		// tornem a posar els steps re-ordenats
        self.routingService_.setSteps(steps);
        
        if(self.widgetRouting_){
			self.widgetRouting_.removeStepDrag(lonlat, indexDragArray);
		}
        
        self.map_.removeMarkers([marker])
        self.calculateRoute();
        
	});
	contextMenu.addOption('cleanRoute', 'Clean the route', function () { self.removeRoute_(); }, 1);

	this.contextMenu_ = contextMenu;
	contextMenu.open(marker.getPosition(), marker.getPosition().getProjectionCode());
	return this;
};

/**
 * @private
 * @return {cercalia.RouteMap}
 */
cercalia.RouteMap.prototype.onRightClickStepPoint_ = function(marker, evt){
	var self = this;
	this.hideContextMenu();
	//Obrir Context menu amb opció de eliminar el marker
	var contextMenu = new cercalia.ContextMenu({id: "contextmenu_" + marker.getId(), map: this.map_});
	
	contextMenu.addOption('removePoint', 'Remove point', function () { 
		
		//eliminem els markers drag existents
		self.removeDragStepsFromService_();
		self.removeDragMarkers_();
		
		var steps = self.routingService_.getSteps();
		
		var index = marker.stepIndex;
		var lonlat = steps[index];
			
		steps.splice(index, 1);
		 
		// tornem a posar els steps re-ordenats
        self.routingService_.setSteps(steps);
        
        if(self.widgetRouting_){
        	self.widgetRouting_.removeStep(index + 1);
		}
        
        self.map_.removeMarkers([marker])
        self.calculateRoute();
        
	});
	contextMenu.addOption('cleanRoute', 'Clean the route', function () { self.removeRoute_(); }, 1);

	
	contextMenu.open(marker.getPosition(), marker.getPosition().getProjectionCode());
	this.contextMenu_ = contextMenu;
	return this;
};

/**
 * Hide routeMap contextMenu
 * @return {cercalia.RouteMap}
 */
cercalia.RouteMap.prototype.hideContextMenu = function(){
	if(this.contextMenu_){
		this.contextMenu_.close();
	}
};


/**
 * @public
 * @return {cercaliax.service.RoutingOptions}
 */
cercalia.RouteMap.prototype.getServiceOptions = function(){
	return this.serviceOptions_;
};

/**
 * @public
 * @param {cercaliax.service.RoutingOptions} opt
 */
cercalia.RouteMap.prototype.setServiceOptions = function(opt){
	var service;
	this.serviceOptions_ = opt;
	
	if(this.widgetRouting_ && this.widgetRouting_.isLogistics()){
		service =  new cercalia.service.LogisticsRouting(this.serviceOptions_);
	} else {
		service = new cercalia.service.Routing(this.serviceOptions_);	
	}
	
	if(this.routingService_.markerOrigin) service.markerOrigin = this.routingService_.markerOrigin;
	if(this.routingService_.getOrigin()) service.setOrigin(this.routingService_.getOrigin());
	
	if(this.routingService_.markerDestination) service.markerDestination = this.routingService_.markerDestination;
	if(this.routingService_.getDestination()) service.setDestination(this.routingService_.getDestination());
	
	if(this.routingService_.markerSteps) service.markerSteps = this.routingService_.markerSteps;
	if(this.routingService_.getSteps()) service.setSteps(this.routingService_.getSteps());
	
	this.routingService_ = service;
	
	return this;
};


/**
 * @return {cercalia.service.Routing}
 */
cercalia.RouteMap.prototype.getService = function(){
	return this.routingService_;
};

/**
 * @return {Array.<Feature>}
 */
cercalia.RouteMap.prototype.getFeatures = function() {
	return this.features_;
};