LCOV - code coverage report
Current view: top level - autotest/cpp - test_marching_squares_polygon.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 140 142 98.6 %
Date: 2026-09-14 13:07:48 Functions: 26 26 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL algorithms
       4             :  * Purpose:  Tests for the marching squares algorithm
       5             :  * Author:   Hugo Mercier, <hugo dot mercier at oslandia dot com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2018, Hugo Mercier, <hugo dot mercier at oslandia dot com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdal_unit_test.h"
      14             : 
      15             : #include "gdal_alg.h"
      16             : 
      17             : #include "marching_squares/level_generator.h"
      18             : #include "marching_squares/polygon_ring_appender.h"
      19             : #include "marching_squares/segment_merger.h"
      20             : #include "marching_squares/contour_generator.h"
      21             : 
      22             : #ifdef DEBUG
      23             : #include <fstream>
      24             : #endif
      25             : 
      26             : #include <limits>
      27             : 
      28             : #include "gtest_include.h"
      29             : 
      30             : namespace marching_squares
      31             : {
      32             : class TestPolygonWriter
      33             : {
      34             :   public:
      35          13 :     void startPolygon(double level)
      36             :     {
      37          13 :         currentPolygon_ = &polygons_[level];
      38          13 :     }
      39             : 
      40          13 :     void endPolygon()
      41             :     {
      42          13 :     }
      43             : 
      44          16 :     void addPart(const std::list<marching_squares::Point> &ring)
      45             :     {
      46          16 :         PolygonPart part;
      47          16 :         part.push_back(ring);
      48          16 :         currentPolygon_->emplace_back(part);
      49          16 :         currentPart_ = &currentPolygon_->back();
      50          16 :     }
      51             : 
      52           3 :     void addInteriorRing(const std::list<marching_squares::Point> &ring)
      53             :     {
      54           3 :         currentPart_->push_back(ring);
      55           3 :     }
      56             : 
      57          13 :     void out(std::ostream &ostr, double level) const
      58             :     {
      59          13 :         auto pIt = polygons_.find(level);
      60          13 :         if (pIt == polygons_.end())
      61           1 :             return;
      62             : 
      63          28 :         for (const auto &part : pIt->second)
      64             :         {
      65          16 :             ostr << "{ ";
      66          35 :             for (const auto &ring : part)
      67             :             {
      68          19 :                 ostr << "{ ";
      69         247 :                 for (const auto &pt : ring)
      70             :                 {
      71         228 :                     ostr << "(" << pt.x << "," << pt.y << ") ";
      72             :                 }
      73          19 :                 ostr << "} ";
      74             :             }
      75          16 :             ostr << "} ";
      76             :         }
      77             :     }
      78             : 
      79             : #ifdef DEBUG
      80             :     void toSvg(const std::string &filename)
      81             :     {
      82             :         std::ofstream ofs(filename);
      83             :         ofs << "<?xml version=\"1.0\" encoding=\"UTF-8\" "
      84             :                "standalone=\"no\"?><svg xmlns=\"http://www.w3.org/2000/svg\" "
      85             :                "version=\"1.1\">\n";
      86             :         ofs << "<defs><marker id=\"arrow\" refX=\"0\" refY=\"0\" "
      87             :                "orient=\"auto\">\n";
      88             :         ofs << "<path d=\"M 0,0 L-1.5,-1 L-1.5,1 L0,0\" "
      89             :                "style=\"fill:#000000;\" />\n";
      90             :         ofs << "</marker></defs>\n";
      91             : 
      92             :         const std::string colors[] = {"white", "#bbb", "#888",
      93             :                                       "#666",  "#333", "black"};
      94             : 
      95             :         int level = 0;
      96             :         for (auto &p : polygons_)
      97             :         {
      98             :             for (const auto &part : p.second)
      99             :             {
     100             :                 ofs << "<path style=\"fill:" << colors[level] << ";\" d=\"";
     101             :                 for (const auto &ring : part)
     102             :                 {
     103             :                     ofs << "M ";
     104             :                     for (const auto &point : ring)
     105             :                     {
     106             :                         ofs << point.x * 10 << "," << point.y * 10 << " ";
     107             :                     }
     108             :                 }
     109             :                 ofs << "\"/>";
     110             :             }
     111             :             level++;
     112             :         }
     113             :         ofs << "</svg>";
     114             :     }
     115             : #endif
     116             : 
     117             :   private:
     118             :     typedef std::vector<LineString> PolygonPart;
     119             :     typedef std::vector<PolygonPart> Polygon;
     120             :     Polygon *currentPolygon_ = nullptr;
     121             :     PolygonPart *currentPart_ = nullptr;
     122             : 
     123             :   public:
     124             :     std::map<double, Polygon> polygons_;
     125             : };
     126             : 
     127           2 : static bool equal_linestrings(const LineString &ls1, const LineString &ls2)
     128             : {
     129           2 :     if (ls1.size() != ls2.size())
     130           0 :         return false;
     131           2 :     auto it1 = ls1.begin();
     132           2 :     auto it2 = ls2.begin();
     133          20 :     for (; it1 != ls1.end(); it1++, it2++)
     134             :     {
     135          18 :         if (!(*it1 == *it2))
     136           0 :             return false;
     137             :     }
     138           2 :     return true;
     139             : }
     140             : }  // namespace marching_squares
     141             : 
     142             : namespace
     143             : {
     144             : using namespace marching_squares;
     145             : 
     146             : // Common fixture with test data
     147             : struct test_ms_polygon : public ::testing::Test
     148             : {
     149             : };
     150             : 
     151             : // Dummy test
     152           4 : TEST_F(test_ms_polygon, dummy)
     153             : {
     154             :     // one pixel
     155           2 :     std::vector<double> data = {2.0};
     156           2 :     TestPolygonWriter w;
     157             :     {
     158             :         PolygonRingAppender<TestPolygonWriter> appender(w, -100, -100, 100,
     159           2 :                                                         100);
     160             :         IntervalLevelRangeIterator levels(
     161           1 :             0.0, 10.0, -std::numeric_limits<double>::infinity());
     162             :         SegmentMerger<PolygonRingAppender<TestPolygonWriter>,
     163             :                       IntervalLevelRangeIterator>
     164           2 :             writer(appender, levels, /* polygonize */ true);
     165             :         ContourGenerator<decltype(writer), IntervalLevelRangeIterator> cg(
     166           2 :             1, 1, false, NaN, writer, levels);
     167           1 :         cg.feedLine(&data[0]);
     168             :     }
     169             : 
     170             :     {
     171           2 :         std::ostringstream ostr;
     172           1 :         w.out(ostr, 10.0);
     173             :         // "Polygon #0",
     174           2 :         EXPECT_EQ(ostr.str(), "{ { (0.5,1) (1,1) (1,0.5) (1,0) (0.5,0) (0,0) "
     175             :                               "(0,0.5) (0,1) (0.5,1) } } ");
     176             :     }
     177           1 : }
     178             : 
     179           4 : TEST_F(test_ms_polygon, four_pixels)
     180             : {
     181             :     // four pixels
     182             :     // two rings
     183             :     // 5  10
     184             :     // 10  5
     185             :     // levels = 0, 10
     186             :     //
     187             :     // legend:
     188             :     //  :   contour
     189             :     //  #   border (level 0)
     190             :     //  =   border (level 10)
     191             :     //
     192             :     //   NaN                NaN                NaN
     193             :     //    +------------------+------------------+------------------+
     194             :     //    |                  |                  |                  |
     195             :     //    |    (0,0)         |      (1,0)       |      (2,0)       |
     196             :     //    |       5         5|      7.5       10|        10        |
     197             :     //    |        +#########+########+########o+========++        |
     198             :     //    |        #         |        |         :        ||        |
     199             :     //    |        #         |        |         :        ||        |
     200             :     //    |        #         |        |         :        ||        |
     201             :     //    +--------+---------+--------+---------o........o+--------+
     202             :     //    |NaN   5 #        5|                10|      10#      NaN|
     203             :     //    |        #         |                  |        #         |
     204             :     //    |        #         |                  |        #         |
     205             :     //    |    7.5++---------+ 7.5           7.5+--------+         |
     206             :     //    |        #         |                  |        #         |
     207             :     //    |        #         |                  |        #         |
     208             :     //    |        #         |       7.5        |        #         |
     209             :     //    +-------++.........o--------+---------+--------+---------+
     210             :     //    |NaN  10||       10:        |        5|      5 #      NaN|
     211             :     //    |       ||         :        |         |        #         |
     212             :     //    |       ||         :        |         |        #         |
     213             :     //    |       ++=========o########+#########+########+         |
     214             :     //    |      10        10|      7.5        5|        5         |
     215             :     //    |     (0,2)        |       (1,2)      |       (2,2)      |
     216             :     //    |                  |                  |                  |
     217             :     //    +------------------+------------------+------------------+
     218             :     //  NaN                 NaN                NaN                NaN
     219             : 
     220           2 :     std::vector<double> data = {5.0, 10.0, 10.0, 5.0};
     221           2 :     TestPolygonWriter w;
     222             :     {
     223             :         PolygonRingAppender<TestPolygonWriter> appender(w, -100, -100, 100,
     224           2 :                                                         100);
     225             :         IntervalLevelRangeIterator levels(
     226           1 :             0.0, 10.0, -std::numeric_limits<double>::infinity());
     227             :         SegmentMerger<PolygonRingAppender<TestPolygonWriter>,
     228             :                       IntervalLevelRangeIterator>
     229           2 :             writer(appender, levels, /* polygonize */ true);
     230             :         ContourGenerator<decltype(writer), IntervalLevelRangeIterator> cg(
     231           2 :             2, 2, false, NaN, writer, levels);
     232           1 :         cg.feedLine(&data[0]);
     233           1 :         cg.feedLine(&data[2]);
     234             :     }
     235             : 
     236             :     {
     237           2 :         std::ostringstream ostr;
     238           1 :         w.out(ostr, 10.0);
     239             :         // "Polygon #1"
     240           2 :         EXPECT_EQ(ostr.str(),
     241             :                   "{ { (1.5,2) (2,2) (2,1.5) (2,1) (2,0.5) (1.5,0.5) (1.5,0.5) "
     242             :                   "(1.5,0) (1,0) (0.5,0) (0,0) (0,0.5) (0,1) (0,1.5) (0.5,1.5) "
     243             :                   "(0.5,1.5) (0.5,2) (1,2) (1.5,2) } } ");
     244             :     }
     245             :     {
     246           2 :         std::ostringstream ostr;
     247           1 :         w.out(ostr, 20.0);
     248             :         // "Polygon #2"
     249           2 :         EXPECT_EQ(ostr.str(),
     250             :                   "{ { (2,0.5) (2,0.5) (2,0) (1.5,0) (1.5,0) (1.5,0.5) "
     251             :                   "(1.5,0.5) (2,0.5) } } { { (0.5,1.5) (0.5,1.5) (0,1.5) "
     252             :                   "(0,1.5) (0,2) (0.5,2) (0.5,2) (0.5,1.5) } } ");
     253             :     }
     254           1 : }
     255             : 
     256           4 : TEST_F(test_ms_polygon, four_pixels_2)
     257             : {
     258             :     // four pixels
     259             :     // 155    155.01
     260             :     // 154.99 155
     261             :     // levels = 155
     262             : 
     263             :     //   NaN                NaN                NaN
     264             :     //    +------------------+------------------+------------------+
     265             :     //    |                  |                  |                  |
     266             :     //    |    (0,0)         |      (1,0)       |      (2,0)       |
     267             :     //    |      155         |     155.005      |      155.01      |
     268             :     //    |        +---------+--------+---------+---------+        |
     269             :     //    |        |       155        |      155.01       |        |
     270             :     //    |        |         |        |         |         |        |
     271             :     //    |        |         |     155.005      |         |        |
     272             :     //    +--------+---------+--------+---------+---------+--------+
     273             :     //    |NaN   155       155               155.01    155.01   NaN|
     274             :     //    |        |         |                  |         |        |
     275             :     //    |    154.995       |                  |      155.005     |
     276             :     //    |        +-------154.995           155.005------+        |
     277             :     //    |        |         |                  |         |        |
     278             :     //    |        |         |                  |         |        |
     279             :     //    |        |         |                  |         |        |
     280             :     //    +--------+---------+--------+---------+---------+--------+
     281             :     //    |NaN  154.99    154.99   154.995    155       155     NaN|
     282             :     //    |        |         |        |         |         |        |
     283             :     //    |        |         |        |         |         |        |
     284             :     //    |        +---------+--------+---------+---------+        |
     285             :     //    |     154.99    154.99   154.995    155       155        |
     286             :     //    |     (0,2)        |       (1,2)      |       (2,2)      |
     287             :     //    |                  |                  |                  |
     288             :     //    +------------------+------------------+------------------+
     289             :     //  NaN                 NaN                NaN                NaN
     290             : 
     291           1 :     std::vector<double> data = {155.0, 155.01, 154.99, 155.0};
     292             :     {
     293           2 :         TestPolygonWriter w;
     294             :         {
     295             :             PolygonRingAppender<TestPolygonWriter> appender(w, -100, -100, 100,
     296           2 :                                                             100);
     297           1 :             const double levels[] = {155.0};
     298             :             FixedLevelRangeIterator levelGenerator(
     299           1 :                 levels, 1, -std::numeric_limits<double>::infinity(),
     300           2 :                 std::numeric_limits<double>::infinity());
     301             :             SegmentMerger<PolygonRingAppender<TestPolygonWriter>,
     302             :                           FixedLevelRangeIterator>
     303           2 :                 writer(appender, levelGenerator, /* polygonize */ true);
     304             :             ContourGenerator<decltype(writer), FixedLevelRangeIterator> cg(
     305           2 :                 2, 2, false, NaN, writer, levelGenerator);
     306           1 :             cg.feedLine(&data[0]);
     307           1 :             cg.feedLine(&data[2]);
     308             :         }
     309           1 :         EXPECT_EQ(w.polygons_.size(), 2U);
     310             :         {
     311           2 :             std::ostringstream ostr;
     312           1 :             w.out(ostr, 155.0);
     313             :             // "Polygon #0"
     314           2 :             EXPECT_EQ(
     315             :                 ostr.str(),
     316             :                 "{ { (1.4999,2) (1.4999,1.5) (0.5,0.5001) (0,0.5001) (0,1) "
     317             :                 "(0,1.5) (0,2) (0.5,2) (1,2) (1.4999,2) } } ");
     318             :         }
     319             :         {
     320           2 :             std::ostringstream ostr;
     321           1 :             w.out(ostr, Inf);
     322             :             // "Polygon #1"
     323           2 :             EXPECT_EQ(
     324             :                 ostr.str(),
     325             :                 "{ { (1.5,2) (2,2) (2,1.5) (2,1) (2,0.5) (2,0) (1.5,0) (1,0) "
     326             :                 "(0.5,0) (0,0) (0,0.5) (0,0.5001) (0.5,0.5001) (1.4999,1.5) "
     327             :                 "(1.4999,2) (1.5,2) } } ");
     328             :         }
     329             :     }
     330             : 
     331             :     {
     332           1 :         TestPolygonWriter w;
     333             :         {
     334             :             PolygonRingAppender<TestPolygonWriter> appender(w, -100, -100, 100,
     335           2 :                                                             100);
     336           1 :             const double levels[] = {155.0};
     337             :             FixedLevelRangeIterator levelGenerator(
     338           1 :                 levels, 1, -std::numeric_limits<double>::infinity(),
     339           2 :                 std::numeric_limits<double>::infinity());
     340             :             SegmentMerger<PolygonRingAppender<TestPolygonWriter>,
     341             :                           FixedLevelRangeIterator>
     342           2 :                 writer(appender, levelGenerator, /* polygonize */ true);
     343           1 :             writer.setSkipLevels({1});
     344             :             ContourGenerator<decltype(writer), FixedLevelRangeIterator> cg(
     345           2 :                 2, 2, false, NaN, writer, levelGenerator);
     346           1 :             cg.feedLine(&data[0]);
     347           1 :             cg.feedLine(&data[2]);
     348             :         }
     349             :         {
     350           1 :             EXPECT_EQ(w.polygons_.size(), 2U);
     351           1 :             auto iter = w.polygons_.find(Inf);
     352           1 :             ASSERT_TRUE(iter != w.polygons_.end());
     353           1 :             EXPECT_TRUE(iter->second.empty());
     354             :         }
     355             : 
     356             :         {
     357           2 :             std::ostringstream ostr;
     358           1 :             w.out(ostr, 155.0);
     359             :             // "Polygon #0"
     360           2 :             EXPECT_EQ(
     361             :                 ostr.str(),
     362             :                 "{ { (1.4999,2) (1.4999,1.5) (0.5,0.5001) (0,0.5001) (0,1) "
     363             :                 "(0,1.5) (0,2) (0.5,2) (1,2) (1.4999,2) } } ");
     364             :         }
     365             :     }
     366             : }
     367             : 
     368           4 : TEST_F(test_ms_polygon, nine_pixels)
     369             : {
     370             :     // nine pixels
     371             :     // two nested rings
     372             :     // levels = 1, 11, 21
     373             :     // pixels
     374             :     // +-----+-----+-----+-----+-----+
     375             :     // |     |     |     |     |     |
     376             :     // | NaN | NaN | NaN | NaN | NaN |
     377             :     // |     |     |     |     |     |
     378             :     // +-----+-----+-----+-----+-----+
     379             :     // |     |     |     |     |     |
     380             :     // | NaN |  0  |  4  |  0  | NaN |
     381             :     // |     |     |     |     |     |
     382             :     // +-----+-----+-----+-----+-----+
     383             :     // |     |     |     |     |     |
     384             :     // | NaN |  4  |  12 |  4  | NaN |
     385             :     // |     |     |     |     |     |
     386             :     // +-----+-----+-----+-----+-----+
     387             :     // |     |     |     |     |     |
     388             :     // | NaN |  0  |  4  |  0  | NaN |
     389             :     // |     |     |     |     |     |
     390             :     // +-----+-----+-----+-----+-----+
     391             :     // |     |     |     |     |     |
     392             :     // | NaN | NaN | NaN | NaN | NaN |
     393             :     // |     |     |     |     |     |
     394             :     // +-----+-----+-----+-----+-----+
     395             :     //
     396             :     //   NaN                NaN                NaN                NaN NaN
     397             :     //    +------------------+------------------+------------------+------------------+
     398             :     //    |                  |                  |                  | | | (0,0)
     399             :     //    |      (1,0)       |      (2,0)       |                  | |        0
     400             :     //    0|        2        4|         2       0|         0        | |
     401             :     //    +---------+---o----+---------+---------+----o---+---------+        |
     402             :     //    |        |         |   :    |         |         |    :   |         | |
     403             :     //    |        |         |   :    |         |         |    :   |         | |
     404             :     //    |        |         |   :    |         |         |    :   |         | |
     405             :     //    +--------+---------+---o----+---------+---------+----o---+---------+--------+
     406             :     //    NaN |NaN    0|        0| _/     2        4|         2     \_0| |0 | |
     407             :     //    o.........o/                 |                 \o.........o        |
     408             :     //    |        |         |                  |                  |         | |
     409             :     //    |       2+---------+ 2                |                 2+---------+2
     410             :     //    | |        |         |                  |                  |         |
     411             :     //    | |        |         |                 _o_                 |         |
     412             :     //    | |        |         |                / | \                |         |
     413             :     //    |
     414             :     //    +--------+---------+---------------o--+--o---------------+---------+--------+
     415             :     //    NaN |NaN    4|        4|                \12 /               4| |4 | |
     416             :     //    |         |                 -o-                 |         |        |
     417             :     //    |        |         |                  |                  |         | |
     418             :     //    |       2+---------+ 2                |                 2+---------+2
     419             :     //    | |        |         |                  |                  |         |
     420             :     //    | |        o.........o_                 |                 _o.........o
     421             :     //    | |        |         | \_     2         |        2      _/ |         |
     422             :     //    |
     423             :     //    +--------+---------+---o----+---------+--------+----o/---+---------+--------+
     424             :     //    NaN |NaN    0|        0|   :    |        4|        |    :   0| |0 | |
     425             :     //    |         |   :    |         |        |    :    |         |        |
     426             :     //    |        |         |   :    |         |        |    :    |         | |
     427             :     //    |        +---------+---o----+---------+--------+----o----+---------+ |
     428             :     //    |       0         0|        2        4|        2        0|         0 |
     429             :     //    |     (0,3)        |       (1,3)      |       (2,3)      | | | | | | |
     430             :     //    +------------------+------------------+------------------+------------------+
     431             :     //  NaN                 NaN                NaN                NaN NaN
     432           2 :     std::vector<double> data = {0.0, 4.0, 0.0, 4.0, 12.0, 4.0, 0.0, 4.0, 0.0};
     433           2 :     TestPolygonWriter w;
     434             :     {
     435             :         PolygonRingAppender<TestPolygonWriter> appender(w, -100, -100, 100,
     436           2 :                                                         100);
     437             :         IntervalLevelRangeIterator levels(
     438           1 :             1.0, 10.0, -std::numeric_limits<double>::infinity());
     439             :         SegmentMerger<PolygonRingAppender<TestPolygonWriter>,
     440             :                       IntervalLevelRangeIterator>
     441           2 :             writer(appender, levels, /* polygonize */ true);
     442             :         ContourGenerator<decltype(writer), IntervalLevelRangeIterator> cg(
     443           2 :             3, 3, false, NaN, writer, levels);
     444           1 :         cg.feedLine(&data[0]);
     445           1 :         cg.feedLine(&data[3]);
     446           1 :         cg.feedLine(&data[6]);
     447             :     }
     448             : 
     449             :     {
     450           2 :         std::ostringstream ostr;
     451           1 :         w.out(ostr, 1.0);
     452             :         // "Polygon #0"
     453           2 :         EXPECT_EQ(ostr.str(),
     454             :                   "{ { (0.5,0.75) (0.75,0.5) (0.75,0) (0.5,0) (0,0) (0,0.5) "
     455             :                   "(0,0.75) (0.5,0.75) } } { { (2.5,0.75) (3,0.75) (3,0.5) "
     456             :                   "(3,0) (2.5,0) (2.25,0) (2.25,0.5) (2.5,0.75) } } { { "
     457             :                   "(0.75,3) (0.75,2.5) (0.5,2.25) (0,2.25) (0,2.5) (0,3) "
     458             :                   "(0.5,3) (0.75,3) } } { { (2.5,3) (3,3) (3,2.5) (3,2.25) "
     459             :                   "(2.5,2.25) (2.25,2.5) (2.25,3) (2.5,3) } } ");
     460             :     }
     461             :     {
     462           2 :         std::ostringstream ostr;
     463           1 :         w.out(ostr, 11.0);
     464             :         // "Polygon #1"
     465           2 :         EXPECT_EQ(ostr.str(),
     466             :                   "{ { (2.25,2.5) (2.5,2.25) (3,2.25) (3,2) (3,1.5) (3,1) "
     467             :                   "(3,0.75) (2.5,0.75) (2.25,0.5) (2.25,0) (2,0) (1.5,0) (1,0) "
     468             :                   "(0.75,0) (0.75,0.5) (0.5,0.75) (0,0.75) (0,1) (0,1.5) (0,2) "
     469             :                   "(0,2.25) (0.5,2.25) (0.75,2.5) (0.75,3) (1,3) (1.5,3) (2,3) "
     470             :                   "(2.25,3) (2.25,2.5) } { (1.625,1.5) (1.5,1.625) (1.375,1.5) "
     471             :                   "(1.5,1.375) (1.625,1.5) } } ");
     472             :     }
     473             :     {
     474           2 :         std::ostringstream ostr;
     475           1 :         w.out(ostr, 21.0);
     476             :         // "Polygon #2"
     477           2 :         EXPECT_EQ(ostr.str(), "{ { (1.625,1.5) (1.5,1.625) (1.375,1.5) "
     478             :                               "(1.5,1.375) (1.625,1.5) } } ");
     479             :     }
     480           1 : }
     481             : 
     482           4 : TEST_F(test_ms_polygon, three_nested_rings)
     483             : {
     484             :     // Three nested rings
     485             :     std::vector<double> data = {2, 2, 2, 2, 2, 2, 4, 4, 4, 2, 2, 4, 6,
     486           2 :                                 4, 2, 2, 4, 4, 4, 2, 2, 2, 2, 2, 2};
     487           2 :     TestPolygonWriter w;
     488             :     {
     489             :         PolygonRingAppender<TestPolygonWriter> appender(w, -100, -100, 100,
     490           2 :                                                         100);
     491             :         IntervalLevelRangeIterator levels(
     492           1 :             1.0, 2.0, -std::numeric_limits<double>::infinity());
     493             :         SegmentMerger<PolygonRingAppender<TestPolygonWriter>,
     494             :                       IntervalLevelRangeIterator>
     495           2 :             writer(appender, levels, /* polygonize */ true);
     496             :         ContourGenerator<decltype(writer), IntervalLevelRangeIterator> cg(
     497           2 :             5, 5, false, NaN, writer, levels);
     498           6 :         for (int i = 0; i < 5; i++)
     499             :         {
     500           5 :             cg.feedLine(&data[5 * i]);
     501             :         }
     502             :     }
     503             :     {
     504           2 :         std::ostringstream ostr;
     505           1 :         w.out(ostr, 1.0);
     506             :         // "Polygon #0"
     507           2 :         EXPECT_EQ(ostr.str(), "");
     508             :     }
     509             :     {
     510           2 :         std::ostringstream ostr;
     511           1 :         w.out(ostr, 3.0);
     512             :         // "Polygon #1"
     513           2 :         EXPECT_EQ(
     514             :             ostr.str(),
     515             :             "{ { (4.5,5) (5,5) (5,4.5) (5,4) (5,3.5) (5,3) (5,2.5) (5,2) "
     516             :             "(5,1.5) (5,1) (5,0.5) (5,0) (4.5,0) (4,0) (3.5,0) (3,0) (2.5,0) "
     517             :             "(2,0) (1.5,0) (1,0) (0.5,0) (0,0) (0,0.5) (0,1) (0,1.5) (0,2) "
     518             :             "(0,2.5) (0,3) (0,3.5) (0,4) (0,4.5) (0,5) (0.5,5) (1,5) (1.5,5) "
     519             :             "(2,5) (2.5,5) (3,5) (3.5,5) (4,5) (4.5,5) } { (4,3.5) (3.5,4) "
     520             :             "(2.5,4) (1.5,4) (1,3.5) (1,2.5) (1,1.5) (1.5,1) (2.5,1) (3.5,1) "
     521             :             "(4,1.5) (4,2.5) (4,3.5) } } ");
     522             :     }
     523             :     {
     524           2 :         std::ostringstream ostr;
     525           1 :         w.out(ostr, 5.0);
     526             :         // "Polygon #2"
     527           2 :         EXPECT_EQ(ostr.str(),
     528             :                   "{ { (4,3.5) (3.5,4) (2.5,4) (1.5,4) (1,3.5) (1,2.5) (1,1.5) "
     529             :                   "(1.5,1) (2.5,1) (3.5,1) (4,1.5) (4,2.5) (4,3.5) } { (3,2.5) "
     530             :                   "(2.5,3) (2,2.5) (2.5,2) (3,2.5) } } ");
     531             :     }
     532             :     {
     533           2 :         std::ostringstream ostr;
     534           1 :         w.out(ostr, 7.0);
     535             :         // "Polygon #3"
     536           2 :         EXPECT_EQ(ostr.str(),
     537             :                   "{ { (3,2.5) (2.5,3) (2,2.5) (2.5,2) (3,2.5) } } ");
     538             :     }
     539             : 
     540             :     // "Inner ring of polygon #1 = exterioring ring of polygon #2"
     541           1 :     EXPECT_TRUE(
     542             :         equal_linestrings(w.polygons_[3.0][0][1], w.polygons_[5.0][0][0]));
     543             :     // "Inner ring of polygon #2 = exterioring ring of polygon #3"
     544           1 :     EXPECT_TRUE(
     545             :         equal_linestrings(w.polygons_[5.0][0][1], w.polygons_[7.0][0][0]));
     546           1 : }
     547             : }  // namespace

Generated by: LCOV version 1.14