Boost::Geometryでmultilinestringをclipping

図形の上に引かれた線を、3*3などで矩形分割する必要があったため調査メモ。

環境
  • OSX 10.11.3
  • xcode 7.2.1
  • boost 1.60
  • clang 3.5.0
サンプルコード
#include <iostream>
#include <vector>

#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/geometries/multi_linestring.hpp>
#include <boost/geometry/geometries/point_xy.hpp>

int main(int argc, const char * argv[]) {
    namespace bg = boost::geometry;

    using point_d2 = bg::model::d2::point_xy<double>;
    using linestring_d2 = bg::model::linestring<point_d2>;

    linestring_d2 ls;
    ls.push_back(bg::make<point_d2>(1.1,1.1));

    point_d2 lp;
    bg::assign_values(lp, 2.5, 2.1);
    bg::append(ls, lp);

    // print 2 points "((1.1, 1.1), (2.5, 2.1))"
    std::cout << bg::dsv(ls) << std::endl;
    // sqrt{(2.5-1.1)^2 + (2.1-1.1)^2} = 1.72…
    std::cout << "distance " << bg::distance(ls.at(0), ls.at(1)) << std::endl;

    // boxは境界点を持ちlinestringとの計算に使える
    using box_d2 = bg::model::box<point_d2>;
    box_d2 b;
    bg::envelope(ls, b);
    std::cout << bg::dsv(b) << std::endl;

    // distance equal to length
    std::cout << "length " << bg::length(ls) << std::endl;

    std::cout << "number of points 1: " << ls.size() << std::endl;
    std::cout << "number of points 2: " << boost::size(ls) << std::endl;
    std::cout << "number of points 3: " << bg::num_points(ls) << std::endl;

    // 点から線への垂線の長さ
    point_d2 p(1.9, 1.2);
    // 0.38
    std::cout << "distance of " << bg::dsv(p)
    << " to line: " << bg::distance(p, ls) << std::endl;

    // linestringはsegmentと見做して計算することもできる
    double d = bg::distance(p, bg::model::segment<point_d2>(ls.front(), ls.back()));
    std::cout << "distance " << d << std::endl;

    // linestringは平滑化できる
    linestring_d2 ls_redundancied = {
        {3,3},
        {3.8, 4},
        {6,6},
        {4,9},
        {5,8},
        {7,7}
    };

    linestring_d2 ls_simplified;
    bg::simplify(ls_redundancied, ls_simplified, 0.5);
    std::cout << "not simplified :" << bg::dsv(ls_redundancied) << std::endl;
    // removed (3.8, 4) and (5,8)
    std::cout << "simplified :" << bg::dsv(ls_simplified) << std::endl;


    //
    // linestringのclipping
    //
    box_d2 cb(point_d2(1.2, 1.2), point_d2(4.5, 2.5));
    std::vector<linestring_d2> clipped;

    ls.push_back(point_d2(1.3,1.3));
    // 矩形, input, output
    bg::intersection(cb, ls, clipped);

    std::cout << "before clipped : " << bg::wkt(ls) << std::endl;
    for(const auto &v : clipped) {
        // 複数の線ができるが、multilinestringとしては解釈してもらえないのでfor文で確認
        std::cout << bg::wkt(v) << std::endl;
    }

    //
    // multilinestringのclipping
    //
    using multilinestring = boost::geometry::model::multi_linestring<linestring_d2>;
    multilinestring mls;

    linestring_d2 ls_1 = {
        {1,9},
        {2.8, 7},
        {3,6},
        {4,5},
        {5,4},
        {6,2}
    };
    mls.push_back(ls_1);
    linestring_d2 ls_2 = {
        {1,2},
        {5.8, 3},
        {5.9,4},
        {6.0,5},
        {6.1,6},
        {9,11}
    };
    mls.push_back(ls_2);

    box_d2 cb_2(point_d2(2,3), point_d2(6, 9));
    multilinestring mls_clipped;
    bg::intersection(cb_2, mls, mls_clipped);
    std::cout << "before mls(A)   : " << bg::wkt(mls) << std::endl;
    std::cout << "clipping box(B) : " << bg::wkt(cb_2) << std::endl;
    std::cout << "after mls(C)    : " << bg::wkt(mls_clipped) << std::endl;

    //
    // multi_pointのclipping
    //
    using bg_multipoint = boost::geometry::model::multi_point<point_d2>;
    bg_multipoint mp;
    mp.push_back(point_d2(1.4, 1.4));
    mp.push_back(point_d2(2.0, 2.0));
    mp.push_back(point_d2(2.9, 2.9));
    mp.push_back(point_d2(3.0, 3.0));

    bg_multipoint clipped_mp;
    box_d2 b2(point_d2(1.5,1.5), point_d2(3.0, 3.0));
    for(const auto &p : mp){
        // intersectionではなくintersectsで1点ずつ確認する必要があるぽい
        if(bg::intersects(b2, p)){
            clipped_mp.push_back(p);
        }
    }
    // MULTIPOINT((1.4 1.4),(2 2),(2.9 2.9),(3 3))
    std::cout << bg::wkt(mp) << std::endl;
    // MULTIPOINT((2 2),(2.9 2.9),(3 3))
    std::cout << bg::wkt(clipped_mp) << std::endl;
}
目で見て確認*1

before mls(A)
MULTILINESTRING((1 9,2.8 7,3 6,4 5,5 4,6 2),(1 2,5.8 3,5.9 4,6 5,6.1 6,9 11 ))
f:id:usadamasa:20160301121537p:plain

clipping box(B)
POLYGON((2 3,2 9,6 9,6 3,2 3))
f:id:usadamasa:20160301121740p:plain

after mls(C)
MULTILINESTRING((2 7.88889,2.8 7,3 6,4 5,5 4,5.5 3),(5.8 3,5.9 4,6 5))
f:id:usadamasa:20160301121834p:plain

線と矩形の接点にちゃんと点がある。
なお、矩形から完全に外れたlinestringは無視されるらしい。

その他

今回はC++かつ既存コードがboostを使用していてためBoost::Geometryを使ったが、他にもいろいろあるぽい。

参考文献