【原創】判斷點在多邊形內



一、應用場景:地圖應用中判斷一個位置是否在一個區域內。我曾經應用在百度地圖上,代碼為js實現。據我了解,目前百度地圖api已經提供該功能。
二、概要:
1、行政區划邊界是多邊形;
2、多邊形分為凸多邊形和凹多邊形;
3、應用:產生隨機數據(即一個平面坐標)在制定的行政區划邊界以內(即多邊形內),在正式情況下不需要從圖形的層面處理數據,數據本身就有在那個區划下的屬性。

三、假定:
1、多邊形的點都是不重合的點;
2、給定的多邊形邊界坐標集就是時針順序的,即要么符合逆時針順序要么符合順時針順序,不存在這種情況:
第一個點是時鍾上1的位置,第二個點是時鍾上3的位置,第三個點回到1和3之間的位置,如2.如果多邊形上包含2的位置,那么給點的坐標集應該是1、23,而不應該是1、32.
四、原理:
1、對於凸多邊形來說,一個點與多邊形邊界所有連續的兩點構成的夾角的和為360度,則說明該點在多邊形內。如果夾角和不是360度,則該點一定不在多邊形內。
 
如圖,A與多邊形上的點構成的夾角是360°說明A在多邊形內。
 
如圖,點A與多邊形上的點構成的夾角不是360°,說明A不在多邊形內。
2、對於凹多邊形來說,將凹的部分可能重復計算夾角的地方調整坐標在集合中的順序后計算夾角。
 
在按照原理1會計算∠BAC加上∠CAD的值,如圖,實際上∠CAD是是∠BAC的一部分,所以這里要么累加時不要包含∠CAD的值;要么調整CD兩點的位置,利用原理1,計算∠BAD加上∠DAC(調整位置后就是∠CAD)。問題是,什么時候需要調整CD的位置,我們發現在原理1中,在多邊形內的點與多邊形上的點的連線與多邊形邊界沒有交點,而該圖有交點。
所以若第一個點A與地圖上出去BC兩點的任意一點D的連線,若與BC兩點的連線存在交點,那么這是需要調整任意一點D與B的位置(或C的位置)。這個過程是對坐標集排序的過程。之后利用原理1即可。

五、代碼實現(javascript):
我的博客園相關代碼訪問地址: http://www.cnblogs.com/langu/p/3458846.html
http://www.cnblogs.com/langu/p/3458835.html

1、兩個點間的距離
獲取坐標軸內兩個點間的距離
function getLength(point1_x, point1_y, point2_x, point2_y)
{
    var diff_x = Math.abs(point2_x - point1_x);
    var diff_y = Math.abs(point2_y - point1_y);

    var length_pow = Math.pow(diff_x, 2) + Math.pow(diff_y, 2);//兩個點在 橫縱坐標的差值與兩點間的直線 構成直角三角形。length_pow等於該距離的平方

    return Math.sqrt(length_pow);
}
2、獲得三個點構成的三角形的 第一個點所在的角度的余弦值
function getCos(point1_x, point1_y, point2_x, point2_y, point3_x, point3_y)
{
    var length1_2 = getLength(point1_x, point1_y, point2_x, point2_y);//獲取第一個點與第2個點的距離
    var length1_3 = getLength(point1_x, point1_y, point3_x, point3_y);
    var length2_3 = getLength(point2_x, point2_y, point3_x, point3_y);

    var res = (Math.pow(length1_2, 2) + Math.pow(length1_3, 2) - Math.pow(length2_3, 2)) / (length1_2 * length1_3 * 2);//cosA=(pow(b,2)+pow(c,2)-pow(a,2))/2*b*c

    return res;
}

var pi180 = 180 / Math.PI;

3、將余弦值轉換為角度
//獲取3個點的夾角,角頂點為第一個點
function getAngle(point1_x, point1_y, point2_x, point2_y, point3_x, point3_y)
{
    var _cos1 = getCos(point1_x, point1_y, point2_x, point2_y, point3_x, point3_y);//第一個點為頂點的角的角度的余弦值

    return Math.acos(_cos1) * pi180;
}


4、已知4個點的坐標,求兩個線段的交點坐標。對應原理2中的ABCD四個點。
//判斷第一個點 與 第四個點所連直線 與 第2個點和第3個點 所連直線的交點 是否在 第2個和第3個點的線段上
function getCrossPoint(point1, point2, point3, point4)
{
    var pD_x = point1.split(',')[0];
    var pD_y = point1.split(',')[1];
    var pA_x = point2.split(',')[0];
    var pA_y = point2.split(',')[1];

    var pC_x = point3.split(',')[0];
    var pC_y = point3.split(',')[1];
    var pB_x = point4.split(',')[0];
    var pB_y = point4.split(',')[1]; 

    var k_y = (pB_x * pC_y * pD_y - pD_x * pB_y * pC_y - pA_y * pB_x * pD_y + pD_x * pB_y * pA_y + pC_x * pA_y * pD_y - pA_x * pC_y * pD_y - pC_x * pB_y * pA_y + pA_x * pB_y * pC_y) /
        (pD_y * pC_x - pA_x * pD_y - pB_y * pC_x + pA_x * pB_y + pB_x * pC_y - pD_x * pC_y - pA_y * pB_x + pA_y * pD_x);

    var k_x = (pD_y * (pC_x - pA_x) * (pB_x - pD_x) - pA_y * (pC_x - pA_x) * (pB_x - pD_x) + pA_x * (pC_y - pA_y) * (pB_x - pD_x) + pD_x * (pD_y - pB_y) * (pC_x - pA_x)) /
        ((pC_y - pA_y) * (pB_x - pD_x) + (pD_y - pB_y) * (pC_x - pA_x));

    return k_x + ',' + k_y;
}

5、兩條線的交點,不一定在線段上,有可能在線段的延長線上,所以需要判斷交點是否在線段上
//判斷 隨機產生的點 和點X 的連線  與地圖上已知的兩點(給出的坐標中連續的兩點)之間的連線  的 交點 是否在 已知的兩點的線段之上  如果在之上:那么點X的順序應該調整到已知的兩點中間
function CrosspointIsOnTheLine(point1, point2, point3, point4)
{

    var crossPoint = getCrossPoint(point1, point2, point3, point4);//交點坐標

    var result = {
        online: false,//是否在線段上
        nearP2: undefined//如果不在線段上,是否近p2,否則近p3
    };

    //交點分別到 第2點 和 第3點 之間的距離 等於 第2點到第3點的距離  那么 交點在線段上

    var cp_xy = crossPoint.split(',');

    var p2_xy = point2.split(',');
    var p3_xy = point3.split(',');

    var cp_p2_length = getLength(cp_xy[0], cp_xy[1], p2_xy[0], p2_xy[1]);//交點到p2的距離
    var cp_p3_length = getLength(cp_xy[0], cp_xy[1], p3_xy[0], p3_xy[1]);//交點到p3的距離
    var p2_p3_length = getLength(p2_xy[0], p2_xy[1], p3_xy[0], p3_xy[1]);//p2到p3的距離

    //由於線段的長度本身存在精度的問題  ,所以這么比較 交點到 p2和p3的距離 同時小於 p2到p3的距離 說明 交點在 p2到p3的線段上
    if (cp_p2_length <= p2_p3_length && cp_p3_length <= p2_p3_length) {
        result.online = true; 
    }
    else {
        //if (cp_p2_length > p2_p3_length) {
        //    result.nearP2 = false;
        //}
        //else if (cp_p3_length > p2_p3_length) {
        //    result.nearP2 = true;
        //}
    }

    return result;

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM