LCOV - code coverage report
Current view: top level - alg/marching_squares - segment_merger.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 106 113 93.8 %
Date: 2024-05-02 22:57:13 Functions: 51 72 70.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Marching square algorithm
       4             :  * Purpose:  Core algorithm implementation for contour line generation.
       5             :  * Author:   Oslandia <infos at oslandia dot com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2018, Oslandia <infos at oslandia dot com>
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : #ifndef MARCHING_SQUARES_SEGMENT_MERGER_H
      29             : #define MARCHING_SQUARES_SEGMENT_MERGER_H
      30             : 
      31             : #include "point.h"
      32             : 
      33             : #include <list>
      34             : #include <map>
      35             : 
      36             : #include <iostream>
      37             : 
      38             : namespace marching_squares
      39             : {
      40             : 
      41             : // SegmentMerger: join segments into linestrings and possibly into rings of
      42             : // polygons
      43             : template <typename LineWriter, typename LevelGenerator> struct SegmentMerger
      44             : {
      45             :     struct LineStringEx
      46             :     {
      47             :         LineString ls = LineString();
      48             :         bool isMerged = false;
      49             :     };
      50             : 
      51             :     // a collection of unmerged linestrings
      52             :     typedef std::list<LineStringEx> Lines;
      53             : 
      54          36 :     SegmentMerger(LineWriter &lineWriter, const LevelGenerator &levelGenerator,
      55             :                   bool polygonize_)
      56             :         : polygonize(polygonize_), lineWriter_(lineWriter), lines_(),
      57          36 :           levelGenerator_(levelGenerator)
      58             :     {
      59          36 :     }
      60             : 
      61          36 :     ~SegmentMerger()
      62             :     {
      63          36 :         if (polygonize)
      64             :         {
      65          66 :             for (auto it = lines_.begin(); it != lines_.end(); ++it)
      66             :             {
      67          49 :                 if (!it->second.empty())
      68           0 :                     debug("remaining unclosed contour");
      69             :             }
      70             :         }
      71             :         // write all remaining (non-closed) lines
      72       14288 :         for (auto it = lines_.begin(); it != lines_.end(); ++it)
      73             :         {
      74       14252 :             const int levelIdx = it->first;
      75       28357 :             while (it->second.begin() != it->second.end())
      76             :             {
      77       14105 :                 lineWriter_.addLine(levelGenerator_.level(levelIdx),
      78       14105 :                                     it->second.begin()->ls, /* closed */ false);
      79       14105 :                 it->second.pop_front();
      80             :             }
      81             :         }
      82          36 :     }
      83             : 
      84       10418 :     void addBorderSegment(int levelIdx, const Point &start, const Point &end)
      85             :     {
      86       10418 :         addSegment_(levelIdx, start, end);
      87       10418 :     }
      88             : 
      89       90723 :     void addSegment(int levelIdx, const Point &start, const Point &end)
      90             :     {
      91       90723 :         addSegment_(levelIdx, start, end);
      92       90723 :     }
      93             : 
      94        2662 :     void beginningOfLine()
      95             :     {
      96        2662 :         if (polygonize)
      97        1316 :             return;
      98             : 
      99             :         // mark lines as non merged
     100       19624 :         for (auto &l : lines_)
     101             :         {
     102       42720 :             for (auto &ls : l.second)
     103             :             {
     104       24442 :                 ls.isMerged = false;
     105             :             }
     106             :         }
     107             :     }
     108             : 
     109        2658 :     void endOfLine()
     110             :     {
     111        2658 :         if (polygonize)
     112        1316 :             return;
     113             : 
     114             :         // At the end of the line, we know that if no segment has been merged to
     115             :         // an existing line, it means there won't be anything more in the
     116             :         // future, we can then emit the line (this both speeds up and saves
     117             :         // memory)
     118             : 
     119       19959 :         for (auto &l : lines_)
     120             :         {
     121       18617 :             const int levelIdx = l.first;
     122       18617 :             auto it = l.second.begin();
     123       47204 :             while (it != l.second.end())
     124             :             {
     125       28587 :                 if (!it->isMerged)
     126             :                 {
     127             :                     // Note that emitLine_ erases `it` and returns an iterator
     128             :                     // advanced to the next element.
     129        3904 :                     it = emitLine_(levelIdx, it, /* closed */ false);
     130             :                 }
     131             :                 else
     132             :                 {
     133       24683 :                     ++it;
     134             :                 }
     135             :             }
     136             :         }
     137             :     }
     138             : 
     139             :     // non copyable
     140             :     SegmentMerger(const SegmentMerger<LineWriter, LevelGenerator> &) = delete;
     141             :     SegmentMerger<LineWriter, LevelGenerator> &
     142             :     operator=(const SegmentMerger<LineWriter, LevelGenerator> &) = delete;
     143             : 
     144             :     const bool polygonize;
     145             : 
     146             :   private:
     147             :     LineWriter &lineWriter_;
     148             :     // lines of each level
     149             :     std::map<int, Lines> lines_;
     150             :     const LevelGenerator &levelGenerator_;
     151             : 
     152      101141 :     void addSegment_(int levelIdx, const Point &start, const Point &end)
     153             :     {
     154      101141 :         Lines &lines = lines_[levelIdx];
     155             : 
     156      101141 :         if (start == end)
     157             :         {
     158           0 :             debug("degenerate segment (%f %f)", start.x, start.y);
     159          80 :             return;
     160             :         }
     161             :         // attempt to merge segment with existing line
     162      101141 :         auto it = lines.begin();
     163      258275 :         for (; it != lines.end(); ++it)
     164             :         {
     165      232236 :             if (it->ls.back() == end)
     166             :             {
     167        1107 :                 it->ls.push_back(start);
     168        1107 :                 it->isMerged = true;
     169        1107 :                 break;
     170             :             }
     171      231129 :             if (it->ls.front() == end)
     172             :             {
     173       33578 :                 it->ls.push_front(start);
     174       33578 :                 it->isMerged = true;
     175       33578 :                 break;
     176             :             }
     177      197551 :             if (it->ls.back() == start)
     178             :             {
     179       39325 :                 it->ls.push_back(end);
     180       39325 :                 it->isMerged = true;
     181       39325 :                 break;
     182             :             }
     183      158226 :             if (it->ls.front() == start)
     184             :             {
     185        1092 :                 it->ls.push_front(end);
     186        1092 :                 it->isMerged = true;
     187        1092 :                 break;
     188             :             }
     189             :         }
     190             : 
     191      101141 :         if (it == lines.end())
     192             :         {
     193             :             // new line
     194       26039 :             lines.push_back(LineStringEx());
     195       26039 :             lines.back().ls.push_back(start);
     196       26039 :             lines.back().ls.push_back(end);
     197       26039 :             lines.back().isMerged = true;
     198             :         }
     199       75102 :         else if (polygonize && (it->ls.front() == it->ls.back()))
     200             :         {
     201             :             // ring closed
     202          80 :             emitLine_(levelIdx, it, /* closed */ true);
     203          80 :             return;
     204             :         }
     205             :         else
     206             :         {
     207             :             // try to perform linemerge with another line
     208             :             // since we got out of the previous loop on the first match
     209             :             // there is no need to test previous elements
     210             :             // also: a segment merges at most two lines, no need to stall here
     211             :             // ;)
     212       75022 :             auto other = it;
     213       75022 :             ++other;
     214      148354 :             for (; other != lines.end(); ++other)
     215             :             {
     216       81282 :                 if (it->ls.back() == other->ls.front())
     217             :                 {
     218        6099 :                     it->ls.pop_back();
     219        6099 :                     it->ls.splice(it->ls.end(), other->ls);
     220        6099 :                     it->isMerged = true;
     221        6099 :                     lines.erase(other);
     222             :                     // if that makes a closed ring, returns it
     223        6099 :                     if (it->ls.front() == it->ls.back())
     224           0 :                         emitLine_(levelIdx, it, /* closed */ true);
     225        6099 :                     break;
     226             :                 }
     227       75183 :                 else if (other->ls.back() == it->ls.front())
     228             :                 {
     229        1845 :                     it->ls.pop_front();
     230        1845 :                     other->ls.splice(other->ls.end(), it->ls);
     231        1845 :                     other->isMerged = true;
     232        1845 :                     lines.erase(it);
     233             :                     // if that makes a closed ring, returns it
     234        1845 :                     if (other->ls.front() == other->ls.back())
     235           0 :                         emitLine_(levelIdx, other, /* closed */ true);
     236        1845 :                     break;
     237             :                 }
     238             :                 // two lists must be merged but one is in the opposite direction
     239       73338 :                 else if (it->ls.back() == other->ls.back())
     240             :                 {
     241           4 :                     it->ls.pop_back();
     242          12 :                     for (auto rit = other->ls.rbegin(); rit != other->ls.rend();
     243           8 :                          ++rit)
     244             :                     {
     245           8 :                         it->ls.push_back(*rit);
     246             :                     }
     247           4 :                     it->isMerged = true;
     248           4 :                     lines.erase(other);
     249             :                     // if that makes a closed ring, returns it
     250           4 :                     if (it->ls.front() == it->ls.back())
     251           0 :                         emitLine_(levelIdx, it, /* closed */ true);
     252           4 :                     break;
     253             :                 }
     254       73334 :                 else if (it->ls.front() == other->ls.front())
     255             :                 {
     256           2 :                     it->ls.pop_front();
     257           6 :                     for (auto rit = other->ls.begin(); rit != other->ls.end();
     258           4 :                          ++rit)
     259             :                     {
     260           4 :                         it->ls.push_front(*rit);
     261             :                     }
     262           2 :                     it->isMerged = true;
     263           2 :                     lines.erase(other);
     264             :                     // if that makes a closed ring, returns it
     265           2 :                     if (it->ls.front() == it->ls.back())
     266           0 :                         emitLine_(levelIdx, it, /* closed */ true);
     267           2 :                     break;
     268             :                 }
     269             :             }
     270             :         }
     271             :     }
     272             : 
     273        3984 :     typename Lines::iterator emitLine_(int levelIdx,
     274             :                                        typename Lines::iterator it, bool closed)
     275             :     {
     276        3984 :         Lines &lines = lines_[levelIdx];
     277        3984 :         if (lines.empty())
     278           0 :             lines_.erase(levelIdx);
     279             : 
     280             :         // consume "it" and remove it from the list
     281        3984 :         lineWriter_.addLine(levelGenerator_.level(levelIdx), it->ls, closed);
     282        3984 :         return lines.erase(it);
     283             :     }
     284             : };
     285             : 
     286             : }  // namespace marching_squares
     287             : #endif

Generated by: LCOV version 1.14