-
Notifications
You must be signed in to change notification settings - Fork 1
/
qPDAL.cpp
298 lines (245 loc) · 9 KB
/
qPDAL.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
//##########################################################################
//# #
//# CLOUDCOMPARE PLUGIN: qPDAL #
//# #
//# This program is free software; you can redistribute it and/or modify #
//# it under the terms of the GNU General Public License as published by #
//# the Free Software Foundation; version 2 of the License. #
//# #
//# This program is distributed in the hope that it will be useful, #
//# but WITHOUT ANY WARRANTY; without even the implied warranty of #
//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
//# GNU General Public License for more details. #
//# #
//# COPYRIGHT: XXX #
//# #
//##########################################################################
// First:
// Replace all occurrences of 'qPDAL' by your own plugin class name in this file.
// This includes the resource path to info.json in the constructor.
// Second:
// Open qPDAL.qrc, change the "prefix" and the icon filename for your plugin.
// Change the name of the file to <yourPluginName>.qrc
// Third:
// Open the info.json file and fill in the information about the plugin.
// "type" should be one of: "Standard", "GL", or "I/O" (required)
// "name" is the name of the plugin (required)
// "icon" is the Qt resource path to the plugin's icon (from the .qrc file)
// "description" is used as a tootip if the plugin has actions and is displayed in the plugin dialog
// "authors", "maintainers", and "references" show up in the plugin dialog as well
#include <map>
#include <QtGui>
#include <QEventLoop>
#include <QFormLayout>
#include <QLineEdit>
#include <QPushButton>
#include <pdal/filters/RangeFilter.hpp>
#include <pdal/PluginManager.hpp>
#include <pdal/StageFactory.hpp>
#include <pdal/PipelineManager.hpp>
#include <json/json.h>
#include "qPDAL.h"
#include "ccPDALReaders.h"
#include "ccPDALWriters.h"
#include "ListSelectionDialog.h"
bool startsWith(const std::string &str, const std::string &startStr)
{
if (str.size() < startStr.size())
{
return false;
}
return str.substr(0, startStr.size()) == startStr;
}
std::vector<QString> askForSelection(const std::vector<QString> &choices)
{
QEventLoop loop;
ListSelectionDialog dm(choices);
dm.show();
QObject::connect(&dm, &ListSelectionDialog::finished, &loop, &QEventLoop::quit);
loop.exec();
return dm.checkedItems();
}
// Default constructor:
// - pass the Qt resource path to the info.json file (from <yourPluginName>.qrc file)
// - constructor should mainly be used to initialize actions and other members
qPDAL::qPDAL(QObject *parent)
: QObject(parent), ccStdPluginInterface(":/CC/plugin/qPDAL/info.json"), m_action(nullptr)
{
}
// This method should enable or disable your plugin actions
// depending on the currently selected entities ('selectedEntities').
void qPDAL::onNewSelection(const ccHObject::Container &selectedEntities)
{
if (m_action == nullptr)
{
return;
}
// If you need to check for a specific type of object, you can use the methods
// in ccHObjectCaster.h or loop and check the objects' classIDs like this:
//
// for ( ccHObject *object : selectedEntities )
// {
// if ( object->getClassID() == CC_TYPES::VIEWPORT_2D_OBJECT )
// {
// // ... do something with the viewports
// }
// }
// For example - only enable our action if something is selected.
//m_action->setEnabled(!selectedEntities.empty());
m_action->setEnabled(true);
}
// This method returns all the 'actions' your plugin can perform.
// getActions() will be called only once, when plugin is loaded.
QList<QAction *> qPDAL::getActions()
{
// default action (if it has not been already created, this is the moment to do it)
if (!m_action)
{
// Here we use the default plugin name, description, and icon,
// but each action should have its own.
m_action = new QAction("PDAL filter", this);
m_action->setToolTip("Apply a PDAL filter to the selected cloud");
m_action->setIcon(getIcon());
// Connect appropriate signal
connect(m_action, &QAction::triggered, this, &qPDAL::doAction);
}
if (!m_executePipeline)
{
// Here we use the default plugin name, description, and icon,
// but each action should have its own.
m_executePipeline = new QAction("Execute Pipeline", this);
m_executePipeline->setToolTip("PDAL pipeline");
m_executePipeline->setIcon(getIcon());
// Connect appropriate signal
connect(m_executePipeline, &QAction::triggered, this, &qPDAL::executePipeline);
}
return {m_action, m_executePipeline};
}
// This is an example of an action's method called when the corresponding action
// is triggered (i.e. the corresponding icon or menu entry is clicked in CC's
// main interface). You can access most of CC's components (database,
// 3D views, console, etc.) via the 'm_app' variable (see the ccMainAppInterface
// class in ccMainAppInterface.h).
void qPDAL::doAction()
{
if (m_app == nullptr)
{
// m_app should have already been initialized by CC when plugin is loaded
Q_ASSERT(false);
return;
}
if (m_app->getSelectedEntities().empty() || m_app->getSelectedEntities().front()->getClassID() != CC_TYPES::POINT_CLOUD) {
return;
}
// Force plugin loading.
pdal::StageFactory f(false);
pdal::StringList allStageNames = pdal::PluginManager<pdal::Stage>::names();
pdal::StringList filtersNames;
std::copy_if(
allStageNames.begin(),
allStageNames.end(),
std::back_inserter(filtersNames),
[](const std::string &stageName)
{ return startsWith(stageName, "filters."); }
);
std::vector<QString> qFiltersNames;
qFiltersNames.reserve(filtersNames.size());
std::transform(
filtersNames.begin(),
filtersNames.end(),
std::back_inserter(qFiltersNames),
[](const std::string &filterName)
{ return QString::fromStdString(filterName); }
);
std::vector<QString> selectedStages = askForSelection(qFiltersNames);
if (selectedStages.empty())
{
m_app->dispToConsole("Select a stage", ccMainAppInterface::ERR_CONSOLE_MESSAGE);
return;
}
std::string selectedStage = selectedStages.front().toStdString();
m_app->dispToConsole(QString("Stage Selected: %1").arg(selectedStages.front()));
//std::unique_ptr<pdal::Stage> s = std::unique_ptr<pdal::Stage>(f.createStage(selectedStage));
pdal::Stage *s = f.createStage(selectedStage);
if (s == nullptr)
{
std::cerr << "Unable to create stage " << selectedStage << "\n";
return;
}
pdal::ProgramArgs args;
s->addAllArgs(args);
std::ostringstream ostr;
args.dump3(ostr);
std::string json = ostr.str();
Json::Reader jsonReader;
Json::Value array;
jsonReader.parse(json, array);
std::map<QString, QLineEdit*> optionInputs;
QWidget window;
QFormLayout form(&window);
// Intentionally skip the 3 last options for now
for (int i = array.size() - 1; i >= 3; --i)
{
QString optionName = QString::fromStdString(array[i]["name"].asString());
m_app->dispToConsole(optionName);
QLineEdit *optionEdit = new QLineEdit;
form.addRow(optionName, optionEdit);
optionInputs[optionName] = optionEdit;
}
QPushButton btn("Ok");
form.addRow(&btn);
QEventLoop loop;
window.show();
QObject::connect(&btn, &QPushButton::clicked, &loop, &QEventLoop::quit);
loop.exec();
pdal::Options filterOptions;
for (const auto& optionInput: optionInputs)
{
if (!optionInput.second->text().isEmpty()) {
filterOptions.add(optionInput.first.toStdString(), optionInput.second->text().toStdString());
m_app->dispToConsole(QString("%1: \"%2\"").arg(optionInput.first).arg(optionInput.second->text()));
}
}
ccPointCloud *inputCloud = ccHObjectCaster::ToPointCloud(m_app->getSelectedEntities().front());
if (inputCloud == nullptr) {
m_app->dispToConsole("FK", ccMainAppInterface::ERR_CONSOLE_MESSAGE);
return;
}
ccPointCloudStreamReader reader(inputCloud);
s->setInput(reader);
try
{
s->setOptions(filterOptions);
} catch (const std::exception& e) {
m_app->dispToConsole(QString::fromUtf8(e.what()), ccMainAppInterface::ERR_CONSOLE_MESSAGE);
return;
}
ccPointCloud *r = new ccPointCloud("lol");
ccPointCloudStreamWriter writer(r);
writer.setInput(*s);
if (writer.pipelineStreamable()) {
m_app->dispToConsole("[qPDAL] streaming mode");
pdal::FixedPointTable t(1000);
try
{
writer.prepare(t);
writer.execute(t);
} catch (const std::exception& e) {
m_app->dispToConsole(QString::fromUtf8(e.what()), ccMainAppInterface::ERR_CONSOLE_MESSAGE);
}
} else {
m_app->dispToConsole("[qPDAL] Non-streaming mode");
pdal::PointTable t;
try
{
writer.prepare(t);
writer.execute(t);
} catch (const std::exception& e) {
m_app->dispToConsole(QString::fromUtf8(e.what()), ccMainAppInterface::ERR_CONSOLE_MESSAGE);
}
}
m_app->addToDB(r);
}
void qPDAL::executePipeline()
{
}