js判断多边形的坐标点是顺时针还是逆时针的两种方法
由于arcgis对顺时针生成的多边形面积计算为正,逆时针生成的为负,所以产生了需要判断多边形坐标是顺时针还是逆时针的需求。
关键算法
首先我们可以通过三点构成的两个矢量的叉乘结果 k 来判断这三点的走向是顺时针还是逆时针的。
如果k<0,则为顺时针,
如果k>0,则为逆时针,
如果k=0,则平行。
当多边形是凸多边形时,各个点均满足这个规律,但如果是凹多边形,在凹边处就会存在特殊情况。
通过极值点与其相邻点的构成的矢量走向算出多边形走向
因此我们需要找到一个绝对为凸边的点,通过这个点与相邻两点所构成的两个矢量的走向就可以反映整个多边形的走向了。
显然这个点可以是多边形中的极值点,即x最大或x最小,或y最大或y最小的点,这就是第一种方法。
// 三个点可以判断矢量是顺时针旋转还是逆时针旋转的,但由于可能存在凹边,所以并不是任意三点都可以正确反映多边形的走向
// 因此需要取多边形中绝对是凸边的点来判断,
// 多边形中的极值点(x最大或x最小或y最大或y最小)它与相邻两点构成的边必然是凸边,因此我们先取出多边形中的极值点,再由极值点和其前后两点去判断矢量的走向,从而判断出多边形的走向。
function isCoordShun2(c_str){
c_str = c_str.replace("\r\n"," ");
var coords = c_str.split(" ");
if(coords.length<3){
return null;
}
if(coords[0] == coords[coords.length-1]){
coords = coords.slice(0,coords.length-1);
}
coords = coords.reverse();
var maxXIndex = 0;
var maxX = parseFloat(coords[maxXIndex].split(",")[0]);
var c1,c2,c3;
var x1,y1,x2,y2,x3,y3;
for(var i=0;i<coords.length;i++){
if(parseFloat(coords[i].split(",")[0])>maxX){
maxX = parseFloat(coords[i].split(",")[0]);
maxXIndex = i;
}
}
if(maxXIndex==0){
c1 = coords[coords.length-1];
c2 = coords[maxXIndex];
c3 = coords[maxXIndex+1];
}else if(maxXIndex==coords.length-1){
c1 = coords[maxXIndex-1];
c2 = coords[maxXIndex];
c3 = coords[0];
}else {
c1 = coords[maxXIndex-1];
c2 = coords[maxXIndex];
c3 = coords[maxXIndex+1];
}
x1 = parseFloat(c1.split(",")[0]);
y1 = parseFloat(c1.split(",")[1]);
x2 = parseFloat(c2.split(",")[0]);
y2 = parseFloat(c2.split(",")[1]);
x3 = parseFloat(c3.split(",")[0]);
y3 = parseFloat(c3.split(",")[1]);
var s = ((x1-x3)*(y2-y3)-(x2-x3)*(y1-y3));
return s<0;
}
通过计算各左边点所在矢量夹角的角度总和来推算多边形走向
我们知道多边形的内角和满足 (n-2)*180,因此我们可以试着计算出位于矢量右侧(即顺时针的一侧)的所成夹角的角度之和m。
如果多边形坐标点是顺时针绘制的,m约等于内角和公式;
如果是逆时针绘制的,m约等于n*180+360。
// 多边形的点两两构成一个矢量,每两个矢量构成一个角度,其右侧的角度为矢量顺时针方向的角度,
// 我们通过计算所有的矢量两两构成的这个顺时针角度的和是否等于多边形内角和(n-2)*180,
// 如果等于,则说明矢量的顺时针方向角是多边形内角,说明矢量前进方向是顺时针,
// 否则,则是逆时针,此时计算出来的角度和应为n*180+多边形外交和360。
// 计算过程存在小数点误差,因此判断相等时使用一个范围。
function isCoordShun1(c_str){
c_str = c_str.replace("\r\n"," ");
var coords = c_str.split(" ");
if(coords.length<3){
return null;
}
if(coords[0] == coords[coords.length-1]){
coords = coords.slice(0,coords.length-1);
}
//角度和
var angSum = 0;
for(var i=0;i<coords.length;i++){
var c1,c2,c3;
if(i==0){
c1 = coords[coords.length-1];
c2 = coords[i];
c3 = coords[i+1];
}else if(i==coords.length-1){
c1 = coords[i-1];
c2 = coords[i];
c3 = coords[0];
}else {
c1 = coords[i-1];
c2 = coords[i];
c3 = coords[i+1];
}
var x1,y1,x2,y2,x3,y3;
x1 = parseFloat(c1.split(",")[0]);
y1 = parseFloat(c1.split(",")[1]);
x2 = parseFloat(c2.split(",")[0]);
y2 = parseFloat(c2.split(",")[1]);
x3 = parseFloat(c3.split(",")[0]);
y3 = parseFloat(c3.split(",")[1]);
var angRight = getCoordAngRight(x1,y1,x2,y2,x3,y3);
angSum += angRight;
}
var isShunshizhen = Math.abs(angSum - (coords.length-2)*180);
//涉及到平方和开方计算,因此结果与理论值会有一点偏差,所以使用一个容差值
return isShunshizhen < coords.length;
}
// 计算两矢量所成的右侧角
function getCoordAngRight(x1,y1,x2,y2,x3,y3){
// 要先判断是左转还是右转,如果是右转,右侧角=夹角,如果是左转,右侧角=360-夹角
var s = ((x1-x3)*(y2-y3)-(x2-x3)*(y1-y3));
var len12 = Math.sqrt(Math.pow(x1-x2,2)+Math.pow(y1-y2,2));
var len23 = Math.sqrt(Math.pow(x2-x3,2)+Math.pow(y2-y3,2));
var len13 = Math.sqrt(Math.pow(x1-x3,2)+Math.pow(y1-y3,2));
var cos2 = (Math.pow(len23,2)+Math.pow(len12,2)-Math.pow(len13,2))/(2*len12*len23);
var angle2 = Math.round( Math.acos(cos2)*180/Math.PI );
if(s<0){
//顺时针
return angle2;
}else if(s>0){
//逆时针
return 360-angle2;
}else{
//平行
return 360;
}
}