Source: temp/jsdocinputdirs/cercalia.util.js

/**
 * @classdesc
 * @static
 * 
 * Class with useful functions
 * @constructor
 */
cercalia.Util = function(){};

/**
 * Convierte color y opacidad en formato rgba
 * @static
 * @param {string} hexcolor Color in hexadecimal. P.e: `#ff0001` 
 * @param {number} opacity Opacity. Decimal value `0` to `1`.
 * @return {string} color p.e: `rgba(0, 0, 255, 0.1)`
 */
cercalia.Util.transformColor2rgba = function(hexcolor,opacity){
    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hexcolor);
    return result?'rgba('+parseInt(result[1], 16)+','+parseInt(result[2], 16)+','+parseInt(result[3], 16)+','+opacity+')':null;
};

cercalia.Util.getHexFromRgb = function(rgb) {
	if(rgb && Array.isArray(rgb)){
		return (rgb && rgb.length >= 3) ? "#" +
				 ("0" + parseInt(rgb[0],10).toString(16)).slice(-2) +
				 ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) +
				 ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) : '';
	}else if(rgb){
		rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
		return (rgb && rgb.length === 4) ? "#" +
		 ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) +
		 ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) +
		 ("0" + parseInt(rgb[3],10).toString(16)).slice(-2) : '';
	}else return '';
}

/**
 * Get opacity from RGB froamt
 * @static
 * @return {number} opacity 
 */
cercalia.Util.getOpacityFromRgb = function(rgb) {
	if(rgb && Array.isArray(rgb) && rgb.length > 3){
		return rgb[3];
	}else if(rgb){
		rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?.[\s+]?(\d+)[\s+]?/i);
		return (rgb && rgb.length > 4) ? rgb[4]+ "." + rgb[5]  : (rgb && rgb.length > 3) ? rgb[4] : '1';
	}else return 1;
};

/**
 * @static
 * @param {ol.geom.Geometry} geometry Object geometry `ol.Geometry`.
 * @param {string} projectionCode Projection code
 * @return {string}
 */
cercalia.Util.geometryToWKT = function(geometry, projectionCode) {
	var format = new ol.format.WKT();
	var geom = geometry.clone();
	geom.transform(projectionCode, 'EPSG:4326');
	return format.writeGeometry(geom);
};

/**
 * @static
 * @param {string} wkt WKT http://es.wikipedia.org/wiki/Well_Known_Text
 * @return {ol.geom.Geometry} Geometry OL3 <a href="http://openlayers.org/en/v3.0.0/apidoc/ol.geom.Geometry.html">`ol.geom.Geometry`</a>
 */
cercalia.Util.wktToGeometry = function(wkt) {
	var format = new ol.format.WKT();
	var feature = format.readFeature(wkt);
	return feature.getGeometry();
};

/**
 * Transform WKT string to another projection.
 * @static
 * @param {string} wkt WKT http://es.wikipedia.org/wiki/Well_Known_Text
 * @param {string} projectionSrc Projection WKT origin
 * @param {string} projectionDst Projection WKT destination
 */
cercalia.Util.transformWKT = function(wkt, projectionSrc, projectionDst){
	var format = new ol.format.WKT();
	var geometry = format.readGeometry(wkt);
	geometry.transform(projectionSrc, projectionDst);
	return format.writeGeometry(geometry);
};

/**
 * @static
 * @param {number} meters
 * @return {number} kilometers
 */
cercalia.Util.metersToKm = function(meters){
	return (Math.round(meters / 1000 * 100) / 100);
};

/**
 *  
 * Returns `true` if the coordinate is inside polygon. Polygon must be in WKT format.
 * 
 * @static
 * @param {string} polygon WKT String de un poligono
 * @param {cercalia.LonLat} lonLat Punto
 * @return {boolean} 
 */
cercalia.Util.geofencing = function(polygon, lonLat){
	if(polygon.indexOf('POLYGON')>=0){		
		var geometry = cercalia.Util.wktToGeometry(polygon);
		var coord = [lonLat.getLon(), lonLat.getLat()];
		var polygon = geometry.getCoordinates()[0];
		return this.isPointInPoly_(coord, polygon);
	} else {
		console.warn("No es un string WKT de un poligono!");
		return false;
	}
};

/**
 * @private
 * @static
 * @param {Array.<number>} point
 * @param {Array.<Array.<number>>} vs
 */
cercalia.Util.isPointInPoly_ = function (point, vs){	
	var x = point[0];
	var y = point[1];
	    
    var inside = false;
    for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) {
        var xi = vs[i][0], yi = vs[i][1];
        var xj = vs[j][0], yj = vs[j][1];
        
        var intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
        if (intersect) {
        	inside = !inside;
        }
    }
    
    return inside;
};

/**
 * Obtiene la distancia en metro de una {@link cercalia.LonLat} respecto el punto más cercano de {@link cercalia.Feature}. 
 * Puede ser una feature del tipo `Point`, `LineString`, `Polygon`. 
 * @static
 * @param {cercalia.LonLat} position Coordenada de referencia
 * @param {cercalia.Feature} feature Feature. No hace falta que esté pintada en el mapa para hacer este cálculo.
 * @return {number} Distancia en metros
 */
cercalia.Util.getDistanceFromFeature = function(position,feature) {
	var geometry = feature.getFeature().getGeometry();
	var point = position.transform('EPSG:3857');
	var closestPoint = geometry.getClosestPoint(point);
	
	return new ol.geom.LineString([point, closestPoint]).getLength();
};


/**
 * Función para obtener la coordenada más cercana de {@link cercalia.Feature} respecto {@link cercalia.LonLat}.
 * Puede ser una feature del tipo `Point`, `LineString`, `Polygon`.
 * @static 
 * @param {cercalia.LonLat} position
 * @param {cercalia.Feature} feature
 * @return {cercalia.LonLat} Coordenada de la feature más cercana.
 */
cercalia.Util.getClosestLonLatOfFeature = function(position,feature) {
	var geometry = feature.getFeature().getGeometry();
	var point = position.transform('EPSG:3857');
	var closestPoint = geometry.getClosestPoint(point);
	var closestPointGeo = ol.proj.transform(closestPoint,'EPSG:3857','EPSG:4326');
	
	return new cercalia.LonLat(closestPointGeo[0],closestPointGeo[1]);
};


/**
 * @param {cercalia.LonLat} coordA
 * @param {cercalia.LonLat} coordB
 * @return distance between two cordenates
 */
cercalia.Util.distanceTo = function (coordA, coordB) {

	var pointA = coordA;
	var pointB = coordB;
	
	if(coordA instanceof cercalia.LonLat && coordB instanceof cercalia.LonLat){		
		pointA = [coordA.getLon(), coordA.getLat()];
		pointB = [coordB.getLon(), coordB.getLat()];
	}
	
    var R = 6371;
    var o1 = cercalia.Util.toRadians_(pointA[1]), fi1 = cercalia.Util.toRadians_(pointA[0]);
    var o2 = cercalia.Util.toRadians_(pointB[1]), fi2 = cercalia.Util.toRadians_(pointB[0]);
    var delta_o = o2 - o1;
    var delta_fi = fi2 - fi1;

    var a = Math.sin(delta_o / 2) * Math.sin(delta_o / 2) + Math.cos(o1) * Math.cos(o2) * Math.sin(delta_fi / 2) * Math.sin(delta_fi / 2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    var d = R * c;

    return d*1000;
};


/**
 * @private
 * @return angle to radiants
 */
cercalia.Util.toRadians_ = function (angle) {
    return angle*Math.PI / 180;
};


/**
 * @private
 * @return radiants to angle
 */
cercalia.Util.toAngle_ = function(rad) {
	var angle = rad*180 / Math.PI;
    if (angle < 0) {
        return 360 - angle;
    } else if ( angle >= 360 ) {
    	return angle - 360;
    } else {
    	return angle;
    }
};


/**
 * 
 * @param {Array.<cercalia.Feature|ol.Feature>} arrayFeatures Array of {cercalia.Feature|ol.Feature}
 * @param {cercalia.Feature|ol.Feature} feature Feature to compare
 * @return {Number} Feature position in array, if array not contains feature return -1
 */
cercalia.Util.positionOfFeature = function( arrayFeatures, feature ) {
	if( arrayFeatures ) {
		var featureOL = feature instanceof cercalia.Feature ? feature.getFeature() : feature;
		
		for(var i = 0 ; i < arrayFeatures.length ; i ++){
			var featureOLcompare = arrayFeatures[i] instanceof cercalia.Feature ? arrayFeatures[i].getFeature() : arrayFeatures[i];			
			if (featureOLcompare == featureOL){
				return i; 
			}
		}
	}
	return -1;
}


/**
 * add object in array to especific position, and move other objects
 * @param {Array.<Object>} array Array of {Object}
 * @param {Object} obj Object
 * @return {Array.<Object>} array result
 */
cercalia.Util.addObjectInArray = function( array, obj, position ) {

	var arrayResult = [ ];
	var p = 0;
	
	if(array !== undefined && array 
			&& position !== undefined && position != null
			&& position > array.length){
		array[position] = obj;
		return array;
	}
	
	//posem els que estan per deban.
	if(array !== undefined && array){
		if(position === undefined || position == null ){
			position = array.length ;
		}
		
		for( var i = 0 ; i < position && i < array.length ; i ++){
			if(array[ i ]) arrayResult.push( array[ i ] );
			p ++;
		}
	}
	
	//posem l'objecte a la posisió que toca.
	arrayResult.push( obj );
	
	// posem la resta a la cua.
	if(array !== undefined && array){
		for( var i = p ; i < array.length ; i ++){
			if(array[ i ]) arrayResult.push( array[ i ] );
		}
	}
	
	return arrayResult;
}

/**
 * 
 * @param wktGeomCollection {Array.<string>} wkt og geomCollection
 * @return {Array.<string>} array of wkts in geomcollection
 */
cercalia.Util.splitWktGeomCollection = function(wktGeomCollection){
	
	if(wktGeomCollection && wktGeomCollection.indexOf('GEOMETRYCOLLECTION') > -1){
		var array = [];
		var s = wktGeomCollection.substring(wktGeomCollection.indexOf('(')+1, wktGeomCollection.lastIndexOf(")"));
		var it = 0;
		while(s.length > 0 && it < 100) {
			var wkt = s.substring(0, s.indexOf(")")+1);
			s = s.substring(wkt.length, s.length);
			
			//triem la coma si al te
			if(wkt.substring(0,1).indexOf(',') > -1){
				wkt = wkt.substring(wkt.indexOf(',')+1,wkt.length);
			}

			array.push(wkt);			
			it++;
		}
		return array;
		
	} else {
		console.warn("not geometrycollection");
	}
	
	return null;
};


/**
 * 
 * @param wkt {string} WKT geometry.
 * @param srs {string} coords system, default: 'EPSG:4326'
 * @return {string} KML of wkt
 */
cercalia.Util.transformWktToKML = function(wkt, srs){
	var result;
	srs = srs ? srs : 'EPSG:4326';
	var feature = new ol.format.WKT().readFeature(wkt);
	
	if(srs != 'EPSG:4326') {
		feature.getGeometry().transform(srs, 'EPSG:4326');//Per defecte EPSG:4326
	}
	
	result = new ol.format.KML().writeFeatures([feature]);

	return result;
}

/**
 * @return true if browser is IE8 or IE9.
 */
cercalia.Util.isIEUnderTen = function(){
	if(navigator.appName.indexOf("Internet Explorer")!=-1){
	    return (navigator.appVersion.indexOf("MSIE 9") ==-1 
	        		|| navigator.appVersion.indexOf("MSIE 8") ==-1);
	}
	
	return false;
};

/**
 * @param {number} distance
 * @return {string} distanceImp Distance imperial format string.
 */
cercalia.Util.distanceMetricToImperial = function(distance) {
	var distanceImp;
	if(distance > 1609.344){
		distanceImp = (distance / 1609.344).toFixed(2) + " mi";
	} else if (distance > 3.28) {
		distanceImp = (distance * 3.28).toFixed(2) + " ft";
	} else {		
		distanceImp = (distance * 39.37).toFixed(2) + " in";
	}
	return distanceImp;
};

/**
 * @param {number} area Area in ha
 * @return {number} areaImp Area in acrees.
 */
cercalia.Util.areaMetricToImperial = function(area) {
	var areaImp = (area * 2.4711).toFixed(2) + " ac";
	return areaImp;
};


/**
 * @param {string} strXML XML in string format
 * @param {string} filename
 */
cercalia.Util.downloadXmlToFile = function(strXML, filename) {
	var pom = document.createElement('a');
	var bb = new Blob([strXML], {type: 'text/xml'});

	pom.setAttribute('href', window.URL.createObjectURL(bb));
	pom.setAttribute('download', filename);

	pom.dataset.downloadurl = ['text/xml', pom.download, pom.href].join(':');
	pom.draggable = true; 
	pom.classList.add('dragout');

	document.body.appendChild(pom);
	pom.click();
	document.body.removeChild(pom);
};

/**
 * Create geometry circle giving a center and radius. If optional parameter`opt_isPolygon`
 * is `true` it returns a circular polygon. Default opt_isPolygon value is `false`.
 * @param {cercalia.LonLat} center Center.
 * @param {number} radius Radius in meters.
 * @param {boolean=} opt_isPolygon If `true`, it creates a circular polygon `ol.geom.Polygon`,
 *    else creates a Circle `ol.geom.Circle`. Default `false`.
 * @return {ol.geom.Polygon|ol.geom.Circle} Obtains Circle geometry or Circular polygon geometry.
 */
cercalia.Util.createCircleGeometry = function(center, radius, opt_isPolygon) {
  var isPolygon = opt_isPolygon !== undefined ? opt_isPolygon : false;
  var geometry;
  var coordinates = center.transform('EPSG:3857');
  geometry = new ol.geom.Circle(coordinates, radius);

  if (isPolygon) {
    return ol.geom.Polygon.fromCircle(geometry);
  } else {
    return geometry;
  }
};

/**
 * Obtains circle radius and circle center. {center: `cercalia.LonLat`, radius: `number`}
 * @param {cercalia.Feature} feature Feature.
 * @return {cercalia.CircleData} data Circle data
 */
cercalia.Util.getCircleData = function(feature) {
  var featureOl = feature.getFeature();
  var geometry = /** @type {ol.geom.Circle} */(featureOl.getGeometry());
  var geometryType = geometry.getType();
  if (geometryType === 'Circle') {
	  var map = feature.getMap();
	  var coordinates = geometry.getCenter();
	  var bounds = feature.getBounds();
	  var radius = bounds.getWidthInMeters() / 2;
	  var center = new cercalia.LonLat(coordinates[0], coordinates[1], map.getProjectionCode());
	  var data = {
	    center: center,
	    radius: radius
	  };
	  return data;
	} else {
		alert('Function only for geometry type \'Circle\'')
		return null;
	}
};