/*****************************************************************************
* shapeEditor
* Created by Karl Pietsch Date: 8/2007
* Updated/Modified/Commented by Rowan Lewis Date: 1/2008
* 
* Desc: ShapeEditor Allows a user to create shapes (points, lines, polygons) directly
* in a browser. The objects can be saved/loaded into a postgis spatial database.
* Objects can be created, deleted, and edited.
* The class requires an external interface to be created to link the objects
* appropriately.
* Utilises wz_jsgraphics library for drawing objects to the screen.
*
*****************************************************************************/

/*****************************************************************************
* Global Variables / Declarations
*****************************************************************************/
var thisSE;
var veRespCallId7;
var mouseLeft = 0;
var mouseMid = 1;
var mouseRight = 2;

if (document.all)
{
    mouseLeft = 1;
    mouseRight = 2;
    mouseMid = 4;
}

/*****************************************************************************
* Object : Shape
* Desc :
*****************************************************************************/
function Shape()
{
	this.shapetype='';
	this.gid;
	this.shapename;
	this.SRID=-1;
	this.shapeAsText='';
	this.shapeParts = new Array();
	this.isSaved=false;
	this.isNewFeature=true; //is this a new part (not saved in shape) or is it one being edited

	this.tostring = function (){
		if (this.shapetype=="POINT"){
			return "POINT(" + this.shapeParts[0].tostring() + ")";
		}else if (this.shapetype=="MULTIPOINT"){
			var str="";
			for (var i in this.shapeParts){
				str = str + "," + this.shapeParts[i].tostring();
			}
			return "MULTIPOINT(" + str.substr(1) + ")";
		}else if (this.shapetype=="POLYLINE"){
			return "LINESTRING(" + this.shapeParts[0].tostring() + ")";
		}else if (this.shapetype=="MULTIPOLYLINE"){
			var str="";
			for (var i in this.shapeParts){
				if (!this.shapeParts[i].isEmpty()) str += ",(" + this.shapeParts[i].tostring() + ")";
			}
			return  "MULTILINESTRING(" + str.substr(1) + ")";
		}else if (this.shapetype=="POLYGON"){
			return "POLYGON(" + this.shapeParts[0].tostring() + ")";
		}else if (this.shapetype=="MULTIPOLYGON"){
			var str="";
			for (var i in this.shapeParts){
			    var x = this.shapeParts[i].isEmpty();
			    if (!this.shapeParts[i].isEmpty()) str += ",(" + this.shapeParts[i].tostring() + ")";
			}
			return "MULTIPOLYGON(" + str.substr(1) + ")";
		}else if (this.shapetype=="GEOMETRYCOLLECTION"){
			var str="";
			for (var i in this.shapeParts){
				if (!this.shapeParts[i].isEmpty()) str += "," + this.shapeParts[i].type.toUpperCase() + "("  + this.shapeParts[i].tostring() + ")";
			}
			return "GEOMETRYCOLLECTION(" + str.substr(1) + ")";
		}
	}
	
	this.fromstring = function(str){
		var parts;
		if (str.indexOf("POINT")==0){
			this.shapeParts[0]=new Point();
			this.shapeParts[0].fromstring(str.substr(6,str.length -7));
		}else if (str.indexOf("MULTIPOINT")==0){
			str = str.substr(11,str.length -12);
			parts = str.split(",");
			for (var i in parts){
				this.shapeParts[i]=new Point();
				this.shapeParts[i].fromstring(parts[i]);
			}
		}else if (str.indexOf("LINESTRING")==0){
			this.shapeParts[0]=new Polyline();
			this.shapeParts[0].fromstring(str.substr(11,str.length -12));
		}else if (str.indexOf("MULTILINESTRING")==0){
			str = str.substr(16,str.length -17);
			parts = tokenSplit(parts[i].substr(1,parts[i].length -2));
			for (var i in parts){
				this.shapeParts[i]=new Polyline();
				this.shapeParts[i].fromstring(parts[i]);
			}
		}else if (str.indexOf("POLYGON")==0){
			this.shapeParts[0]=new Polygon();
			this.shapeParts[0].fromstring(str.substr(8,str.length -9));
		}else if (str.indexOf("MULTIPOLYGON")==0){
			str = str.substr(13,str.length -14);
			parts = tokenSplit(str);
			for (var i in parts){
				this.shapeParts[i]=new Polygon();
				this.shapeParts[i].fromstring(parts[i].substr(1,parts[i].length -2));
			}
		}else if (str.indexOf("GEOMETRYCOLLECTION")==0){
			str = str.substr(19,str.length -20);
			parts = tokenSplit(str);
			for (var i in parts){
				if (parts[i].indexOf("POINT")==0){
					this.shapeParts[i]=new Point();
					this.shapeParts[i].fromstring(parts[i].substr(6,parts[i].length -7));
				}else if (parts[i].indexOf("LINESTRING")==0){
					this.shapeParts[i]=new Polyline();
					this.shapeParts[i].fromstring(parts[i].substr(11,parts[i].length -12));
				}else if (parts[i].indexOf("POLYGON")==0){
					this.shapeParts[i]=new Polygon();
					this.shapeParts[i].fromstring(parts[i].substr(8,parts[i].length -9));
				}else{
					//unrecognised geometry 
					// to do : error
					continue;
				}
			//	this.shapeParts[i].fromstring(parts[i]);
			}
		}else {
			//unrecognised geometry 
			// to do : error
		}
	}
}

/*****************************************************************************
* Object : Point
* Desc :
*****************************************************************************/
function Point(){
	this.type="point";
	this.isClosed = false;	 // is finished editing, for polygon parts is ring closed aswell
	this.x = 0;
	this.y = 0;
	
	this.tostring = function (){
		return this.x + " " + this.y;
	}
	
	this.fromstring = function(str){
    //if (str.indexOf("(")>-1) str = str.substr(str.indexOf("(")+1,str.lastIndexOf(")")-str.indexOf("(")-1);
		var coords = str.split(" ")
		this.x = coords[0];
		this.y = coords[1];
		this.isClosed=true;
	}
	
	this.getNumPoints = function(){
		return 1;
	}
	
	this.addPoint = function(mapX,mapY){
		this.x = mapX;
		this.y = mapY;
		this.isClosed=true;
	}
	
	this.close = function(){
		
		//this.isClosed=true;
		//can only be closed if point is added, closed automatically when one is
	}
	
	this.removeLastPoint = function(){
	    this.isClosed=false;
	}
	
	this.isEmpty = function() { return !this.isClosed;}
}

/*****************************************************************************
* Object : Polyline
* Desc :
*****************************************************************************/
function Polyline(){
	this.type="polyline";
	this.xCoords = new Array(); //array of x coordinates
	this.yCoords = new Array(); //array of x coordinates
	this.isClosed = false;	 // is finished editing, for polygon parts is ring closed aswell
	
	this.tostring= function (){
		var str="";
		for (var i in this.xCoords){
			if (!this.isEmpty()) str += "," + this.xCoords[i] + " " + this.yCoords[i];
		}
		return str.substr(1);
	}
	
	this.getNumPoints=function(){
		return this.xCoords.length;
	}
	
	this.getNumSegments=function(){
		return this.xCoords.length-1;
	}
	
	this.fromstring = function(str){
		var points = str.split(",")
		var coords;
		for (var i in points){
			coords = points[i].split(" ");
			this.xCoords[i] = coords[0];
			this.yCoords[i] = coords[1];
		}	
	}
	
	this.setPoint = function(index,X,Y){
	    this.xCoords[index]= X;
	    this.yCoords[index]= Y;
	    this.xCoordsOrgProj[index]= null;
	    this.yCoordsOrgProj[index]= null;
	 }
	 
	 this.movePoint = function(index,X,Y){
	    this.xCoords[index]= X;
	    this.yCoords[index]= Y;
	 }
	
	this.addPoint = function(mapX,mapY){
		if (this.getNumPoints()> 0){
		    if ((mapX != this.xCoords[this.getNumPoints()-2]) && (mapY != this.yCoords[this.getNumPoints()-2])){
				//already added if using dynamic drag
				//this.xCoords.push(mapX);
				//this.yCoords.push(mapY);
				   this.xCoords[this.getNumPoints()-1]=mapX;
				   this.yCoords[this.getNumPoints()-1]=mapY;
			}
		}else{
			this.xCoords.push(mapX);
			this.yCoords.push(mapY);
		}
		
	}
	
	this.removePoint = function(index){
		if (index < this.getNumPoints()) {
				this.xCoords.splice(index,1);
				this.yCoords.splice(index,1);
		}
	}
	
	this.removeLastPoint = function(){
		this.xCoords.pop();
		this.yCoords.pop();
		if (this.isClosed){
			this.isClosed=false;
		}
	}
	
	this.insertPoint = function(index,mapX,mapY){
		if (index < this.getNumPoints()) {
			this.xCoords.splice(index,0,mapX);
			this.yCoords.splice(index,0,mapY);
		}
	}
	
	this.close = function(){
		if (this.getNumSegments() > 0 ){
			this.isClosed=true;
		}
	}
	
	this.isEmpty = function() { return this.getNumSegments()<1;}
}

/*****************************************************************************
* Object : Polygon
* Desc :
*****************************************************************************/
function Polygon()
{
	//polygon is  a container shape, editing is done on each polygonpart object that this contains
	//first part of polygon is boundary, remaining parts are holes
	this.type="polygon";
	this.parts = new Array();
	this.parts[0] = new PolygonPart(this);
	this.basePart = this.parts[0];
	this.isClosed = false;	 // is finished editing, for polygon parts is ring closed aswell
	
	this.getNewPart = function(){
		this.parts.push(new PolygonPart(this));
		return this.parts[this.parts.length-1];
	}
	
	this.tostring= function (){
		var str="";
		for (var i in this.parts){
			if (!this.isEmpty()) str += ",(" + this.parts[i].tostring() + ")";
		}
		return str.substr(1);
	}
	
	this.fromstring = function(str){
		var partsStr = tokenSplit(str);
		
		var coords;
		for (var i in partsStr){
			this.parts[i]= new PolygonPart(this);
			this.parts[i].fromstring(partsStr[i].substr(1,partsStr[i].length-2));
			//points = parts[i].split(",");
			
		}	
		this.basePart = this.parts[0];
	}
	this.isEmpty = function() { return this.parts.length<1 || (this.parts.length==1 && this.parts[0].getNumSegments()<1);}
}

/*****************************************************************************
* Object : Polygonpart
* Desc :
*****************************************************************************/
function PolygonPart(parent)
{
	this.type="polygonpart";
	this.xCoords = new Array(); //array of x coordinates
	this.yCoords = new Array(); //array of y coordinates
	this.xCoordsOrgProj = new Array(); //array of x coordinates
	this.yCoordsOrgProj = new Array(); //array of y coordinates
	this.parent = parent;
	this.isClosed = false;	 // is finished editing, for polygon parts is ring closed aswell
	
	this.tostring= function (){
		var str="";
		for (var i in this.xCoords){
			str = str + "," + this.xCoords[i] + " " + this.yCoords[i];
		}
		return str.substr(1);
	}
	
	this.fromstring = function(str){
		var points = str.split(",");
		for (var j in points){
				coords = points[j].split(" ");
				this.xCoords[j] = coords[0];
				this.yCoords[j] = coords[1];
			}
		this.isClosed=true; //assumed, should check valid closed ring polygon
	}
	
	this.fromstringOrgProj = function(str){
		var points = str.split(",");
		for (var j in points){
				coords = points[j].split(" ");
				this.xCoordsOrgProj[j] = coords[0];
				this.yCoordsOrgProj[j] = coords[1];
			}
		this.isClosed=true; //assumed, should check valid closed ring polygon
	}
	
	this.getNumPoints=function(){
		return this.xCoords.length;
	}
	
	this.getNumSegments=function(){
		//if (this.isClosed){
		//	return this.xCoords.length-2;
		//}else{
			return this.xCoords.length-1;
		//}
	}
	
	this.setPoint = function(index,X,Y,mapXorgProj,mapYorgProj){
	    this.xCoords[index]= X;
	    this.yCoords[index]= Y;
	    this.xCoordsOrgProj[index]= mapXorgProj;
	    this.yCoordsOrgProj[index]= mapYorgProj;
	    if (this.isClosed){
		    if (index=0){
			    this.xCoords[this.getNumPoints()-1]= X;
			    this.yCoords[this.getNumPoints()-1]= Y;
			    this.xCoordsOrgProj[this.getNumPoints()-1]= mapXorgProj;
			    this.yCoordsOrgProj[this.getNumPoints()-1]= mapYorgProj;
		    }
		    if (index==this.getNumPoints()-1){
			    this.xCoords[0]= X;
			    this.yCoords[0]= Y;
			    this.xCoordsOrgProj[0]= mapXorgProj;
			    this.yCoordsOrgProj[0]= mapYorgProj;
		    }
	    }
	}
	
	this.movePoint = function(index,X,Y){
	    this.xCoords[index]= X;
	    this.yCoords[index]= Y;
	    if (this.isClosed){
		    if (index=0){
			    this.xCoords[this.getNumPoints()-1]= X;
			    this.yCoords[this.getNumPoints()-1]= Y;
		    }
		    if (index==this.getNumPoints()-1){
			    this.xCoords[0]= X;
			    this.yCoords[0]= Y;
		    }
	    }
	}
	
	this.addPoint = function(mapX,mapY,mapXorgProj,mapYorgProj){
		if (this.getNumPoints() > 0){
			if ((mapX != this.xCoords[this.getNumPoints()-2]) && (mapY != this.yCoords[this.getNumPoints()-2])){
				//already added if using dynamic drag
				//this.xCoords.push(mapX);
				//this.yCoords.push(mapY);
				   this.xCoords[this.getNumPoints()-1]=mapX;
				   this.yCoords[this.getNumPoints()-1]=mapY;
				   this.xCoordsOrgProj[this.getNumPoints()-1]=mapXorgProj;
				   this.yCoordsOrgProj[this.getNumPoints()-1]=mapYorgProj;
			}
		}else{
			this.xCoords.push(mapX);
			this.yCoords.push(mapY);
			this.xCoordsOrgProj.push(mapXorgProj);
			this.yCoordsOrgProj.push(mapYorgProj);
		}
		if (checkAllSegIntersectShapeParts(this,this.getNumSegments()-1)) this.removeLastPoint();
	}
	
	this.removePoint = function(index){
		var x;
		var y;
		var xOrgProj;
		var yOrgProj;
		if (index < this.getNumPoints()) {
			if (this.isClosed){
				if (this.getNumPoints()>4){
					// dont remove point if closed polygon and only triangle left
					x = this.xCoords.splice(index,1);
					y = this.yCoords.splice(index,1);
					xOrgProj = this.xCoordsOrgProj.splice(index,1);
					yOrgProj = this.yCoordsOrgProj.splice(index,1);
					// make sure closed ends meet
					if (index==0){
						this.xCoords[this.getNumPoints()-1]= this.xCoords[0];
						this.yCoords[this.getNumPoints()-1]= this.yCoords[0];
						this.xCoordsOrgProj[this.getNumPoints()-1]= this.xCoordsOrgProj[0];
						this.yCoordsOrgProj[this.getNumPoints()-1]= this.yCoordsOrgProj[0];
					}else if (index==this.getNumPoints()-1){
						this.xCoords[0]= this.xCoords[this.getNumPoints()-1];
						this.yCoords[0]= this.yCoords[this.getNumPoints()-1];
						this.xCoordsOrgProj[0]= this.xCoordsOrgProj[this.getNumPoints()-1];
						this.yCoordsOrgProj[0]= this.yCoordsOrgProj[this.getNumPoints()-1];
					}
				}else{
				  /*  // trianlge split open into open polygon
				    // makes split point start of polygon
				    this.isClosed=false;
				    //x = this.xCoords.splice(index,1);
					//y = this.yCoords.splice(index,1);
					var i = parseInt(index);
					while (index > 0){
					    for (var j=index;j+=1;j<this.xCoords.count-1){
					        this.xCoords[j]=this.xCoords[j+1];
					    }
					    this.xCoords[this.xCoords.count-1]=this.xCoords[0];
					    for (j=0;j+=1;j<index-1){
					        this.xCoords[j]=this.xCoords[j+1];
					    }
					    index -=1;
					}
					x = this.xCoords.splice(index,1);
				    y = this.yCoords.splice(index,1);
				    //N.B. undo later will not be good ij just insert, order changed*/
				}   
			}else{
				x = this.xCoords.splice(index,1);
				y = this.yCoords.splice(index,1);
				xOrgProj = this.xCoordsOrgProj.splice(index,1);
				yOrgProj = this.yCoordsOrgProj.splice(index,1);
			}
			
			//check that does not intersect
			if (checkAllSegIntersectShapeParts(this,index) || checkAllSegIntersectShapeParts(this,(index+this.getNumSegments()-1)%this.getNumSegments())){
					// check that segments either side of point do not intersect shape
					//	if so put it back
				this.insertPoint(index,x[0],y[0]);
				this.xCoordsOrgProj[index]=xOrgProj[0];
				this.yCoordsOrgProj[index]=yOrgProj[0];
				if (this.isclosed){ //actually probably wont ever be in removePoint mode while polygon not closed?
					// make sure closed ends meet
					if (index==0){
						this.xCoords[this.getNumPoints()-1]= this.xCoords[0];
						this.yCoords[this.getNumPoints()-1]= this.yCoords[0];
						this.xCoordsOrgProj[this.getNumPoints()-1]= this.xCoordsOrgProj[0];
						this.yCoordsOrgProj[this.getNumPoints()-1]= this.yCoordsOrgProj[0];
					}else if (index==this.getNumPoints()-1){
						this.xCoords[0]= this.xCoords[this.getNumPoints()-1];
						this.yCoords[0]= this.yCoords[this.getNumPoints()-1];
						this.xCoordsOrgProj[0]= this.xCoordsOrgProj[this.getNumPoints()-1];
						this.yCoordsOrgProj[0]= this.yCoordsOrgProj[this.getNumPoints()-1];
					}
				}
			}
		}
	}
	
	this.removeLastPoint = function(){
		var x = this.xCoords.pop();
		var y = this.yCoords.pop();
		this.xCoordsOrgProj.pop();
		this.yCoordsOrgProj.pop();
		if (this.isClosed){
			this.isClosed=false;
		}
		//no need to check if intersects when removing last point cause would not have been added if did
	}
	
	this.insertPoint = function(index,mapX,mapY,mapXorgProj,mapYorgProj){
		if (index < this.getNumPoints()) {
			this.xCoords.splice(index,0,mapX);
			this.yCoords.splice(index,0,mapY);
			this.xCoordsOrgProj.splice(index,0,mapXorgProj);
			this.yCoordsOrgProj.splice(index,0,mapYorgProj);
			if (!checkAllSegIntersectShapeParts(this,index) && !checkAllSegIntersectShapeParts(this,(index+this.getNumSegments()-1)%this.getNumSegments())){
					// check that segments either side of point do not intersect shape
					//	if so put remove
				
			}
		}
	}
	
	this.close = function(){
		// makes last point join with start point when closing
		if (!this.isClosed){
			if (this.getNumSegments() > 2  ){
				//this.xCoords.push(this.xCoords[0]);
				//this.yCoords.push(this.yCoords[0]);
				this.xCoords[this.getNumPoints()-1]=this.xCoords[0];
				this.yCoords[this.getNumPoints()-1]=this.yCoords[0];
				this.xCoordsOrgProj[this.getNumPoints()-1]=this.xCoordsOrgProj[0];
				this.yCoordsOrgProj[this.getNumPoints()-1]=this.yCoordsOrgProj[0];
				this.isClosed=true;
				if (checkAllSegIntersectShapeParts(this,this.getNumSegments()-1)) this.removeLastPoint();
			}
		}
	}
	
	this.isEmpty = function() { return this.getNumSegments()<1;}
	
}

function pick(ev){
	//attachEvent will not work for function "this.pick" so need this function external to shapeEditor object
	thisSE.pick(ev);
}

function mouseMove(ev){
	thisSE.mouseMove(ev);
}

function keyDown(ev){
	thisSE.keyDown(ev);
}

function keyUp(ev){
	thisSE.keyUp(ev);
}

/*****************************************************************************
* Object : ShapeEditor
* Desc :
*****************************************************************************/
function shapeEditor(editorDivId)
{
	thisSE = this;
	this.veRespCallId;
	this.editShape = new Shape();
	this.editPart;
	this.editPointIndex; //integer, index of point in array of points in editpart
	//this.editPolygonPart;
	this.editPointPrevX; //previous values for undo, when editing point positions of features
	this.editPointPrevY;
	
	this.pointsRemaining = 0;
	
	this.mode="";
	this.editDiv = document.getElementById(editorDivId);
	this.viewOrigin = new Point();
	this.viewOrigin.x=0;
	this.viewOrigin.y=0;
	this.viewCellSizeX=1;
	this.viewCellSizeY=1;
	this.viewSRID=-1;
	
	this.snapOrigin = new Point();
	this.snapOrigin.x=0;
	this.snapOrigin.y=0;
	this.snapCellSizeX=1;
	this.snapCellSizeY=1;
	this.snapMode=0; //0 is centre of view pixel, 1 is top left corner of snap cell , 2 is centre of snap cell 
	this.secondShape;
	this.isNewShape=true; //not saved in db
		
	this.onAfterUpdate= function(){} //event functions that can trigger back to client
	this.onAfterGetFeature= function(){}
	this.onAfterCompleteShape = function(){}
	
	this.editshapename='';
	this.editShapeType='';
	this.editShapeSRID=-1;
	
	this.closestVertexY;
	this.closestVertexX;
	this.closestVertexYorgProj;
	this.closestVertexXorgProj;
		
	this.key;	
	this.movedMouse=false;
	
	this.allowFeatureOverlapping=true; //by default a feature can overlap another one in database
	this.allowMergeOnlyIntersecting=false; // by default merges any features, otherwise only touching or overlapping ones 
	
	this.snapShapes= new Array(); // array of extra shpae layers that editor snaps
		
	this.editRestrictionQuery;  //restriction on edit area
	
	//this.editDiv.oncontextmenu=function(){return false}; // disable right click context menu
	//document.oncontextmenu=function(){return false}; // disable right click context menu
	
	var jg = new jsGraphics(editorDivId);
	//jg.clear();
	
	if (document.addEventListener)
    {
        document.addEventListener("mousedown", pick, false);
        document.addEventListener("mousemove", mouseMove, false);
        document.addEventListener("keydown", keyDown, false);
        document.addEventListener("keyup", keyUp, false);
    }
    else if(document.attachEvent)
    {
        document.attachEvent("onmousedown", pick);
        document.attachEvent("onmousemove", mouseMove);
        document.attachEvent("onkeydown", keyDown);
        document.attachEvent("onkeyup", keyUp);
    }
    else
    {
        document.onmousedown = pick;
        document.onmousemove = mouseMove;
        document.onkeydown = keyDown;
        document.onkeyup = keyUp;
    } 
		
	this.kill = function(){
	
        if (document.removeEventListener)
        {
            document.removeEventListener("mousedown", pick, false);
            document.removeEventListener("mousemove", mouseMove, false);
            document.removeEventListener("keydown", keyDown, false);
            document.removeEventListener("keyup", keyUp, false);
        }
        else if(document.detachEvent)
        {
            document.detachEvent("onmousedown", pick);
            document.detachEvent("onmousemove", mouseMove);
            document.detachEvent("onkeydown", keyDown);
            document.detachEvent("onkeyup", keyUp);
        }
        else
        {
            document.onmousedown = null;
            document.onmousemove = null;
            document.onkeydown = null;
            document.onkeyup = null;
        } 
	   
	}
	
	this.onChangeEditShapeName = function(){
	    //used if attched to drop down list or something, should be handled in client. 
	    // was planning on having standard controls for client app
		this.setEditShapeName(window.event.srcElement.value);
	}
	
	this.onChangeEditShapeType = function(){
	    //used if attched to drop down list or something, should be handled in client. 
	    // was planning on having standard controls for client app
		this.setEditShapeType(window.event.srcElement.value);
	}
	
	this.setEditShapeSRID = function(SRID){
		this.editShapeSRID=SRID;
	}
	
	this.setEditShapeName = function(shapename){
		this.editshapename = shapename;
		this.mode="waitingForShapeInfo";
		this.veRespCallId= vectorEditor.getShapeInfo(this.editshapename, getShapeInfoResult);
		this.isNewShape=false;
	}
	
	this.getNewShape = function(){
		this.editShape = new Shape();
		this.editShape.shapetype = this.editShapeType;
		this.editShape.SRID = this.editShapeSRID;
		this.editShape.shapename = this.editshapename;
		this.redrawShape();
	}	
	
	this.clear = function(){
		jg.clear();
		jg.paint();
	}
	
	this.setFont = function(fontFamily,size,style){
	    jg.setFont(fontFamily,size,style);
	}
	
	this.drawString = function(text,x,y){
	    jg.drawString(text,x,y);
	    jg.paint();
	}
	
	this.viewExtent = function(){
		var extent = new Array();
		extent.push(this.viewOrigin.x);
		extent.push(this.viewOrigin.y);
		extent.push(this.viewOrigin.x + (this.viewCellSizeX * this.editDiv.clientWidth));
		extent.push(this.viewOrigin.y - (this.viewCellSizeY * this.editDiv.clientHeight));
		return extent;
	}
	
	this.getNewFeature = function(){
		this.editShape = new Shape();
		this.redrawShape();
	}
	
	this.cleanup = function(){
		//this.editDiv.detachEvent("onmousedown", pick);
		//this.editDiv.detachEvent("onmousemove", mouseMove);
		//document.detachEvent("onmousemove", mouseMove);
		//document.detachEvent("onmousedown", pick);
		//alert("ye");
		// to do re-enable context menu
	}
	
	this.setMode = function(mode){
		
		//this.editShape = new Shape();
		if (mode=="drawingPolygon"){
			this.editPart = new Polygon();//.basePart();
			if (this.editShape.shapeParts.length>0){
				if (this.editShape.shapetype=="POLYGON" || this.editShape.shapetype=="MULTIPOLYGON"){
					this.editShape.shapetype="MULTIPOLYGON";
				}else{
					this.editShape.shapetype="GEOMETRYCOLLECTION";
				}
			}else if (this.editShape.shapetype==""){
				this.editShape.shapetype="POLYGON";
			}
			this.editShape.shapeParts.push(this.editPart);
			this.editPart = this.editPart.basePart;
			//this.editShape.isNewFeature=true;
			this.editPointIndex=0;
	    }else if (mode=="drawingPolygonHole"){
	        if (this.editPart.isClosed){
	            if (this.editPart.type=="polygonpart"){
	                this.editPart=this.editPart.parent.getNewPart();
	                this.editPointIndex=0;
	            }else{
	                alert("Error: must have a polygon part selected to add a hole")
	                return;
	            }
	        }else{
	            alert("Error part is not closed, cant add hole");
	            return;
	        }
		}else if (mode=="drawingPolyline"){
			this.editPart = new Polyline();
			if (this.editShape.shapeParts.length>0){
				if (this.editShape.shapetype=="POLYLINE" || this.editShape.shapetype=="MULTIPOLYLINE"){
					this.editShape.shapetype="MULTIPOLYLINE";
				}else{
					this.editShape.shapetype="GEOMETRYCOLLECTION";
				}
			}else if (this.editShape.shapetype==""){
				this.editShape.shapetype="POLYLINE";
			}
			this.editShape.shapeParts.push(this.editPart);
			//this.editShape.isNewFeature=true;
			this.editPointIndex=0;
		}else if (mode=="drawingPoint"){
			this.editPart = new Point();
			if (this.editShape.shapeParts.length>0){
				if (this.editShape.shapetype=="POINT" || this.editShape.shapetype=="MULTIPOINT"){
					this.editShape.shapetype="MULTIPOINT";
				}else{
					this.editShape.shapetype="GEOMETRYCOLLECTION";
				}
			}else if (this.editShape.shapetype==""){
				this.editShape.shapetype="POINT";
			}
			this.editShape.shapeParts.push(this.editPart);
			this.editShape.isNewFeature=true;
			this.editPointIndex=0;
		}else if (mode=="selectingEditFeature"){
			this.editShape=null;
		}else if (mode=="editingPoints"){
			//check current editPart is not empty
		}else if (mode=="intersectingFeatures"){
			this.secondShape = new Shape();
			this.secondShape.shapetype="POLYLINE";
			this.secondShape.shapeParts.push(new Polyline());
			this.editPointIndex=0;
		}else if (mode=="mergingFeatures"){
			this.secondShape = new Shape();
		}else if (mode=="deletingPart"){
		}
		this.mode=mode;
		this.redrawShape();
	}
	
	this.isWithin = function(x,y,left,top,width,height){
		return ((x >= left) && (x < (left+width)) && (y >=top) && (y<(top+height)));
	}
	
	this.mouseMove = function(ev){
		
		    
		var eX, eY, oX, oY;
		if (document.all)
        {
            if (this.editDiv.parentElement == null)
		    return false;
            eX = ev.clientX + document.documentElement.scrollLeft;
            eY = ev.clientY + document.documentElement.scrollTop;
            oX = oY = 0;
            var tmpObj = this.editDiv;
            while(tmpObj)
            {
	            oX += parseInt(tmpObj.offsetLeft);
	            oY += parseInt(tmpObj.offsetTop);
	            tmpObj = tmpObj.offsetParent || null;
            } 
        }
        else
        {
            eX = ev.pageX;
            eY = ev.pageY;
            oX = oY = 0; //global helper vars
            var tmpObj = this.editDiv;
	        while(tmpObj)
	        {
		        oX += parseInt(tmpObj.offsetLeft);
		        oY += parseInt(tmpObj.offsetTop);
		        tmpObj = tmpObj.offsetParent || null;
	        }
        }
        
		if (this.isWithin(eX,eY,oX,oY,this.editDiv.offsetWidth,this.editDiv.offsetHeight)){
		    this.movedMouse=true;
			if (this.mode=="placingPoint"){
				if (this.editPart.type=="point"){
					this.editPart.x=this.convertImageToMapCoordX(eX-oX);
					this.editPart.y=this.convertImageToMapCoordY(eY-oY);
					this.redrawShape();
				}else{
				    if (!ev.altKey){
					    this.editPart.movePoint(this.editPointIndex,this.convertImageToMapCoordX(eX-oX), 
					    this.convertImageToMapCoordY(eY-oY));
					    this.redrawShape();
					}
				}
			}else if (this.mode=="drawingPoint"){
			   
			}else if (this.mode.indexOf("drawingPol")==0 && this.editPart.getNumPoints()>0 && !this.editPart.isClosed){
			    if (!ev.altKey){
			        this.editPart.movePoint(this.editPointIndex,this.convertImageToMapCoordX(eX-oX), 
					        this.convertImageToMapCoordY(eY-oY));
			        if (this.editPart.getNumPoints()>1) this.redrawShape();
			    }
		    }else if (this.mode.indexOf("intersectingFeatures")==0 && this.secondShape.shapeParts[0].getNumPoints()>0 && !this.secondShape.shapeParts[0].isClosed){
			    if (!ev.altKey){
			        this.editPart.movePoint(this.editPointIndex,this.convertImageToMapCoordX(eX-oX), 
					        this.convertImageToMapCoordY(eY-oY));
			        if (this.secondShape.shapeParts[0].getNumPoints()>1) this.redrawShape();
			    }
		    }
		}
	}
		
	this.keyDown = function(ev){	
	    var eX, eY, oX, oY;
		if (document.all)
        {
            eX = ev.x;
            eY = ev.y;
            oX = this.editDiv.offsetLeft
            oY = this.editDiv.offsetTop;
        }
        else
        {
            eX = ev.pageX;
            eY = ev.pageY;
            oX = oY = 0; //global helper vars
            var tmpObj = this.editDiv;
		    while(tmpObj)
		    {
			    oX += parseInt(tmpObj.offsetLeft);
			    oY += parseInt(tmpObj.offsetTop);
			    tmpObj = tmpObj.offsetParent || null;
		    }
        }
        
		if (ev.keyCode==27 && this.key!=ev.keyCode){//esc
			if (this.mode.indexOf("drawing")==0){
			    if (this.editPart.type!="point"){
				    this.editPart.removeLastPoint();
				    if (this.editPointIndex>0) this.editPointIndex -=1;
				    this.redrawShape();
				}
			}else if (this.mode=="placingPoint"){
			    if (this.editPart.type!="point"){
			       this.setMode("editingPoints");
			       this.editPart.xCoords[this.editPointIndex]= this.editPointPrevX;
			       this.editPart.yCoords[this.editPointIndex]= this.editPointPrevY;
			       // CoordsOrgproj unchanged
		           this.redrawShape();
			    }
			}
		}else if (ev.keyCode==18 && this.key!=ev.keyCode && this.movedMouse){//alt
		    //this.movedMouse=false;
		    /*if (this.mode.indexOf("drawing")==0 || this.mode=="placingPoint"){
		       this.snapToClosestVertex(eX-oX,eY-oY);
		    }*/
		}else if (ev.keyCode==17 && this.key!=ev.keyCode && this.movedMouse){//ctrl
		   // this.movedMouse=false;
		    /*if (this.mode.indexOf("drawing")==0 || this.mode=="placingPoint"){
		       this.snapToClosestSegment(eX-oX,eY-oY);
		    }*/
		}else if (ev.keyCode==46 && this.mode=="placingPoint"){
		    if (this.editPart.type!="point"){
		        this.setMode("editingPoints");
		        this.editPart.removePoint(this.editPointIndex);
		        this.redrawShape();
		    }    
		}
		this.key=ev.keyCode; //allow repeat on key down or not
	}
	
	this.keyUp = function(ev){
	    this.key=-1;
	    
	    //this.redrawShape();
	}
	
	this.loadFeature = function(gid){
	    this.veRespCallId = vectorEditor.getFeature("gid",gid,this.editshapename,VEresults);    
	}
	
	this.snapToClosestVertex = function(x,y){
	    var shapes = "";
	    if (!this.isNewShape) shapes = "," + this.editshapename ;
	    for  (var i in this.snapShapes){
	        shapes += "," + this.snapShapes[i];
	    }
	    shapes = shapes.substr(1);
	    this.veRespCallId= vectorEditor.getClosestVertex(shapes,this.convertImageToMapCoordX(x),this.convertImageToMapCoordY(y),this.viewOrigin.x,this.viewOrigin.y,this.viewOrigin.x + (this.viewCellSizeX * this.editDiv.clientWidth),this.viewOrigin.y - (this.viewCellSizeY * this.editDiv.clientHeight),this.editShapeSRID,getClosestVertexResult);    
	}
	
	this.snapToClosestSegment= function(x,y){
	    var shapes = "";
	    if (!this.isNewShape) shapes = "," + this.editshapename;
	    for (var i in this.snapShapes){
	        shapes += "," + this.snapShapes[i];
	    }
	    shapes = shapes.substr(1);
	    this.veRespCallId= vectorEditor.getClosestSegment(shapes,this.convertImageToMapCoordX(x),this.convertImageToMapCoordY(y),this.viewOrigin.x,this.viewOrigin.y,this.viewOrigin.x + (this.viewCellSizeX * this.editDiv.clientWidth),this.viewOrigin.y - (this.viewCellSizeY * this.editDiv.clientHeight),this.editShapeSRID, getClosestSegmentResult);    
	}

	this.pick = function(ev){
		// user click 
		
		    
		var eX, eY, oX, oY;
		if (document.all)
        {
            if (this.editDiv.parentElement == null)
		    return false;
		    
            eX = ev.clientX + document.documentElement.scrollLeft;
            eY = ev.clientY + document.documentElement.scrollTop;
            //oX = this.editDiv.offsetLeft
            //oY = this.editDiv.offsetTop;
            oX = oY = 0;
            var tmpObj = this.editDiv;
	        while(tmpObj)
	        {
		        oX += parseInt(tmpObj.offsetLeft);
		        oY += parseInt(tmpObj.offsetTop);
		        tmpObj = tmpObj.offsetParent || null;
	        }   
        }
        else
        {
            eX = ev.pageX;
            eY = ev.pageY;
            oX = oY = 0; //global helper vars
            var tmpObj = this.editDiv;
	        while(tmpObj)
	        {
		        oX += parseInt(tmpObj.offsetLeft);
		        oY += parseInt(tmpObj.offsetTop);
		        tmpObj = tmpObj.offsetParent || null;
	        }           
        }
        
        
		var pickX=eX-oX;
		var pickY=eY-oY;
		var mapX = this.convertImageToMapCoordX(pickX);
		var mapY = this.convertImageToMapCoordY(pickY);
		var mapXorgProj = null;
		var mapYorgProj = null;
		if (ev.altKey==true){
		    mapX=this.closestVertexX;
		    mapY=this.closestVertexY;
		    mapXorgProj = this.closestVertexYorgProj;
	        mapYorgProj = this.closestVertexXorgProj;
		}
		if (this.isWithin(eX,eY,oX,oY,this.editDiv.offsetWidth,this.editDiv.offsetHeight)){
		    this.movedMouse=true;
			if (this.mode.indexOf("drawing")==0){
				if (ev.button == mouseLeft){
				    if (this.pointsRemaining > 0){	    
					    if (!this.editPart.isClosed){ 
					        var name = prompt("Enter a name for the point", "");
					        if(name != null && name != '')
					        {	
						        this.editPart.addPoint(mapX,mapY,mapXorgProj,mapYorgProj);
						        if (this.editPart.getNumPoints() != this.editPointIndex){    						       					            
						            //added
					                this.editPointIndex +=1    						        
						        }
						        this.save(name);
						    }
					    }else if (this.editPart.type=="polygonpart"){
						    // add part (hole) to polygon
						    this.editPart = this.editPart.parent.getNewPart();
						    this.editPart.addPoint(mapX,mapY,mapXorgProj,mapYorgProj);
						    this.editPointIndex=1;
					    }
					}else
					{
					    alert("You have allocated all your available points.");					
					}					
				}else if (ev.button == mouseRight){
					this.editPart.close();
					//if (this.editPart.isClosed && this.editPart.type=="polygonpart"){
					if (this.editPart.isClosed){
					    //need to check if still closed, close may fail if constraints violated
						this.onAfterCompleteShape();
					}
				}else if (ev.button == mouseMid){
					this.editPart.removeLastPoint();
					if (this.editPointIndex>0) this.editPointIndex -=1;
				}
				this.redrawShape();
			}else if (this.mode=="selectingEditFeature"){
				this.veRespCallId= vectorEditor.getClosestFeature(this.editshapename,mapX,mapY,this.viewOrigin.x,this.viewOrigin.y,this.viewOrigin.x + (this.viewCellSizeX * this.editDiv.clientWidth),this.viewOrigin.y - (this.viewCellSizeY * this.editDiv.clientHeight),this.viewSRID,getClosestFeatureResult);
			}else if (this.mode=="editingPoints"){
				if (ev.button == mouseLeft){
					// select point
					this.editPointIndex=this.getClosestPoint(mapX,mapY); //also set editPart to closest part
					if (this.editPart.type=="point") {
					    
					}else{
					    this.editPointPrevX=this.editPart.xCoords[this.editPointIndex];
					    this.editPointPrevY=this.editPart.yCoords[this.editPointIndex];
					}					
					this.mode="placingPoint";
				}else if (ev.button == mouseRight){
					// add point
					var index = parseInt(this.getClosestSegment(mapX,mapY)); //also set editPart to closest part
					if (this.editPart.type=="polygonpart") {
						this.editPart.insertPoint(index+1,mapX,mapY,mapXorgProj,mapYorgProj);
						this.editPointIndex = index+1;
					}else if (this.editPart.type=="polyline") {
						if ((index==0) || (index==this.editPart.getNumPoints()-1)){
							if (this.editPart.getNumSegments()>0){
								if (Math.abs(this.getAngleBetween(mapX,mapY,this.editPart.xCoords[index],this.editPart.yCoords[index],this.editPart.xCoords[index+1],this.editPart.yCoords[index+1]))< (Math.PI/2)){
									// angle is acute so insert in segment
									this.editPart.insertPoint(1,mapX,mapY,mapXorgProj,mapYorgProj);
									this.editPointIndex = index+1;
								}else{
									// add new point to start of line
									this.editPart.insertPoint(0,mapX,mapY,mapXorgProj,mapYorgProj);
									this.editPointIndex = index;
								}
							}else{							
								this.editPart.addPoint(mapX,mapY,mapXorgProj,mapYorgProj);
								this.editPointIndex = index;
							}
						}
					}
					this.mode="placingPoint";
				}else if (ev.button == mouseMid){
					// remove point
					if (this.editPart.type!="point") {
					    var index = this.getClosestPoint(mapX,mapY); //also set editPart to closest part
					    this.editPart.removePoint(index);
					}
				}
				this.redrawShape();
			}else if (this.mode=="placingPoint"){
				if (ev.button == mouseLeft){
					// done
					if (this.editPart.type=="polygonpart"){
						if (!checkAllSegIntersectShapeParts(this.editPart,this.editPointIndex) && !checkAllSegIntersectShapeParts(this.editPart,(this.editPointIndex+this.editPart.getNumSegments()-1)%this.editPart.getNumSegments())){
							// check that segments either side of point do not intersect shape
							this.editPart.setPoint(mapX,mapY,mapXorgProj,mapYorgProj);
							this.mode="editingPoints";
						}					
					}else{					    
						this.mode="editingPoints";
					    if (confirm('Update location?')){
                            this.save(null);
                        }
                        else{                            
						    this.mode="placingPoint";
                        }
                        
					}
				}else if (ev.button == mouseRight){
					if (this.editPart.type=="polygonpart"){
						if (!checkAllSegIntersectShapeParts(this.editPart,this.editPointIndex) && !checkAllSegIntersectShapeParts(this.editPart,(this.editPointIndex+this.editPart.getNumSegments()-1)%this.editPart.getNumSegments())){
							// check that segemntts either side of point do not intersect shape
							this.editPart.setPoint(mapX,mapY,mapXorgProj,mapYorgProj);
							this.mode="editingPoints";
						}
					}else{
						this.mode="editingPoints";
					}
				}else if (ev.button == mouseMid){
					//to do undo edit point
					//this.mode="editingPoints";
				}
				this.redrawShape();
			}else if (this.mode=="intersectingFeatures"){
				if (ev.button == mouseLeft){
					if (!this.secondShape.shapeParts[0].isClosed){ 
						this.secondShape.shapeParts[0].addPoint(mapX,mapY);
						if (this.secondShape.shapeParts[0].getNumPoints() != this.editPointIndex){
							//added
						    this.editPointIndex +=1
						}
					}
				}else if (ev.button == mouseRight){
				// may hit button twice?????
				    if (!this.secondShape.shapeParts[0].isClosed){
				       this.secondShape.shapeParts[0].close();
					   this.veRespCallId= vectorEditor.intersectFeatures(this.editShape.tostring(),this.viewSRID,this.secondShape.tostring(),mergeSplitResult);
				    }
					
				}else if (ev.button == mouseMid){
					this.secondShape.shapeParts[0].removeLastPoint();
					if (this.editPointIndex>0) this.editPointIndex -=1;
				}
				this.redrawShape();
			}else if (this.mode=="mergingFeatures"){
				this.veRespCallId= vectorEditor.getClosestFeature(this.editshapename,mapX,mapY,this.viewOrigin.x,this.viewOrigin.y,this.viewOrigin.x + (this.viewCellSizeX * this.editDiv.clientWidth),this.viewOrigin.y - (this.viewCellSizeY * this.editDiv.clientHeight),this.editShapeSRID,selectMergeFeatureResult);
			}else if (this.mode=="deletingPart"){
			    this.getClosestSegment(mapX,mapY); //sets editpart as closest Part
			    this.deleteEditShapePart();
			    this.redrawShape();
			}
		}else{
		    if (this.mode.indexOf("drawing")==0){
		        
		    }   
		}
	}

	this.getClosestSegment = function(mapX,mapY){
		//not correct algorithm needs to be changed
		
		// sets editPart to be closest part and returns index of segment in part that is closest
		var index = parseInt(this.getClosestPoint(mapX,mapY));
		// could be segment either side of this point, is the one with most acute angle
		if (this.editPart.getNumPoints()>2){
			if (this.editPart.type=="polygonpart"){
				var prevIndex =parseInt(-1);
				var nextIndex =parseInt(-1);
				if (index==0){
					prevIndex=this.editPart.getNumPoints()-2; //N.B. final point is same as first if shape closed
					nextIndex=index+1;
				}else if (index==this.editPart.getNumPoints()-1){
					prevIndex = index-1;
					nextIndex = 1; //N.B. final point is same as first if shape closed
				}else{
					prevIndex = index-1;
					nextIndex = index+1;
				}
				var angle = this.getAngleBetween(this.editPart.xCoords[prevIndex],this.editPart.yCoords[prevIndex],this.editPart.xCoords[index],this.editPart.yCoords[index],mapX,mapY);
				angle = this.getAngleBetween(this.editPart.xCoords[nextIndex],this.editPart.yCoords[nextIndex],this.editPart.xCoords[index],this.editPart.yCoords[index],mapX,mapY);
				if (Math.abs(this.getAngleBetween(this.editPart.xCoords[prevIndex],this.editPart.yCoords[prevIndex],this.editPart.xCoords[index],this.editPart.yCoords[index],mapX,mapY)) < Math.abs(this.getAngleBetween(this.editPart.xCoords[nextIndex],this.editPart.yCoords[nextIndex],this.editPart.xCoords[index],this.editPart.yCoords[index],mapX,mapY))){
						return prevIndex;
					}else{
						return index;
					}
			}else{
				//polyline
				if (index==0){
					return 0;
				}else if (index==this.editPart.getNumPoints()-1){
					return index-1;
				}else{
					if (Math.abs(getAngleBetween(this.editPart.xCoords[index-1],this.editPart.yCoords[index-1],this.editPart.xCoords[index],this.editPart.yCoords[index],mapX,mapY)) < Math.abs(getAngleBetween(this.editPart.xCoords[index+1],this.editPart.yCoords[index+1],this.editPart.xCoords[index],this.editPart.yCoords[index],mapX,mapY))){
						return index-1;
					}else{
						return index;
					}
				}
			}
		}else{
			return 0;
		}
		
	}
	
	this.getAngleBetween = function(ax,ay,bx,by,cx,cy){
		//angle between AB and BC
		return Math.atan2(this.vectCrossProdLength(ax,ay,bx,by,cx,cy),this.vectDotProd(ax,ay,bx,by,cx,cy));
	}
	
	this.vectDotProd = function(ax,ay,bx,by,cx,cy){
		// returns AB.BC
		return ((ax-bx) * (cx-bx)) + ((ay-by)*(cy-by));
		
	}
	
	this.vectCrossProdLength = function(ax,ay,bx,by,cx,cy){
		// return length of AB X BC
		return ((ax-bx) * (cy-by)) - ((ay-by)*(cx-bx));
	}
	
	this.getClosestPoint = function(mapX,mapY){
		// sets editPart to be closest part in edit shape (current feature) and returns index of point in part that is closest
		var index =-1;
		var minDist;
		var thisDist;
		for (var i in this.editShape.shapeParts){
			if (this.editShape.shapeParts[i].type=="polygon"){
				for (var j in this.editShape.shapeParts[i].parts){
					for (var k in this.editShape.shapeParts[i].parts[j].xCoords){
						if (index==-1){
							this.editPart = this.editShape.shapeParts[i].parts[j];
							minDist = this.getDistance(this.editShape.shapeParts[i].parts[j].xCoords[k],this.editShape.shapeParts[i].parts[j].yCoords[k],mapX,mapY);
							index = k;
						}else{
							thisDist = this.getDistance(this.editShape.shapeParts[i].parts[j].xCoords[k],this.editShape.shapeParts[i].parts[j].yCoords[k],mapX,mapY);
							if (thisDist < minDist){
								index=k;
								this.editPart = this.editShape.shapeParts[i].parts[j];
								minDist=thisDist;
							}
						}
					}
				}
			}else if (this.editShape.shapeParts[i].type=="polyline"){
				for (var j in this.editShape.shapeParts[i].xCoords){
					if (index==-1){
						this.editPart = this.editShape.shapeParts[i];
						minDist = this.getDistance(this.editShape.shapeParts[i].xCoords[j],this.editShape.shapeParts[i].yCoords[j],mapX,mapY);
						index = j;
					}else{
						thisDist = this.getDistance(this.editShape.shapeParts[i].xCoords[j],this.editShape.shapeParts[i].yCoords[j],mapX,mapY);
						if (thisDist < mindist){
							index=j;
							this.editPart = this.editShape.shapeParts[i];
							minDist=thisDist;
						}
					}
				}
			}else if (this.editShape.shapeParts[i].type=="point"){
			  this.editPart = this.editShape.shapeParts[i]
			}
		}
		return index;
	}
	
	this.getDistance = function(x1,y1,x2,y2){
		return Math.sqrt(((x2-x1)*(x2-x1))+((y2-y1)*(y2-y1)));
	}
			
	this.setColor = function(color){
		jg.setColor(color);
	}
		
	this.selectShapePart = function(index){
		editShapePart = this.editShape.shapeParts[index];
	}
	
	this.deleteShapePart = function(index){
		// check not deleting editing polygon
		// check not deleting first plygon since this is container of hole polygons
		this.editShape.shapeParts.splice(index,1);
	}
	
	this.deleteEditShapePart = function(){
		// check not deleting first polygon since this is container of hole polygons
		for (var i in this.editShape.shapeParts){
		    if (this.editShape.shapeParts[i]==this.editPart){
		        this.editShape.shapeParts.splice(i,1);
		        if (this.editShape.shapeParts.length>0) this.editPart=this.editShape.shapeParts[0].basePart;
		        return;      
		    }
		    if (this.editShape.shapeParts[i].type=="polygon"){
		        if (this.editShape.shapeParts[i].basePart==this.editPart){
		            this.editShape.shapeParts.splice(i,1);
		            if (this.editShape.shapeParts.length>0) this.editPart=this.editShape.shapeParts[0].basePart;
		            return;      
		        }else{
		            for (var j in this.editShape.shapeParts[i].parts){
		                if (this.editShape.shapeParts[i].parts[j]==this.editPart){
		                    this.editShape.shapeParts[i].parts.splice(j,1);
		                    if (this.editShape.shapeParts[i].parts.length==0){
		                        this.editShape.shapeParts.splice(i,1);
		                        if (this.editShape.shapeParts.length>0) this.editPart=this.editShape.shapeParts[0].basePart;
		                    }else{
		                        this.editPart=this.editShape.shapeParts[i].basePart;
		                    }
		                    return;      
		                }
		            }
		        }
		    }
		}
	}
	
	this.convertImageToMapCoordX = function(x){
		var trueX;
		var num;
		if (this.snapMode==0){
			// need to check meridians etc!!!!!!!!!!!!
			trueX = this.viewOrigin.x + (x-0.5)*this.viewCellSizeX;
		}else if (this.snapMode==1){
			trueX = this.viewOrigin.x + (x-0.5)*this.viewCellSizeX;
			num = (trueX-snapOrigin.x)/snapOriginCellSizeX;
			num = round(num);
			trueX = this.snapOrigin.x + (num-1)*this.snapCellSizeX;
		}else if (this.snapMode==2){
			trueX = this.viewOrigin.x + (x-0.5)*this.viewCellSizeX;
			num = (trueX-snapOrigin.x)/snapOriginCellSizeX;
			num = round(num);
			trueX = this.snapOrigin.x + (num-0.5)*this.snapCellSizeX;
		}
		return trueX;
	}
	
	this.convertImageToMapCoordY = function(y){
		var trueY;
		var num;
		if (this.snapMode==0){
			// need to check meridians etc!!!!!!!!!!!!
			trueY = this.viewOrigin.y - (y-0.5)*this.viewCellSizeY;
		}else if (this.snapMode==1){
			trueY = this.viewOrigin.y - (y-0.5)*this.viewCellSizeY;
			num = (trueY-snapOrigin.y)/snapOriginCellSizeY;
			num = round(num);
			trueY = this.snapOrigin.y + (num-1)*this.snapCellSizeY;
		}else if (this.snapMode==2){
			trueY = this.viewOrigin.y - (y-0.5)*this.viewCellSizeY;
			num = (trueY-snapOrigin.y)/snapOriginCellSizeY;
			num = round(num);
			trueY = this.snapOrigin.y + (num-0.5)*this.snapCellSizeY;
		}
		return trueY;
	}
	
	this.markPoint = function(x,y,color){
	    var color_orig = jg.color;
	    this.redrawShape();
	    jg.setColor(color);
	    jg.drawEllipse(this.convertPointX(x)-7,this.convertPointY(y)-7,14,14);
	    jg.paint();
	    
	    jg.setColor(color_orig);
	}
	
	this.markPointInShape = function(x,y,shape,color){
	    var color_orig = jg.color;
	    this.redrawShape();
	    jg.setColor(color);
	    jg.drawEllipse(this.convertPointX(x)-3,this.convertPointY(y)-3,5,5);
	    for (var i in shape.shapeParts){
			if (shape.shapeParts[i].type=="point"){
				if (shape.shapeParts[i].isClosed){
					jg.fillRect(this.convertPointX(shape.shapeParts[i].x)-1,this.convertPointY(shape.shapeParts[i].y)-1,3,3);
				}
			}else if (shape.shapeParts[i].type=="polyline"){
				jg.drawPolyline(this.convertPointsX(shape.shapeParts[i].xCoords),this.convertPointsY(shape.shapeParts[i].yCoords));
			}else if (shape.shapeParts[i].type=="polygon"){
				for (var j in shape.shapeParts[i].parts){
				    if (j==0){
				        //base part of polyogon (border)
				            jg.setStroke(1);
				    }else{
				        jg.setStroke(Stroke.DOTTED);
				    }
					if (shape.shapeParts[i].parts[j].isClosed){
						jg.drawPolygon(this.convertPointsX(shape.shapeParts[i].parts[j].xCoords),this.convertPointsY(shape.shapeParts[i].parts[j].yCoords));
					}else{
						jg.drawPolyline(this.convertPointsX(shape.shapeParts[i].parts[j].xCoords),this.convertPointsY(shape.shapeParts[i].parts[j].yCoords));
					}					
				}
			}
		}
	    jg.paint();
	    jg.setColor(color_orig);
	}
	
	this.markVerticies = function(){
	    for (var i in this.editShape.shapeParts){
	        
            if (this.editShape.shapeParts[i].type=="polyline" ){
                if (this.editShape.shapeParts[i]==this.editPart){
                    for (var j in this.editShape.shapeParts[i].xCoords){
                        jg.fillRect(this.convertPointX(this.editShape.shapeParts[i].xCoords[j])-2,this.convertPointY(this.editShape.shapeParts[i].yCoords[j])-2,5,5);
                    }   
                }
            }else if (this.editShape.shapeParts[i].type=="polygon"){
                for (var j in this.editShape.shapeParts[i].parts){
                    if (this.editShape.shapeParts[i].parts[j]==this.editPart){
                        for (var k in this.editShape.shapeParts[i].parts[j].xCoords){
                            jg.fillRect(this.convertPointX(this.editShape.shapeParts[i].parts[j].xCoords[k])-2,this.convertPointY(this.editShape.shapeParts[i].parts[j].yCoords[k])-2,5,5);
                        }
                    }
                }  
            }else if (this.editShape.shapeParts[i].type=="point"){
                /*if (this.editShape.shapeParts[i]==this.editPart){
                    jg.fillRect(this.convertPointX(this.editShape.shapeParts[i].x)-2,this.convertPointY(this.editShape.shapeParts[i].y)-2,5,5);
                }*/
            }
	        
	    }
	}
		
	this.redrawShape = function(){
		jg.clear();
		if (this.editShape!=null){
		for (var i in this.editShape.shapeParts){
			if (this.editShape.shapeParts[i].type=="point"){
				if (this.editShape.shapeParts[i].isClosed){
					jg.fillEllipse(this.convertPointX(this.editShape.shapeParts[i].x)-7,this.convertPointY(this.editShape.shapeParts[i].y)-7,14,14);
				}
			}else if (this.editShape.shapeParts[i].type=="polyline"){
				jg.drawPolyline(this.convertPointsX(this.editShape.shapeParts[i].xCoords),this.convertPointsY(this.editShape.shapeParts[i].yCoords));
			}else if (this.editShape.shapeParts[i].type=="polygon"){
				for (var j in this.editShape.shapeParts[i].parts){
				    if (j==0){
				        //base part of polyogon (border)
				        if (this.mode=="drawingPolygonHole" && this.editShape.shapeParts[i].parts[j]==this.editPart.parent.basePart){
				            //highlight current base part when drawing hole, so user knows which part hole should be in
				             jg.setStroke(2);
				        }else{
				            jg.setStroke(1);
				        }
				    }else{
				        jg.setStroke(Stroke.DOTTED);
				    }
					if (this.editShape.shapeParts[i].parts[j].isClosed){
						jg.drawPolygon(this.convertPointsX(this.editShape.shapeParts[i].parts[j].xCoords),this.convertPointsY(this.editShape.shapeParts[i].parts[j].yCoords));
					}else{
						jg.drawPolyline(this.convertPointsX(this.editShape.shapeParts[i].parts[j].xCoords),this.convertPointsY(this.editShape.shapeParts[i].parts[j].yCoords));
					}					
				}
			}
		}
		this.markVerticies();
		if (this.mode=="intersectingFeatures"){
			for (var i in this.secondShape.shapeParts){
				if (this.secondShape.shapeParts[i].type=="point"){
					if (this.secondShape.shapeParts[i].isClosed){
						jg.fillRect(this.convertPointX(this.secondShape.shapeParts[i].x)-1,this.convertPointY(this.secondShape.shapeParts[i].y)-1,3,3);
					}
				}else if (this.secondShape.shapeParts[i].type=="polyline"){
					jg.drawPolyline(this.convertPointsX(this.secondShape.shapeParts[i].xCoords),this.convertPointsY(this.secondShape.shapeParts[i].yCoords));
				}else if (this.secondShape.shapeParts[i].type=="polygon"){
					for (var j in this.secondShape.shapeParts[i].parts){
						if (this.secondShape.shapeParts[i].parts[j].isClosed){
							jg.drawPolygon(this.convertPointsX(this.secondShape.shapeParts[i].parts[j].xCoords),this.convertPointsY(this.secondShape.shapeParts[i].parts[j].yCoords));
						}else{
							jg.drawPolyline(this.convertPointsX(this.secondShape.shapeParts[i].parts[j].xCoords),this.convertPointsY(this.secondShape.shapeParts[i].parts[j].yCoords));
						}					
					}
				}
			}
		}
		}
		jg.paint();
	}
	
	this.convertPointX = function(x){
		return Math.ceil(((x-this.viewOrigin.x)/this.viewCellSizeX) + 0.4);
		// the + 0.4 is because depending on snapMode the calculated number of pixels
		// would be 0.5 to 1 less (with rounding errors) than the pixel number indexed from 1
		// there may be round off errors so instead of being 1 less it could be more than one so
		// that is why 0.4 is added first
	}
	
	this.convertPointY = function(y){
		return Math.ceil(((this.viewOrigin.y-y)/this.viewCellSizeY) + 0.4);
	}
	
	this.convertPointsX = function(x){
		var points = new Array();
		for (var i in x){
			points[i]= Math.ceil(((x[i]-this.viewOrigin.x)/this.viewCellSizeX) + 0.4);
		}
		return points;
	}
	
	this.convertPointsY = function(y){
		var points = new Array();
		for (var i in y){
			points[i]= Math.ceil(((this.viewOrigin.y-y[i])/this.viewCellSizeY) + 0.4);
		}
		return points;
	}
	
	this.save = function(featureName){
		//var str = this.editShape.tostring();
		//alert(str);
		//document.getElementById("errorMsg").innerHTML=str;
		if (!this.editPart.isClosed){
		    alert("Current part being edited is not finished or closed");
		}else{
		    this.mode="waitingForSave";
		    if (this.isNewShape){
		        this.veRespCallId= vectorEditor.insertShape(this.editShape.shapename,this.editShape.tostring(),this.viewSRID,this.editRestrictionQuery,updateResult);
		        this.isNewShape=false;
		    }else{
		        if (this.editShape.isNewFeature){
			        this.veRespCallId= vectorEditor.insertFeature(this.editshapename,this.editShape.tostring(),this.viewSRID,this.allowFeatureOverlapping,this.editRestrictionQuery,featureName,updateResult);
		        }else{
			        if (this.secondShape!=null){
			         //if merging or spliting removes other bit
			            if (this.secondShape.isNewFeature){
			                this.secondShape=null;
		                }else{
			                this.veRespCallId= vectorEditor.deleteFeature(this.editshapename,this.secondShape.gid,updateResult);
		                }
			        }
			        this.veRespCallId= vectorEditor.updateFeature(this.editshapename,this.editShape.tostring(),this.viewSRID,this.editShape.gid,this.allowFeatureOverlapping,this.editRestrictionQuery,featureName,updateResult);
		        }
		    }  
		}	
	}
	
	this.del = function(){
		this.mode="waitingForDelete";
		if (this.editShape!=null){
		    if (this.editShape.isNewFeature){
			    this.getNewShape();
		    }else{
			    this.veRespCallId= vectorEditor.deleteFeature(this.editshapename,this.editShape.gid,updateResult);
		    }
		}
		
	}
	
	this.delFeature = function(gid){
		this.mode="waitingForDelete";    
		this.veRespCallId= vectorEditor.deleteFeature(this.editshapename,gid,updateResult);
		
	}
	
	this.deleteShape = function(){
		this.mode="waitingForDelete";
		if (!this.isNewShape){
			this.veRespCallId= vectorEditor.deleteShape(this.editshapename,updateResult);
		}
		
	}
	
	this.openConnection = function(){
	    this.veRespCallId= vectorEditor.openConnection(noAction);
	}
	
	this.getEditShapeArea = function(callbackFunc){
		this.veRespCallId= vectorEditor.measureFeatureArea(this.editShape.tostring(),this.editShape.gid,callbackFunc);
	}
	
	this.setClosestVertex = function(point,shp){
	    this.closestVertexX=point.x;
	    this.closestVertexY=point.y;
	    if (this.mode=="placingPoint" || (this.mode.indexOf("drawing")==0 && !this.editPart.isClosed)){
	        this.editPart.xCoords[this.editPointIndex]= point.x;
		    this.editPart.yCoords[this.editPointIndex]= point.y;
	        //this.markPoint(point.x,point.y,"red");
	        this.markPointInShape(point.x,point.y,shp,"red");
	    }
	    this.keyUp(); //trigger keyup so event can repeat
	}
	
	this.setClosestSegment = function(x1,y1,x2,y2){
	    var color_orig = jg.color;
	    var orig_stroke = jg.stroke;
	    var x;
	    var y;
	    if (this.editPointIndex==0){
	        //get closest point
	        
	    }else{
	        //intersect
	    }
	    this.editPart.xCoords[this.editPointIndex]= x;
		this.editPart.yCoords[this.editPointIndex]= y;
	    this.redrawShape();
	    jg.setColor("red");
	    jg.setStroke(2);
	    jg.drawLine(this.convertPointX(x1),this.convertPointY(y1),this.convertPointX(x2),this.convertPointY(y2));
	    jg.paint();
	    jg.setStroke(orig_stroke);
	    jg.setColor(color_orig);
	    this.keyUp();
	}

}

function VEresults(result)
//return feature from DB result
{	
	//show result
	//alert(result.value.type);
	thisSE.editShape = new Shape();
	thisSE.editShape.shapetype=result.typeStr;
	thisSE.editShape.shapeAstext=result.geom;
	thisSE.editShape.fromstring(result.geom);
	thisSE.editShape.gid = result.gid;
	thisSE.editShape.SRID = result.SRID;
	thisSE.editShape.shapename = result.shapename;
	thisSE.editShape.isNewFeature=false;
	thisSE.editPart = thisSE.editShape.shapeParts[0];
	thisSE.setMode("editingPoints");
	thisSE.redrawShape();
}
	
function noAction(result)
    //no action
{	

}
	
function getClosestFeatureResult(result)
    //return feature from DB result
{	
	//if there is an error,
	if (!result){			
		alert("error " + result.msg);
		// Add code to handle specific error codes here
	}
	//if there was no error
	else 
	{
	    thisSE.isNewShape =false;
	    thisSE.editShape = new Shape();
	    thisSE.editShape.shapetype=result.shp.typeStr;
	    thisSE.editShape.shapeAstext=result.shp.geom;
	    thisSE.editShape.fromstring(result.shp.geom);
	    thisSE.editShape.gid = result.shp.gid;
	    thisSE.editShape.SRID = result.shp.SRID;
	    thisSE.editShape.shapename = result.shp.shapename;
	    thisSE.editShape.isNewFeature=false;
	    thisSE.getClosestSegment(result.searchPoint.x,result.searchPoint.y); //sets editpart as closest Part
	    thisSE.redrawShape();
        thisSE.onAfterGetFeature();
	}
}
	
/*function VEresults2(result)
	{	
		//if there is an error,
		//flashLoading(false,"");
		if (result.error)
		{
			//Pull the error information from the event.result.errorDetail properties
			var xfaultcode = result.errorDetail.code;
			var xfaultstring = result.errorDetail.string;
			var xfaultsoap = result.errorDetail.raw;
			alert("error " + xfaultstring);
			// Add code to handle specific error codes here
		}
		//if there was no error
		else 
		{
			//show result
			//alert(result.value);
			if (result.value){
				//polygon is valid
			}else{
				//remove
				thisSE.editPart.parent.parts.pop();
				thisSE.redrawShape();
			}
		}
	}*/
	
	
function updateResult(result)
{	
	//if there is an error,
	if (!result.success){			
		alert("error " + result.msg);
		// Add code to handle specific error codes here
	}
	//if there was no error
	else 
	{
	    thisSE.onAfterUpdate(result);		
	}
	if (thisSE.mode=="waitingForSave"){
	    thisSE.mode="noneAfterUpdate";
	}
}
	
function getClosestVertexResult(result)
{	
	//if there is an error,
	if (!result.success){			
		alert("error " + result.msg);
		// Add code to handle specific error codes here
	}
	//if there was no error
	else 
	{
        var s = new Shape();
        s.fromstring(result.value.shape.geom);
        thisSE.setClosestVertex(result.value.point,result.value.pointOrgProj,s);	
	}
}

function getClosestSegmentResult(result)
{	
	//if there is an error,
	if (!result.success){			
		alert("error " + result.msg);
		// Add code to handle specific error codes here
	}
	//if there was no error
	else 
	{
		//show result
		thisSE.setClosestSegment(result.value.p0.x,result.value.p0.y,result.value.p1.x,result.value.p1.y);
		//alert(result.value);
	}
}

function selectMergeFeatureResult(result)
{	
	//if there is an error,
	if (!result.success){			
		alert("error " + result.msg);
		// Add code to handle specific error codes here
	}
	//if there was no error
	else 
	{		
	    thisSE.secondShape = new Shape();
	    thisSE.secondShape.shapetype=result.value.shp.typeStr;
	    thisSE.secondShape.shapeAstext=result.value.shp.geom;
	    thisSE.secondShape.fromstring(result.value.shp.geom);
	    thisSE.secondShape.gid = result.value.shp.gid;
	    thisSE.secondShape.SRID = result.value.shp.SRID;
	    thisSE.secondShape.shapename = result.value.shp.shapename;
	    thisSE.secondShape.isNewFeature=false;
	    veRespCallId7= vectorEditor.mergeFeatures(thisSE.editShape.tostring(),thisSE.editShape.SRID,thisSE.secondShape.tostring(),thisSE.allowMergeOnlyIntersecting,mergeSplitResult);
	}
}

function mergeSplitResult(result)
{	
	//if there is an error,
	if (!result.success){			
		alert("error " + result.msg);
		// Add code to handle specific error codes here
	}
	//if there was no error
	else 
	{
	    thisSE.editShape.shapetype=result.value.shp.typeStr;
	    thisSE.editShape.shapeAstext=result.value.shp.geom;
	    thisSE.editShape.fromstring(result.value.shp.geom);
	    thisSE.mode="mergeSplitDisplayResult";
	    thisSE.redrawShape();		
	}
}

function getShapeInfoResult(result)
{	
	//if there is an error,
	if (!result.success){			
		alert("error " + result.msg);
		// Add code to handle specific error codes here
	}
	//if there was no error
	else 
	{   //show result
		//alert(result.value.type);
		if (result.value!=null){
			thisSE.editShapeType=result.value.shapetype;
			thisSE.editShapeSRID=parseInt(result.value.SRID);
			thisSE.editShape= new Shape();
			thisSE.mode="none";
		}
		
	}
}

function tokenSplit(str)
{
    var i=0;
    var s=0;
    var parts = new Array();
    var numParts = 0;
    var tokenDelimCount=0;
    while (i < str.length){
	    if (str.charAt(i)=="("){
		    tokenDelimCount++;
	    }else if (str.charAt(i)==")"){
		    tokenDelimCount--;
	    }else if (str.charAt(i)==","){
		    if (tokenDelimCount==0){
			    parts[numParts]=str.substr(s,i-s);
			    numParts++;
			    s = i+1;
		    }
	    }
	    i++;
    }
    if (tokenDelimCount==0){
	    //i--;
	    parts[numParts]=str.substr(s,i-s);
    }
    return parts;
}

function checkAllSegIntersectShapeParts(shapePartWithSeg,segIndex){
	// checks all polygon parts in shape to see if the segment of this shape intersects any
	for (var i in thisSE.editShape.shapeParts){
		for (var j in thisSE.editShape.shapeParts[i].parts){
			if (doesSegIntersectShape(shapePartWithSeg,segIndex, thisSE.editShape.shapeParts[i].parts[j])) return true;
		}
	}
	return false;
}

function doesSegIntersectShape(shapeWithSeg,segmentIndex,shapeToCheck){
	// do any segments of this shape intersect with segment with index segIndex
	// can improve speed by checking minimum  enclosing rectangle first or using monotonic sections
	//var numSegments = this.getNumSegments();
	var segIndex = parseInt(segmentIndex);
	if ((shapeWithSeg==shapeToCheck) && (shapeToCheck.getNumSegments() < 2)) return false;
	var u1 = shapeWithSeg.xCoords[segIndex];
	var u2 = shapeWithSeg.xCoords[segIndex+1];
	var v1 = shapeWithSeg.yCoords[segIndex];
	var v2 = shapeWithSeg.yCoords[segIndex+1];
	var x1,x2,y1,y2;
	var b1,a1,xi,yi;
	for (var i=0;i<=(shapeToCheck.getNumSegments());i++){
		
		if  ((shapeWithSeg==shapeToCheck) && (((i==segIndex+1) || (i == segIndex-1) || (shapeToCheck.isClosed && ((i==(segIndex+shapeToCheck.getNumSegments()-1)%shapeToCheck.getNumSegments()) || (i==(segIndex+1)%shapeToCheck.getNumSegments())))))){
			// if i is previous or next segment to one being checked, then they touch at vertex of feature and we need to
			// only check if colinear and slope is opposite direction to overlap
			b2 = (v2-v1)/(u2-u1);
			x1 = shapeToCheck.xCoords[i];
			x2 = shapeToCheck.xCoords[i+1];
			y1 = shapeToCheck.yCoords[i];
			y2 = shapeToCheck.yCoords[i+1];
			b1 = (y2-y1)/(x2-x1);
			if (b1==-b2) return true;
		}else if ((shapeWithSeg!=shapeToCheck) || (i!=segIndex)){ //if ... not necessary
			x1 = shapeToCheck.xCoords[i];
			x2 = shapeToCheck.xCoords[i+1];
			y1 = shapeToCheck.yCoords[i];
			y2 = shapeToCheck.yCoords[i+1];
			// SHOULD check bounding box first (for speed): if ((((yi>y1) && (yi<y2)) || ((yi>y2) && (yi <y1)))  &&  (((u1 < x1) && (u2 > x1)) ||  ((u1 > x1) && (u2 < x1))))
			if (x2==x1){
				// line is vertical
				if (u2==u1){
					// other line is vertical too
					if (u2==x1){
					    //if colinear
						if  ( ( ((v2<y2) && (v2>y1)) || ((v2>y2) && (v2<y1)) ) || (  ((v1<y2) && (v1>y1)) || ((v1>y2) && (v1<y1)) ) ) return true; //if overlap
					}
				}else{
				    //not parallel, current seg vertical
					b2 = (v2-v1)/(u2-u1);
					a2=v1-b2*u1;
					yi=a2+b2*x1; // height of intersection point
					if (((yi>y1) && (yi<y2)) || ((yi>y2) && (yi <y1))){ //if would intersect within height
					    //line would cross line within height if long enough (that is if x range overlaps)
					    if (((u1 < x1) && (u2 > x1)) ||  ((u1 > x1) && (u2 < x1))) return true;
					} 
				}
			}else{
				if (u2==u1){
					//line is vertical
					// other line was not vertical
					b1 = (y2-y1)/(x2-x1);
					a1=y1-b1*x1;
					yi=a1+b1*u1;
					if (((yi>v1) && (yi<v2)) || ((yi>v2) && (yi <v1))){
					    //line would cross line within its height if long enough (that is if x range overlaps)
					    if (((x1 < u1) && (x2 > u1)) ||  ((x1 > u1) && (x2 < u1))) return true;
					} 
				}else{
					b1 = (y2-y1)/(x2-x1);
					b2 = (v2-v1)/(u2-u1);
					a1=y1-b1*x1;
					a2=v1-b2*u1;
					if ((b1-b2)!=0){
						// lines are not parallel	
						// maybe should check Math.Abs(b1-b2)>= SMALL_NUM instead due to rounding errors?
						xi = -(a1-a2)/(b1-b2);
						yi = a1+b1*xi;
						if (((x1-xi)*(xi-x2)>=0) && ((u1-xi)*(xi-u2)>=0) && ((y1-yi)*(yi-y2)>=0) && ((v1-yi)*(yi-v2)>=0)){
						    
						    // check if coincide at vertex only
						    if (!((xi==u1 && yi==v1) || (xi==u2 && yi==v2))){
						        //in case of roundoff error check that does not coincide at actual end point
						        if (!((x1==u1 && y1==v1) || (x1==u2 && y1==v2) || (x2==u1 && y2==v1) || (x2==u2 && y2==v2))) return true;
						    } 
						}
					}else{
						//if parallel only need to check if colinear then check x range overlaps
						if (a1 == a2){
							//colinear
							if  ( ( ((u2<x2) && (u2>x1)) || ((u2>x2) && (u2<x1)) ) || (  ((u1<x2) && (u1>x1)) || ((u1>x2) && (u1<x1)) ) ) return true;
						}
					}
				}
			}
		}
	}		
	return false;
}

