var parseTileLayerName = function(layername){
    var posDot = layername.lastIndexOf(".");
    var fileExtension = "";
    var fileBaseName = layername;
    if(posDot > -1){
        fileExtension = layername.substring(posDot, layername.length);
        fileBaseName = layername.substring(0, posDot);
    }
    var nameParts = fileBaseName.split("_");
    return {
        fileNameAppendix : nameParts[0],
        quadcode : nameParts[1],
        fullRots : nameParts[2],
        fileExtension : fileExtension
    };
}

function cloneObj(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var getLowerZoomlevelTiles = function(tiles){
    var lowerZLtiles = [];
    tiles.forEach(function(tile){
        var quadcode = tile.quadcode;
        if(quadcode.length > 1){
            var lowerZLtile = cloneObj(tile)
            lowerZLtile.quadcode = quadcode.substr(0,quadcode.length - 1)
            if(!lowerZLtiles.containsClone(lowerZLtile)){
                lowerZLtiles.push(lowerZLtile);
            }
        }
        
    })
    return lowerZLtiles
}

var getTilesInAThatAreNotInB = function(a,b){
    if(a instanceof Array && b instanceof Array){
        var tilesInAThatAreNotInB = a.filter(function(tileFromA){
            var isSameTiles = b.filter(function(tileFromB){
                //if(!b.hasOwnProperty(tileFromB)) return false
                return (tileFromA.quadcode && (tileFromA.quadcode == tileFromB.quadcode && tileFromA.fullRots == tileFromB.fullRots))
            })
            //if(!a.hasOwnProperty(tileFromA)) return false
            if(isSameTiles.length === 0){
                return true;
            }else{
                return false;
            }
        })
        return tilesInAThatAreNotInB;    
    }else{
        return a
    }
}

var getTileLayerName = function(fileNameAppendix, quadcode, fullRot, fileExtension){
    if(fileNameAppendix instanceof Object && !(fileNameAppendix instanceof String)){
        var tileObj = fileNameAppendix
        fileNameAppendix = tileObj.fileNameAppendix;
        quadcode = tileObj.quadcode;
        fullRot = tileObj.fullRots;
        fileExtension = tileObj.fileExtension;
    }
    if(!fullRot){fullRot = 0;}
    
    if(fileExtension && fileExtension.indexOf(".") === -1){fileExtension = "." + fileExtension};
    if(!fileExtension){fileExtension = "";}
    return fileNameAppendix + "_" + quadcode + "_" + String(fullRot);
}

var getTileFileName = function(fileNameAppendix, quadcode, fileExtension){
    if(fileNameAppendix instanceof Object && !(fileNameAppendix instanceof String)){
        var tileObj = fileNameAppendix
        quadcode = tileObj.quadcode;
        var fullRots = tileObj.fullRots;
        fileExtension = tileObj.fileExtension;
        fileNameAppendix = tileObj.fileNameAppendix;
    }
    if(fileExtension && fileExtension.indexOf(".") === -1){fileExtension = "." + fileExtension}
    if(!fileExtension){fileExtension = "";}
    return fileNameAppendix + "_" + quadcode + fileExtension;
}

function IntersectRect(a, b) {
    return ! ( b.left > a.right || b.right < a.left || b.top < a.bottom || b.bottom > a.top);
}

var convertBBoxOSMtoGEOLAYERS = function(bbox){
    //top, left, bottom, right
    return [[bbox[1], bbox[2]],[bbox[0], bbox[3]]]
}

var convertBBoxGEOLAYERStoOSM = function(OLUR){
    //bottom, top, left, right
    return [OLUR[1][0], OLUR[0][0],OLUR[0][1], OLUR[1][1]]
}

var convertBBoxGEOLAYERStoGeoJSON = function(OLUR){
    return [OLUR[1][0], OLUR[0][1],OLUR[0][0], OLUR[1][1]]
}

var convertBBoxGEOLAYERStoTLBRObj = function(OLUR){
    return {
        top : OLUR[0][0],
        left : OLUR[0][1],
        bottom : OLUR[1][0],
        right : OLUR[1][1]
    }
}
var convertBBoxGEOLAYERStoObj = function(OLUR){
    return {
        yMax : OLUR[0][0],
        xMin : OLUR[0][1],
        yMin : OLUR[1][0],
        xMax : OLUR[1][1]
    }
}

var convertBBoxOSMtoGeoJSON = function(bbox){
    return convertBBoxGEOLAYERStoGeoJSON(convertBBoxOSMtoGEOLAYERS(bbox));
}

var convertBBoxObjToGeoJSON = function(bboxobj){
    return [bboxobj.yMin, bboxobj.xMin, bboxobj.yMax, bboxobj.xMax];
}

var convertBBoxGeoJSONtoTLBRObj = function(bbox){
    return convertBBoxGEOLAYERStoTLBRObj(convertBBoxGeoJSONtoGEOLAYERS(bbox));
}

var convertBBoxGeoJSONtoGEOLAYERS = function(bbox){
    //bottom, left, top, right
    bbox = normalizeGeoJSONBBox(bbox);
    return [[bbox[2], bbox[1]],[bbox[0], bbox[3]]]
}

var normalizeGeoJSONBBox = function(bbox){
    if(bbox.length > 4){
        bbox = [bbox[0], bbox[1], bbox[3], bbox[4]];
    }
    return bbox;
}


var addbufferOLUR = function(OLUR, relBuffer){
    var OLpixel = tileUtils.LatLongToPixelXY(OLUR[0][0], OLUR[0][1], getGlobalMaxZL(), 256);
    var URpixel = tileUtils.LatLongToPixelXY(OLUR[1][0], OLUR[1][1], getGlobalMaxZL(), 256);
    var dist = Math.abs(Math.sqrt(Math.pow(OLpixel[0]-URpixel[0], 2) + Math.pow(OLpixel[0]-URpixel[0], 2)));
    
    var bufferPixel = dist * relBuffer;
    OLpixel = [OLpixel[0]-bufferPixel, OLpixel[1]-bufferPixel];
    URpixel = [URpixel[0]+bufferPixel, URpixel[1]+bufferPixel];
    
    return [tileUtils.PixelXYToLatLong(OLpixel[0], OLpixel[1], getGlobalMaxZL(), true, 256), tileUtils.PixelXYToLatLong(URpixel[0], URpixel[1], getGlobalMaxZL(), true, 256)]
    
}

var splitAryToPackages = function(ary, size){
    var aryParts = [];
    for(var s=0; s<=Math.floor(ary.length/size);s++){
        var step = s*size
        var step2 = Math.min(step+size, ary.length);
        var part=ary.slice(step,step2);
        if(part.length>0){aryParts.push(part)};
    }
    return aryParts;
}

function rgbToHex(rgb) {
    return (((rgb[0]*256) << 16) + ((rgb[1]*256) << 8) + (rgb[2]*256))
}

var hexToRGB = function(hex) {
    var r = (hex >> 16)/256;
    var g = (hex >> 8 & 0xFF)/256;
    var b = (hex & 0xFF)/256;
    return [r, g, b];
};

function rgbToHexCode(rgb) {
    return "#" + ((1 << 24) + ((rgb[0]*256) << 16) + ((rgb[1]*256) << 8) + (rgb[2]*256)).toString(16).slice(1);
};

var hexToHexCode = function(hex){
    return rgbToHexCode(hexToRGB(hex))
};


var getFileExtensionOfFileName = function(fileName){
    if(fileName){
        return fileName.substring(fileName.lastIndexOf(".")+1,fileName.length)
    }else{
        return ""
    }
}

var getFeaturesAryFromGeoJsonObj = function(geoJsonObj, addDisplayNameToFeature, includeCollectionFilter, includeFeatureFilter, includeFeatureCollections, excludeSelf) {
    if(!includeCollectionFilter){includeCollectionFilter = function(f){return true};}
    if(!includeFeatureFilter){includeFeatureFilter = function(f){return true};}
    if(!includeFeatureCollections){includeFeatureCollections = false;}
	var recursiveGetFeature = function(inputGeoJsonObj, depthIndex) {
		for (var f in inputGeoJsonObj) {
            if(!inputGeoJsonObj[f].properties){inputGeoJsonObj[f].properties = {};}
            inputGeoJsonObj[f].GEOlayersDepthIndex = depthIndex;
            if (addDisplayNameToFeature) {
                if(!inputGeoJsonObj[f].properties.GEOlayersDisplayName){
                    var featureName = getNameOfGeoJSONNode(inputGeoJsonObj[f]);
                    if (!featureName) {
                        featureName = "Feature"
                    }
                    inputGeoJsonObj[f].properties.GEOlayersDisplayName = featureName;
                }
            }
			if (inputGeoJsonObj[f].type && inputGeoJsonObj[f].type.toUpperCase() == "FEATURE" && inputGeoJsonObj[f].geometry) {	
                if(includeFeatureFilter(inputGeoJsonObj[f])){
                    featuresAry.push(inputGeoJsonObj[f])
                }
			}
			if (inputGeoJsonObj[f].type && inputGeoJsonObj[f].type.toUpperCase() == "FEATURECOLLECTION") {
                if(includeFeatureCollections){
                    featuresAry.push(inputGeoJsonObj[f]);
                }
                if(includeCollectionFilter(inputGeoJsonObj[f])){
                    
				    recursiveGetFeature(inputGeoJsonObj[f].features, depthIndex+1);
                }
			}
		}
	}
	if (true) {
        //geoJsonObj.GEOlayersDepthIndex = 0
        if (addDisplayNameToFeature) {
            var featureName = getNameOfGeoJSONNode(geoJsonObj);
            if (!featureName) {
                featureName = "Feature"
            }
            if(!geoJsonObj.properties){geoJsonObj.properties = {};}
            geoJsonObj.properties.GEOlayersDisplayName = featureName;
            
        }
		var featuresAry = [];
		if (geoJsonObj.type && geoJsonObj.type.toUpperCase() == "FEATURE" && geoJsonObj.geometry) {
            if(includeFeatureFilter(geoJsonObj)){
                return [geoJsonObj];
            }else{
                return [];
            }
		}
        if(geoJsonObj.type.toUpperCase() == "FEATURECOLLECTION"){
            
            if(includeFeatureCollections && !excludeSelf){
                featuresAry.push(geoJsonObj);
            }
            if(includeCollectionFilter(geoJsonObj)){
                
                recursiveGetFeature(geoJsonObj.features, getFeatureDepthIndex(geoJsonObj)+1);
            }
            
            return featuresAry;
        }
	} else {
		return []; //input is no object
	}
}

var getFeatureDepthIndex = function(feature){
    var depthIndex = null;
    if(feature){
        depthIndex = feature.GEOlayersDepthIndex;
    }    
    if(depthIndex){
        return depthIndex
    }else{
        return 0
    }
}

function replaceAll(str, find, replace) {
    if(str && str.replace){
        return str.replace(new RegExp(escapeRegExp(find), 'g'), replace);
    }else{
        return str
    }
}

function escapeRegExp(str) {
    if(str){
        return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
    }else{
        return null
    }
}

var replaceStrBetween = function(str, begin, end, replaceStr){
    var indexOfBegin = str.indexOf(begin)
    if(indexOfBegin > -1){
        var firstpart = str.substring(0,indexOfBegin);
        var restpart = str.substring(indexOfBegin, str.length)
        var indexOfEnd = restpart.indexOf(end);
        if(indexOfEnd > -1){
            var lastpart = restpart.substring(indexOfEnd,str.length);
            var replaced = firstpart + begin + replaceStr + lastpart;
            return replaced
        }
    }
    return str
}

var getStrBetween = function(str, begin, end){
    var indexOfBegin = str.indexOf(begin)
    if(indexOfBegin > -1){
        var restpart = str.substring(indexOfBegin + begin.length, str.length)
        var indexOfEnd = restpart.indexOf(end);
        if(indexOfEnd > -1){
            var between = restpart.substring(0,indexOfEnd);
            return between
        }
    }
    return null
}

var getNameOfGeoJSONNode = function(curNodeToSearch, nameKeyCatchwords) {
    if(curNodeToSearch.properties.GEOlayersDisplayName){
        return curNodeToSearch.properties.GEOlayersDisplayName;
    }
	if (!(nameKeyCatchwords instanceof Array)) {
        if(preferences){
            nameKeyCatchwords = ["name:" + preferences.languageSettings.ISO639].concat(preferences.geoJsonPropsForNaming)
        }else{
            nameKeyCatchwords = ["name:" + gl.preferences.languageSettings.ISO639].concat(gl.preferences.geoJsonPropsForNaming)
        }
		
	}
	var nameFound = false;
    var curNameKeyCatchWord;
	for (var cw = 0; cw < nameKeyCatchwords.length; cw++) {
        curNameKeyCatchWord = nameKeyCatchwords[cw].replace(/^\s+|\s+$/g, '');
        if(curNodeToSearch.properties && curNodeToSearch.properties[curNameKeyCatchWord]){
            return curNodeToSearch.properties[curNameKeyCatchWord];
        }
		for (var n in curNodeToSearch.properties) {
			//if(tempFCname == undefined){tempFCname = curNodeToSearch.properties[n].toString().substr(0,20);}
			if (n.toUpperCase().indexOf(curNameKeyCatchWord.toUpperCase()) > -1 && curNodeToSearch.properties[n] != undefined && typeof curNodeToSearch.properties[n] == "string" && curNodeToSearch.properties[n].length > 0) {
				nameFound = true;
				break
			}
		}
		if (nameFound == true) {
			return curNodeToSearch.properties[n].toString();
			break
		}
	}
	return "Feature";
}

var getUniqueName = function(names, uniqueName, isNotInAry, dontOptimizeForExpressions){
    uniqueName = String(uniqueName);
    if(!dontOptimizeForExpressions){
        uniqueName = replaceAll(uniqueName, ";", "_");
        //uniqueName = unicodeLiteral(uniqueName);
        uniqueName = escapeQuotesAndSlashes(uniqueName);
    }
    var counter = 0
    var curName = uniqueName;
    var uniqueNameWithoutCounter = isNaN(parseInt(uniqueName.substring(uniqueName.lastIndexOf(" "), uniqueName.length))) && uniqueName || uniqueName.substring(0, uniqueName.lastIndexOf(" "));
    var lastCurName
    var nameIsUnique = false
    var uniqueIndex = -1;
    names.forEach(function(name, index){
        if(name == uniqueName){
            uniqueIndex = index;
        }
    })
    while(!nameIsUnique){
        nameIsUnique = true
        lastCurName = curName;
        if(counter>0){curName = uniqueNameWithoutCounter + " " + String(counter)}
        names.forEach(function(name, index){
            if(name == curName && ((index != uniqueIndex) || isNotInAry)){
                nameIsUnique = false;
            }
              
        })
        
        counter++
    }
    return curName;
}

var isTileSizeSupported = function(tileSize){
    var log2 = Math.log(tileSize)/Math.log(2);
    return log2 === Math.floor(log2)
}

var subtractVector = function(v1, v2){
    var val2
    var diffVec = [];
    v1.forEach(function(val, index){
        val2 = v2[index] || 0
        diffVec.push(val-val2);
    })
    return diffVec
}
var addVector = function(v1, v2){
    var val2
    var diffVec = [];
    v1.forEach(function(val, index){
        val2 = v2[index] || 0
        diffVec.push(val+val2);
    })
    return diffVec
}
var multVector = function(v1, v2){
    var diffVec = [];
    v1.forEach(function(val, index){
        diffVec.push(val*v2);
    })
    return diffVec
}

var getDiffFullRot = function(lon1, lon2){
    var diffLon = lon2-lon1
    if(Math.abs(diffLon) > 180){
        if(diffLon<0){
            return 1
        }else{
            return -1
        }
    }
    return 0
}

var getFullRotationAndClippedLon = function(lon){
    lon = parseFloat(lon);
    var stepVal = 360;
    var fullRots = Math.ceil((lon + (stepVal / 2)) / stepVal) - 1;
    var fullRotDegrees = stepVal * (fullRots) - (stepVal / 2);
    var clippedDegrees = lon - fullRotDegrees - (stepVal / 2);
    return {
        fullRots : fullRots,
        clippedDegrees : clippedDegrees
    }
}

var getAbsoluteLonFromFullRotsAndClippedDegree = function(fullRots, clippedDegree){
    //clippedDegree = getFullRotationAndClippedLon(clippedDegree).clippedDegrees;
    clippedDegree = parseFloat(clippedDegree);
    var stepVal = 360;
    return clippedDegree+(stepVal*fullRots);
}

var getItemById = function(items, id){
    for(var i = 0; i < items.length; i++){
        if(items[i].id == id){
            return items[i]
        }
    }
}

var convertPointToPath = function(Gpoint) {
	var pointSize = 0.00001;
    
	return [[Gpoint[0]+pointSize, Gpoint[1]+pointSize], Gpoint]
}


var arrayContains = function(ary, obj) {
    var i = ary.length;
    while (i--) {
        if (ary[i] === obj) {
            return true;
        }
    }
    return false;
}

var transformData = function(dataTransformer, input, dontRandomize){
    dataTransformer = JSON.parse(JSON.stringify(dataTransformer));
    var stop, transformFunction, parsedInput, firstChar, lastChar
    var spliceStringIndecies = [];
    var splicedStrings = [];
    if(input === true){input = "true"}
    if(input === false){input = "false"}
    if(input === undefined){input = "undefined"}
    if(dataTransformer.randomize && !dontRandomize){input = -99999999999999999}
    for(var i=0; i < dataTransformer.stops.length; i++){
        stop = dataTransformer.stops[i];
        if(input == stop.inV){return stop.outV}
        stop.inV = String(stop.inV)
        firstChar = stop.inV.substr(0,1);
        if(firstChar === "'" || firstChar === '"'){
            lastChar = stop.inV.substr(stop.inV.length-1,1);
            if(lastChar === "'" || lastChar === '"'){
                if(input == stop.inV.substr(1,stop.inV.length-2)){return stop.outV}
                spliceStringIndecies.push(i);
            }else{
                stop.inV = 0
            }
        }else{
            if(typeof stop.inV == "string"){stop.inV = stop.inV.replace(",", ".")}
            stop.inV = parseFloat(stop.inV) || 0;
        }
    }
    for(var i=0; i < spliceStringIndecies.length; i++){
        splicedStrings = splicedStrings.concat(dataTransformer.stops.splice(spliceStringIndecies[i]-i, 1));
    }
    if(dataTransformer.stops.length > 0){
        dataTransformer.stops.sort(function(a,b){
            return a.inV - b.inV
        });
        if(typeof input == "string"){input = input.replace(",", ".")}
        parsedInput = parseFloat(input);
        if(parsedInput || parsedInput === 0){
            if(dataTransformer.randomize && !dontRandomize){
                input = dataTransformer.stops[0].inV + Math.random()*(dataTransformer.stops[dataTransformer.stops.length-1].inV-dataTransformer.stops[0].inV)
            }
            switch(dataTransformer.type){
                case "value":
                    transformFunction = transformLinear;
                    break;
                case "color":
                    transformFunction = transformColor;
                    break;
            }
            if(input < dataTransformer.stops[0].inV){
                //belowFirst
                if(dataTransformer.clip){
                    return dataTransformer.stops[0].outV
                }else{
                    return transformFunction(input, dataTransformer.stops[0].inV, dataTransformer.stops[1].inV, dataTransformer.stops[0].outV, dataTransformer.stops[1].outV)
                }
            }
            if(input >= dataTransformer.stops[dataTransformer.stops.length-1].inV){
                //aboveLast
                if(dataTransformer.clip){
                    return dataTransformer.stops[dataTransformer.stops.length-1].outV
                }else{
                    return transformFunction(input, dataTransformer.stops[dataTransformer.stops.length-2].inV, dataTransformer.stops[dataTransformer.stops.length-1].inV, dataTransformer.stops[dataTransformer.stops.length-2].outV, dataTransformer.stops[dataTransformer.stops.length-1].outV)
                }
            }
            for(var i=0; i < dataTransformer.stops.length-1; i++){

                if(input >= dataTransformer.stops[i].inV && input < dataTransformer.stops[i+1].inV){
                    //betweenPair
                    return transformFunction(input, dataTransformer.stops[i].inV, dataTransformer.stops[i+1].inV, dataTransformer.stops[i].outV, dataTransformer.stops[i+1].outV)
                }
            }
        }
    }else{
        if(dataTransformer.randomize && !dontRandomize){
            if(splicedStrings.length > 1){
                return splicedStrings[Math.round(Math.random()*(splicedStrings.length-1))].outV
            }
        }
    }
}

var transformLinear = function(ix, ix1, ix2, iy1, iy2) {
    var m = (iy2 - iy1) / (ix2 - ix1);
    var n = (m * -1) * ix1 + iy1;
    var y = m * ix + n;
    return y
}

var transformColor = function(ix, ix1, ix2, iy1, iy2) {
    var iy1RGB = hexToRGB(iy1);
    var iy2RGB = hexToRGB(iy2);
    var oRGB = [0,0,0];
    for(var c=0; c < 3; c++){
        oRGB[c] = transformLinear(ix, ix1, ix2, iy1RGB[c], iy2RGB[c]);
    }
    return rgbToHex(oRGB);
}

var isDrawingOptionsDataTransforming = function(drawingOptions){
    if(!drawingOptions.dataTransformers){return false}
    for(var dataTransformer in drawingOptions.dataTransformers){
        if(drawingOptions.dataTransformers.hasOwnProperty(dataTransformer)){
            if(drawingOptions.dataTransformers[dataTransformer].active === true){return true}
        }
    }
    return false
}

var transformDataDrivenDrawingOptionsIfTransformersActive = function(feature, drawingOptions, dontRandomize){
    if(!drawingOptions || !isDrawingOptionsDataTransforming(drawingOptions)){return drawingOptions}
    return transformDataDrivenDrawingOptions (feature, drawingOptions, dontRandomize)
}

var transformDataDrivenDrawingOptions = function(feature, drawingOptions, dontRandomize){
    drawingOptions = JSON.parse(JSON.stringify(drawingOptions));
    drawingOptions.extrusion = getDataDrivenDrawingOption(feature, drawingOptions, "extrusion", dontRandomize)
    drawingOptions.fill.fillColor = getDataDrivenDrawingOption(feature, drawingOptions, "fillColor", dontRandomize)
    drawingOptions.fill.opacity = getDataDrivenDrawingOption(feature, drawingOptions, "fillOpacity", dontRandomize)
    drawingOptions.stroke.strokeColor = getDataDrivenDrawingOption(feature, drawingOptions, "strokeColor", dontRandomize)
    drawingOptions.stroke.strokeGapColor = getDataDrivenDrawingOption(feature, drawingOptions, "strokeGapColor", dontRandomize)
    drawingOptions.stroke.strokeWidth = getDataDrivenDrawingOption(feature, drawingOptions, "strokeWidth", dontRandomize)
    drawingOptions.stroke.dashVal = getDataDrivenDrawingOption(feature, drawingOptions, "strokeDashVal", dontRandomize)
    drawingOptions.stroke.opacity = getDataDrivenDrawingOption(feature, drawingOptions, "strokeOpacity", dontRandomize)
    return drawingOptions
}

var getDataDrivenDrawingOption = function(feature, drawingOptions, stylePropName, dontRandomize) {
    var curDataTranformer, transformedData
    switch(stylePropName){
        case "extrusion":
            if(drawingOptions.dataTransformers && drawingOptions.dataTransformers[stylePropName] && drawingOptions.dataTransformers[stylePropName].active){
                curDataTranformer = drawingOptions.dataTransformers[stylePropName];
                if(feature.properties && (feature.properties[curDataTranformer.propName] != undefined || curDataTranformer.randomize)){
                    transformedData = transformData(curDataTranformer, feature.properties[curDataTranformer.propName], dontRandomize)
                    if(transformedData || transformedData === 0){
                        return transformedData
                    }
                }
            }
            return drawingOptions[stylePropName]
            break;
        case "fillColor":
            if(drawingOptions.dataTransformers && drawingOptions.dataTransformers[stylePropName] && drawingOptions.dataTransformers[stylePropName].active){
                curDataTranformer = drawingOptions.dataTransformers[stylePropName];
                if(feature.properties && (feature.properties[curDataTranformer.propName] != undefined || curDataTranformer.randomize)){
                    transformedData = transformData(curDataTranformer, feature.properties[curDataTranformer.propName], dontRandomize)
                    if(transformedData || transformedData === 0){
                        return transformedData
                    }
                }
            }
            return drawingOptions.fill.fillColor
            break;
        case "fillOpacity":
            if(drawingOptions.dataTransformers && drawingOptions.dataTransformers[stylePropName] && drawingOptions.dataTransformers[stylePropName].active){
                curDataTranformer = drawingOptions.dataTransformers[stylePropName];
                if(feature.properties && (feature.properties[curDataTranformer.propName] != undefined || curDataTranformer.randomize)){
                    transformedData = transformData(curDataTranformer, feature.properties[curDataTranformer.propName], dontRandomize)
                    if(transformedData || transformedData === 0){
                        return transformedData
                    }
                }
            }
            return drawingOptions.fill.opacity
            break;
        case "strokeColor":
            if(drawingOptions.dataTransformers && drawingOptions.dataTransformers[stylePropName] && drawingOptions.dataTransformers[stylePropName].active){
                curDataTranformer = drawingOptions.dataTransformers[stylePropName];
                if(feature.properties && (feature.properties[curDataTranformer.propName] != undefined || curDataTranformer.randomize)){
                    transformedData = transformData(curDataTranformer, feature.properties[curDataTranformer.propName], dontRandomize)
                    if(transformedData || transformedData === 0){
                        return transformedData
                    }
                }
            }
            return drawingOptions.stroke.strokeColor
            break;
        case "strokeGapColor":
            if(drawingOptions.dataTransformers && drawingOptions.dataTransformers[stylePropName] && drawingOptions.dataTransformers[stylePropName].active){
                curDataTranformer = drawingOptions.dataTransformers[stylePropName];
                if(feature.properties && (feature.properties[curDataTranformer.propName] != undefined || curDataTranformer.randomize)){
                    transformedData = transformData(curDataTranformer, feature.properties[curDataTranformer.propName], dontRandomize)
                    if(transformedData || transformedData === 0){
                        return transformedData
                    }
                }
            }
            return drawingOptions.stroke.strokeGapColor
            break;
        case "strokeWidth":
            if(drawingOptions.dataTransformers && drawingOptions.dataTransformers[stylePropName] && drawingOptions.dataTransformers[stylePropName].active){
                curDataTranformer = drawingOptions.dataTransformers[stylePropName];
                if(feature.properties && (feature.properties[curDataTranformer.propName] != undefined || curDataTranformer.randomize)){
                    transformedData = transformData(curDataTranformer, feature.properties[curDataTranformer.propName], dontRandomize)
                    if(transformedData || transformedData === 0){
                        return transformedData
                    }
                }
            }
            return drawingOptions.stroke.strokeWidth
            break;
        case "strokeDashVal":
            if(drawingOptions.dataTransformers && drawingOptions.dataTransformers[stylePropName] && drawingOptions.dataTransformers[stylePropName].active){
                curDataTranformer = drawingOptions.dataTransformers[stylePropName];
                if(feature.properties && (feature.properties[curDataTranformer.propName] != undefined || curDataTranformer.randomize)){
                    transformedData = transformData(curDataTranformer, feature.properties[curDataTranformer.propName], dontRandomize)
                    if(transformedData || transformedData === 0){
                        return transformedData
                    }
                }
            }
            return drawingOptions.stroke.dashVal
            break;
        case "strokeOpacity":
            if(drawingOptions.dataTransformers && drawingOptions.dataTransformers[stylePropName] && drawingOptions.dataTransformers[stylePropName].active){
                curDataTranformer = drawingOptions.dataTransformers[stylePropName];
                if(feature.properties && (feature.properties[curDataTranformer.propName] != undefined || curDataTranformer.randomize)){
                    transformedData = transformData(curDataTranformer, feature.properties[curDataTranformer.propName], dontRandomize)
                    if(transformedData || transformedData === 0){
                        return transformedData
                    }
                }
            }
            return drawingOptions.stroke.opacity
            break;
        default:
            return 0
            break;
    }
    /*var dataTransformer = {
        active: true,
        type: "value",
        propName: "height",
        clip: false,
        stops: [
            {inV:0, outV:0},
            {inV:200, outV:1000}
        ]
    }*/
    return drawingOptions[propName];
}

function escapeQuotesAndSlashes(string) {
    return string.replace(/["|\\]/g, "\\$&");
}

function unicodeLiteral(str) {
    var i;
    var result = "";
    for (i = 0; i < str.length; ++i) { /* You should probably replace this by an isASCII test */
        if (str.charCodeAt(i) > 126 || str.charCodeAt(i) < 32) {
            result += "u" + fixedHex(str.charCodeAt(i), 4);
        } else result += str[i];
    }
    return result;

    function fixedHex(number, length) {
        var str = number.toString(16).toUpperCase();
        while (str.length < length) str = "0" + str;
        return str;
    }
}

var awareStringReverse = function(string) {

	var regexSymbolWithCombiningMarks = /([\0-\u02FF\u0370-\u1AAF\u1B00-\u1DBF\u1E00-\u20CF\u2100-\uD7FF\uE000-\uFE1F\uFE30-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])([\u0300-\u036F\u1AB0-\u1AFF\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]+)/g;
	var regexSurrogatePair = /([\uD800-\uDBFF])([\uDC00-\uDFFF])/g;

	var reverse = function(string) {
		// Step 1: deal with combining marks and astral symbols (surrogate pairs)
		string = string
			// Swap symbols with their combining marks so the combining marks go first
			.replace(regexSymbolWithCombiningMarks, function($0, $1, $2) {
				// Reverse the combining marks so they will end up in the same order
				// later on (after another round of reversing)
				return reverse($2) + $1;
			})
			// Swap high and low surrogates so the low surrogates go first
			.replace(regexSurrogatePair, '$2$1');
		// Step 2: reverse the code units in the string
		var result = '';
		var index = string.length;
		while (index--) {
			result += string.charAt(index);
		}
		return result;
	};

	return reverse(string)

};

