Search code examples
c++polygoncgalsimplification

Restore order of polygons after CGAL::Polyline_simplification_2


I follow this example of the CGAL documentation in order to simplify a number of polygons, which are initially stored in a vector called polys. Afterwards I collect the simplified polygons into a vector called simple_polys. However, the order of the polygons is mixed up during the simplification. This means simple_polys[i] is not necessarily the simplified version of polys[i]. I would be thankful for any ideas on how to adapt my code (see below) such that the order of the polygons is maintained or restored.

Notice: The obvious solution to simplify each polygon individually is not an option for me, since I need to preserve the common edges of the polygons (if there are any), i.e., the common edges are allowed to be simplified but they must remain common edges.

Here is the current version of my code:

#include <vector>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Polygon_2.h>
#include <CGAL/Constrained_Delaunay_triangulation_2.h>
#include <CGAL/Constrained_triangulation_plus_2.h>
#include <CGAL/Polyline_simplification_2/simplify.h>
#include <CGAL/Polyline_simplification_2/Squared_distance_cost.h>
#include <CGAL/IO/WKT.h>

// CGAL typedefs
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef Kernel::Point_2 Point_2;
typedef CGAL::Polygon_2<Kernel> Polygon_2;

// CGAL classes needed for polyline simplification
namespace PS = CGAL::Polyline_simplification_2;
typedef PS::Vertex_base_2<Kernel> Vb;
typedef CGAL::Constrained_triangulation_face_base_2<Kernel> Fb;
typedef CGAL::Triangulation_data_structure_2<Vb, Fb> TDS;
typedef CGAL::Constrained_Delaunay_triangulation_2<Kernel, TDS, CGAL::Exact_predicates_tag> CDT;
typedef CGAL::Constrained_triangulation_plus_2<CDT> CT;
typedef PS::Stop_above_cost_threshold Stop;
typedef PS::Squared_distance_cost Cost;

int main()
{
  // Read the polygons from WTK file
  std::ifstream is("polys.wkt");
  std::vector<Polygon_2> polys;
  do {
    Polygon_2 p;
    CGAL::IO::read_polygon_WKT(is, p);
    if(!p.is_empty())
      polys.push_back(p);
  } while(is.good() && !is.eof());

  // Insert the polygons into a Constrained_triangulation_plus_2
  CT ct;
  for ( auto poly : polys ) ct.insert_constraint(poly);

  // Simplify the polygons
  PS::simplify(ct, Cost(), Stop(2.0));

  // Convert the result back into normal polygons
  std::vector<Polygon_2> simple_polys;
  for( CT::Constraint_iterator cit = ct.constraints_begin(); cit != ct.constraints_end(); ++cit)
  {
    Polygon_2 poly;
    for ( CT::Points_in_constraint_iterator vit = ct.points_in_constraint_begin(*cit);
          vit != ct.points_in_constraint_end(*cit); ++vit) poly.push_back(*vit);
    simple_polys.push_back(poly);
  }
}

Here is an example of a WKT input file one can use to run the above code (name it polys.wkt):

POLYGON((46.465 -37.3521,35.5358 -20.6702,48.8869 -11.958,51.9866 -16.8288,50.0332 -18.1056,54.9318 -25.6487,55.8444 -25.0541,55.5429 -22.8841,73.1793 -11.5299,77.2208 -17.435))
POLYGON((150.934 48.3496,137.482 47.0145,136.581 54.533,134.581 54.3679,134.206 58.2453,136.107 58.4855,135.177 66.2472,148.772 67.1611))
POLYGON((113.969 -24.4624,103.013 -25.5321,102.007 -15.8837,101.437 -15.8568,101.143 -12.0671,101.652 -12.0653,100.581 -1.99834,107.794 -1.3459,107.853 -2.01614,111.495 -1.66637,111.568 -1.96882,111.947 -1.88637))
POLYGON((35.8102 -21.0888,27.2558 -8.05192,38.1005 -0.96724,44.4211 -10.2175,45.1996 -9.66818,45.021 -9.3973,45.5261 -7.35956,50.2289 -4.30611,50.4096 -3.43368,64.9678 6.02664,66.7379 -0.809735))
POLYGON((-1.28342 -115.889,22.725 -111.81,25.6615 -128.839,16.2488 -130.272,16.4807 -131.888,12.9582 -132.401,12.7173 -130.807,12.4127 -130.833,16.6721 -156.064,0.111882 -158.497,-1.28339 -150.737))
POLYGON((133.248 -68.93,100.825 -72.0011,99.8362 -61.4584,107.418 -60.8479,107.399 -60.397,110.68 -60.2175,110.691 -60.6869,120.743 -59.7579,120.696 -59.3589,124.041 -59.0963,124.058 -59.5348,132.275 -58.7058))
POLYGON((118.371 29.3127,111.786 28.6569,110.332 42.1233,117.682 42.8478,117.895 41.5415,118.397 41.5653,118.858 38.6083,118.3 38.521,118.704 35.4032,117.714 35.3187))
POLYGON((146.407 -67.6797,132.75 -68.9772,131.777 -58.752,135.766 -58.415,135.713 -57.87,138.974 -57.6063,139.028 -58.0592,145.445 -57.5652))
POLYGON((38.7765 -180.005,42.57 -177.511,39.8274 -173.17,45.4545 -169.387,34.4094 -152.557,47.1264 -144.271,47.0159 -144.108,51.1497 -141.344,53.8363 -145.461,55.5009 -144.64,58.878 -149.842,57.4133 -151.006,61.0026 -156.489,62.2453 -155.685,67.94 -140.543,85.4228 -166.825,64.2719 -180.516,60.9827 -175.481,56.3313 -178.594,57.1839 -179.899,44.1068 -188.413))
POLYGON((117.162 35.7683,136.609 37.8265,137.825 36.8336,138.697 36.9208,137.923 42.338,137.507 42.2789,137.23 44.2193,137.646 44.2784,137.59 44.664,137.165 44.6034,136.887 46.5429,137.314 46.6042,137.188 47.4854,150.877 48.8464,152.721 32.789,117.873 29.2627))
POLYGON((115.832 -45.2902,104.975 -46.2859,104.142 -37.6361,103.622 -37.7286,103.332 -34.3286,103.777 -34.0371,102.964 -25.0344,113.924 -23.9644))
POLYGON((66.5341 -28.0144,70.402 -33.9903,70.291 -34.3719,60.4063 -40.7947,59.9046 -40.7393,56.0238 -34.773,56.1665 -34.3699,56.3403 -34.274,66.1632 -27.8986))
POLYGON((105.673 -127.635,90.8711 -104.911,92.492 -102.406,115.3 -100.248,116.48 -111.052,112.586 -111.576,112.605 -111.81,111.09 -112.735,110.572 -112.55,110.365 -112.893,115.687 -121.196))
POLYGON((152.194 5.74036,111.195 -2.55811,111.11 -2.20564,107.405 -2.56147,106.262 7.74335,143.459 15.1823,142.642 22.9693,142.005 22.9033,141.67 26.0846,142.307 26.1507,141.68 32.1367,152.665 33.2868,154.612 15.5801,151.26 15.2101,151.847 9.88508,151.738 9.87268))
POLYGON((22.9004 58.0935,-1.28339 56.2459,-1.28339 62.8131,-0.817275 62.867,-0.1184 63.6614,5.80649 64.1534,6.12005 64.7858,8.92235 65.0356,9.85597 64.4913,21.9914 65.718))

Solution

  • I was able to figure it out myself. I post the solution here in case it would be useful for somebody else in the future. The solution is to make a connection between the indices of the polygons in the vector polys and the IDs of the constraints in the Constrained_triangulation_plus_2 object. Below is the adapted code which includes the solution to the problem.

    #include <vector>
    #include <CGAL/Exact_predicates_exact_constructions_kernel.h>
    #include <CGAL/Polygon_2.h>
    #include <CGAL/Constrained_Delaunay_triangulation_2.h>
    #include <CGAL/Constrained_triangulation_plus_2.h>
    #include <CGAL/Polyline_simplification_2/simplify.h>
    #include <CGAL/Polyline_simplification_2/Squared_distance_cost.h>
    #include <CGAL/IO/WKT.h>
    
    // CGAL typedefs
    typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
    typedef Kernel::Point_2 Point_2;
    typedef CGAL::Polygon_2<Kernel> Polygon_2;
    
    // CGAL classes needed for polyline simplification
    namespace PS = CGAL::Polyline_simplification_2;
    typedef PS::Vertex_base_2<Kernel> Vb;
    typedef CGAL::Constrained_triangulation_face_base_2<Kernel> Fb;
    typedef CGAL::Triangulation_data_structure_2<Vb, Fb> TDS;
    typedef CGAL::Constrained_Delaunay_triangulation_2<Kernel, TDS, CGAL::Exact_predicates_tag> CDT;
    typedef CGAL::Constrained_triangulation_plus_2<CDT> CT;
    typedef PS::Stop_above_cost_threshold Stop;
    typedef PS::Squared_distance_cost Cost;
    
    int main()
    {
      // Read the polygons from WTK file
      std::ifstream is("polys.wkt");
      std::vector<Polygon_2> polys;
      do {
        Polygon_2 p;
        CGAL::IO::read_polygon_WKT(is, p);
        if(!p.is_empty())
          polys.push_back(p);
      } while(is.good() && !is.eof());
    
      // Insert the polygons into a Constrained_triangulation_plus_2 and keep
      // track of the constraint IDs
      CT ct;
      std::vector<CT::Constraint_id> constraint_IDs;
      for ( auto poly : polys )
      {
        CT::Constraint_id ID = ct.insert_constraint(poly);
        constraint_IDs.push_back(ID);
      }
    
      // Simplify the polygons
      PS::simplify(ct, Cost(), Stop(2.0));
    
      // Convert the result back into normal polygons
      std::vector<Polygon_2> simple_polys(polys.size());
      for( CT::Constraint_iterator cit = ct.constraints_begin(); cit != ct.constraints_end(); ++cit)
      {
        // Find the index of the constraint ID in the constraint_IDs vector. This is the index
        // of the original polygon the current simplified polygon corresponds to
        auto it = std::find(constraint_IDs.begin(), constraint_IDs.end(), *cit);
        size_t idx = it - constraint_IDs.begin();
    
        // Obtain the simplified polygon
        Polygon_2 poly;
        for ( CT::Points_in_constraint_iterator vit = ct.points_in_constraint_begin(*cit);
              vit != ct.points_in_constraint_end(*cit); ++vit) poly.push_back(*vit);
    
        // Write the simplified polygon to the correct location
        simple_polys[idx] = poly;
      }
    }