Back to Home

Thuật toán rút gọn số điểm trên đường polyline

Phát biểu bài toán

Cho đường polyline C được tạo bởi tập hợp các đoạn thẳng nối tiếp nhau của một tập hợp điểm. Cho trước một độ error rate nhất định, rút gọn số lượng điểm của tập hợp này sao cho vẫn giữ được hình dạng của đường C ban đầu.

Thuật toán này còn được biết đến với tên là Ramer–Douglas–Peucker.Thuật toán được phát triển độc lập bởi Urs Ramer vào năm 1972 và bởi David DouglasThomas Peucker vào năm 1973.

Thuật toán

Cho đường CC dưới dạng tập hợp của nn điểm sắp xếp thứ tự: C=(P1,P2,,Pn)C=(P_1,P_2,\dots,P_n) và một giá trị độ sai δ>0\delta > 0.

Ta định nghĩa hàm fReduce(points,start,end,δ)fReduce(points, start,end,\delta)là hàm lược bỏ các điểm trong tập điểm pointspoints từ index startstart đến endend như sau:

  • Cho ii lần lượt nhận giá trị start+1start + 1 từ đến end1end-1:

  • Tính di=d(Pi,PstartPend)d_i = d(P_i, \overline{P_{start}P_{end}}), với:

    • PstartPend\overline{P_{start}P_{end}} là đoạn thẳng nối PstartP_{start}PendP_{end}

    • Hàm d(Pi,PiPj)d(P_i, \overline{P_iP_j}) là hàm đo khoảng cách từ điểm PiP_i đến PiPj\overline{P_iP_j}

  • Tìm dmax=maxd(Pi,PstartPend)d_{max} = \max d(P_i,\overline{P_{start}P_{end}})

  • Nếu dmaxδd_{max} \le \delta: tất cả các điểm từ đến đều có thể lược bỏ

  • Nếu dmax>δd_{max} > \delta tại index thứ kk, ta chia đoạn thẳng thành 2 phần để thực hiện gọi đệ quy như sau:

    • fReduce(points,start,k,δ)fReduce(points, start,k,\delta)

    • fReduce(points,k,end,δ)fReduce(points, k, end,\delta)

Minh họa

Source Code

// pointIndices là mảng để lưu trữ các index được giữ lại sau quá trình thu gọn
function fReduce(points, firstPoint, lastPoint, tolerance, pointIndices){
    let maxD = 0;
    let indexFurthest = 0;

    for (let i=firstPoint + 1; i< lastPoint - 1; i++){
        let distance = dPointLine(points[i], points[firstPoint], points[lastPoint]);
        if (distance > maxD){
            maxD = distance;
            // lưu index của điểm xa nhất 
            indexFurthest = i;
        }
    }

    // tolerance là giá trị delta
    if ((maxD > tolerance) && (indexFurthest != 0)){
        pointIndices.push(indexFurthest);
        fReduce(points, firstPoint, indexFurthest, tolerance, pointIndices)
        fReduce(points, indexFurthest, lastPoint, tolerance, pointIndices)
    }
}

Tính khoảng cách từ một điểm đến đường thẳng

Khoảng cách từ điểm AA đến đoạn BCBC là chiều dài đoạn AHAH

Ta có: SABC=AH×BC2    AH=2×SABCBC\displaystyle S_{ABC} = \frac {AH \times BC} {2} \implies AH = \frac {2 \times S_{ABC}} {BC}

Tuy nhiên, SABCS_{ABC} có thể được tính bởi công thức cross product của vector AB\overrightarrow{AB}AC\overrightarrow{AC} như sau:

2×SABC=ABACsinθ=AB×AC    AH=AB×AC2\displaystyle 2 \times S_{ABC} = |\overrightarrow{AB}||\overrightarrow{AC}||\sin \theta|=|\overrightarrow{AB} \times \overrightarrow{AC}| \implies AH = \frac {|\overrightarrow{AB} \times \overrightarrow{AC}|} 2

Với θ\theta là góc tạo bởi vector AB,AC\overrightarrow{AB}, \overrightarrow{AC}

Source code

function dPointLine(p1, p2, p3){
    let area = Math.abs(0.5 * (p2.x * p3.y + p3.x * p1.y + p1.x * p2.y - p3.x * p2.y - p1.x * p3.y - p2.x * p1.y))    

    let bottom = p2.dist(p3);
    let height = area / bottom * 2.0;

    return height;
}

Comments

0/300

Leave name/email blank to comment anonymously

No comments yet. Be the first to comment!