1 /*****************************************************************************
2  *   Copyright (c) 2020, Hobu, Inc. (info@hobu.co)                           *
3  *                                                                           *
4  *   All rights reserved.                                                    *
5  *                                                                           *
6  *   This program is free software; you can redistribute it and/or modify    *
7  *   it under the terms of the GNU General Public License as published by    *
8  *   the Free Software Foundation; either version 3 of the License, or       *
9  *   (at your option) any later version.                                     *
10  *                                                                           *
11  ****************************************************************************/
12 
13 #include <regex>
14 #include <string>
15 #include <vector>
16 
17 #include <pdal/util/FileUtils.hpp>
18 
19 #include "../untwine/ProgressWriter.hpp"
20 #include "../untwine/VoxelKey.hpp"
21 
22 #include "Processor.hpp"
23 #include "PyramidManager.hpp"
24 #include "VoxelInfo.hpp"
25 
26 namespace untwine
27 {
28 namespace bu
29 {
30 
PyramidManager(const BaseInfo & b)31 PyramidManager::PyramidManager(const BaseInfo& b) : m_b(b), m_pool(10), m_totalPoints(0)
32 {}
33 
34 
~PyramidManager()35 PyramidManager::~PyramidManager()
36 {}
37 
38 
setProgress(ProgressWriter * progress)39 void PyramidManager::setProgress(ProgressWriter *progress)
40 {
41     m_progress = progress;
42 }
43 
44 
queue(const OctantInfo & o)45 void PyramidManager::queue(const OctantInfo& o)
46 {
47     {
48         std::lock_guard<std::mutex> lock(m_mutex);
49 
50         m_queue.push(o);
51     }
52     m_cv.notify_one();
53 }
54 
55 
run()56 void PyramidManager::run()
57 {
58     while (true)
59     {
60         OctantInfo o;
61         {
62             std::unique_lock<std::mutex> lock(m_mutex);
63 
64             m_cv.wait(lock, [this](){return m_queue.size();});
65             o = m_queue.front();
66             m_queue.pop();
67         }
68 
69         if (o.key() == VoxelKey(0, 0, 0, 0))
70             break;
71         process(o);
72     }
73     createHierarchy();
74 }
75 
76 
77 // Take the item off the queue and stick it on the complete list. If we have all 8 octants,
78 // remove the items from the complete list and queue a Processor job.
process(const OctantInfo & o)79 void PyramidManager::process(const OctantInfo& o)
80 {
81     VoxelKey pk = o.key().parent();
82     addComplete(o);
83     if (!childrenComplete(pk))
84         return;
85 
86     VoxelInfo vi(m_b.bounds, pk);
87     for (int i = 0; i < 8; ++i)
88         vi[i] = removeComplete(pk.child(i));
89 
90     // If there are no points in this voxel, just queue it as a child.
91     if (!vi.hasPoints())
92     {
93         queue(vi.octant());
94         m_progress->writeIncrement("Bypass sample for " + vi.key().toString());
95     }
96     else
97     {
98         m_pool.add([vi, this]()
99         {
100             Processor p(*this, vi, m_b);
101             p.run();
102             m_progress->writeIncrement("Sample complete for " + vi.key().toString());
103         });
104     }
105 }
106 
107 
addComplete(const OctantInfo & o)108 void PyramidManager::addComplete(const OctantInfo& o)
109 {
110     m_completes.insert({o.key(), o});
111 }
112 
113 
childrenComplete(const VoxelKey & parent)114 bool PyramidManager::childrenComplete(const VoxelKey& parent)
115 {
116     for (int i = 0; i < 8; ++i)
117         if (m_completes.find(parent.child(i)) == m_completes.end())
118             return false;
119     return true;
120 }
121 
122 
removeComplete(const VoxelKey & k)123 OctantInfo PyramidManager::removeComplete(const VoxelKey& k)
124 {
125     OctantInfo o;
126 
127     auto oi = m_completes.find(k);
128     if (oi != m_completes.end())
129     {
130         o = std::move(oi->second);
131         m_completes.erase(oi);
132     }
133     return o;
134 }
135 
136 
logOctant(const VoxelKey & k,int cnt,const IndexedStats & istats)137 void PyramidManager::logOctant(const VoxelKey& k, int cnt, const IndexedStats& istats)
138 {
139     std::lock_guard<std::mutex> lock(m_mutex);
140 
141     for (auto is : istats)
142     {
143         Stats& s = is.second;
144 
145         auto it = m_stats.find(s.name());
146         if (it != m_stats.end())
147         {
148             Stats& cur = it->second;
149             cur.merge(s);
150         }
151         else
152             m_stats.insert({s.name(), s});
153     }
154     m_written.insert({k, cnt});
155     m_totalPoints += cnt;
156 }
157 
158 
createHierarchy()159 void PyramidManager::createHierarchy()
160 {
161     std::function<int(const VoxelKey&)> calcCounts;
162     calcCounts = [this, &calcCounts](const VoxelKey& k)
163     {
164         int count = 0;
165         for (int i = 0; i < 8; ++i)
166         {
167             VoxelKey c = k.child(i);
168             if (m_written.find(c) != m_written.end())
169                 count += calcCounts(c);
170         }
171         m_childCounts[k] = count;
172         return count + 1;
173     };
174 
175     calcCounts(VoxelKey(0, 0, 0, 0));
176 
177     std::deque<VoxelKey> roots;
178 
179     roots.push_back(VoxelKey(0, 0, 0, 0));
180     while (roots.size())
181     {
182         VoxelKey k = roots.front();
183         roots.pop_front();
184         auto newRoots = emitRoot(k);
185         roots.insert(roots.end(), newRoots.begin(), newRoots.end());
186     }
187 }
188 
emitRoot(const VoxelKey & root)189 std::deque<VoxelKey> PyramidManager::emitRoot(const VoxelKey& root)
190 {
191     int level = root.level();
192     int stopLevel = level + LevelBreak;
193 
194     Entries entries;
195     entries.push_back({root, m_written[root]});
196     std::deque<VoxelKey> roots = emit(root, stopLevel, entries);
197 
198     std::ofstream out(m_b.outputDir + "/ept-hierarchy/" + root.toString() + ".json");
199 
200     out << "{\n";
201 
202     for (auto it = entries.begin(); it != entries.end(); ++it)
203     {
204         if (it != entries.begin())
205             out << ",\n";
206         out << "\"" << it->first << "\": " << it->second;
207     }
208     out << "\n";
209 
210     out << "}\n";
211 
212     return roots;
213 }
214 
215 
emit(const VoxelKey & p,int stopLevel,Entries & entries)216 std::deque<VoxelKey> PyramidManager::emit(const VoxelKey& p, int stopLevel, Entries& entries)
217 {
218     std::deque<VoxelKey> roots;
219 
220     for (int i = 0; i < 8; ++i)
221     {
222         VoxelKey c = p.child(i);
223         auto ci = m_childCounts.find(c);
224         if (ci != m_childCounts.end())
225         {
226 
227             if (c.level() != stopLevel || ci->second <= MinHierarchySize)
228             {
229                 entries.push_back({c, m_written[c]});
230                 auto r = emit(c, stopLevel, entries);
231                 roots.insert(roots.end(), r.begin(), r.end());
232             }
233             else
234             {
235                 entries.push_back({c, -1});
236                 roots.push_back(c);
237             }
238         }
239     }
240     return roots;
241 }
242 
243 
stats(const std::string & name)244 Stats *PyramidManager::stats(const std::string& name)
245 {
246     auto si = m_stats.find(name);
247     if (si == m_stats.end())
248         return nullptr;
249     return &si->second;
250 }
251 
252 } // namespace bu
253 } // namespace untwine
254