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