ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ccCompassExport.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "ccCompassExport.h"
9 
10 #include <QtCompat.h>
11 #include <ecvDisplayTools.h>
12 
13 #include <QBuffer>
14 #include <QFileInfo>
15 #include <QXmlStreamWriter>
16 
17 #include "ccFitPlane.h"
18 #include "ccGeoObject.h"
19 #include "ccLineation.h"
20 #include "ccSNECloud.h"
21 #include "ccThickness.h"
22 #include "ccTrace.h"
23 #include "ecvFacet.h"
24 #include "ecvMainAppInterface.h"
25 #include "ecvPointCloud.h"
26 
27 namespace {
28 
29 int _writePlanes(ccHObject* rootObject,
30  ccHObject* object,
31  QTextStream* out,
32  const QString& parentName = QString()) {
33  // get object name
34  QString name;
35  if (parentName.isEmpty()) {
36  name = QStringLiteral("%1").arg(object->getName());
37  } else {
38  name = QStringLiteral("%1.%2").arg(parentName, object->getName());
39  }
40 
41  // find point cloud (biggest in project) to pull global shift & scale from
42  // n.b. ccPlanes do not store a global shift/scale like point clouds do,
43  // hence this hack. Will only cause issues if CC is being used with multiple
44  // point clouds that have different underlying coordinate systems (and hence
45  // shift and scales) - which I can't see happening too often. (in any case,
46  // 99% of the time the biggest point cloud in the project will be the model
47  // being interpreted)
48  ccPointCloud* ss = nullptr;
49  if (object->isKindOf(CV_TYPES::PLANE) | ccFitPlane::isFitPlane(object)) {
50  std::vector<ccHObject*> clouds;
51  rootObject->filterChildren(clouds, true, CV_TYPES::POINT_CLOUD, false);
52  unsigned int npoints = 0;
53  for (ccHObject* o : clouds) {
54  ccPointCloud* p = static_cast<ccPointCloud*>(o);
55  if (npoints <= p->size()) {
56  npoints = p->size();
57  ss = p;
58  }
59  }
60  }
61 
62  // is object a plane made by ccCompass?
63  int n = 0;
64  if (ccFitPlane::isFitPlane(object)) {
65  // write global position
66  ccPlane* P = static_cast<ccPlane*>(object);
67 
68  // Write object as
69  // Name,Strike,Dip,Dip_Dir,Cx,Cy,Cz,Nx,Ny,Nz,Radius,RMS,Gx,Gy,Gz,Length
70  *out << name << ",";
71  *out << object->getMetaData("Strike").toString() << ","
72  << object->getMetaData("Dip").toString() << ","
73  << object->getMetaData("DipDir").toString() << ",";
74  *out << object->getMetaData("Cx").toString() << ","
75  << object->getMetaData("Cy").toString() << ","
76  << object->getMetaData("Cz").toString() << ",";
77  *out << object->getMetaData("Nx").toString() << ","
78  << object->getMetaData("Ny").toString() << ","
79  << object->getMetaData("Nz").toString() << ",";
80  *out << object->getMetaData("Radius").toString() << ","
81  << object->getMetaData("RMS").toString() << ",";
82 
83  if (ss != nullptr) {
85  CCVector3d G = ss->toGlobal3d(L);
86 
87  *out << G.x << "," << G.y << "," << G.z << ",";
88  }
89 
90  // write length of trace associated with this plane
91  *out << std::max(P->getXWidth(), P->getYWidth()) << QtCompat::endl;
92  n++;
93  } else if (object->isKindOf(
94  CV_TYPES::PLANE)) // not one of our planes, but a plane
95  // anyway (so we'll export it)
96  {
97  // calculate plane orientation
98  // get plane normal vector
99  ccPlane* P = static_cast<ccPlane*>(object);
100  CCVector3 N(P->getNormal());
102 
103  // We always consider the normal with a positive 'Z' by default!
104  if (N.z < 0.0) N *= -1.0;
105 
106  // calculate strike/dip/dip direction
107  float strike = 0.0f;
108  float dip = 0.0f;
109  float dipdir = 0.0f;
112 
113  // export
114  *out << name << ",";
115  *out << strike << "," << dip << "," << dipdir
116  << ","; // write orientation
117  *out << L.x << "," << L.y << "," << L.z << ","; // write location
118  *out << N.x << "," << N.y << "," << N.z << ","; // write normal
119  *out << "NA"
120  << ","
121  << "UNK"
122  << ","; // the "radius" and "RMS" are unknown
123 
124  // write global position
125  if (ss != nullptr) {
126  CCVector3d G = ss->toGlobal3d(L);
127  *out << G.x << "," << G.y << "," << G.z << QtCompat::endl;
128  } else {
129  *out << QtCompat::endl;
130  }
131 
132  n++;
133  }
134 
135  // write all children
136  for (unsigned i = 0; i < object->getChildrenNumber(); i++) {
137  ccHObject* o = object->getChild(i);
138  n += _writePlanes(rootObject, o, out, name);
139  }
140 
141  return n;
142 }
143 
144 int _writeTraces(ccHObject* object,
145  QTextStream* out,
146  const QString& parentName = QString()) {
147  // get object name
148  QString name;
149  if (parentName.isEmpty()) {
150  name = QStringLiteral("%1").arg(object->getName());
151  } else {
152  name = QStringLiteral("%1.%2").arg(parentName, object->getName());
153  }
154 
155  // is object a polyline
156  int n = 0;
157  if (ccTrace::isTrace(object)) // ensure this is a trace
158  {
159  ccTrace* p = static_cast<ccTrace*>(object);
160 
161  // loop through points
162  CCVector3 start;
163  CCVector3 end;
164  int cost;
165  int tID = object->getUniqueID();
166  if (p->size() >= 2) {
167  // set cost function
168  ccTrace::COST_MODE = p->getMetaData("cost_function").toInt();
169 
170  // loop through segments
171  for (unsigned i = 1; i < p->size(); i++) {
172  // get points
173  p->getPoint(i - 1, start);
174  p->getPoint(i, end);
175 
176  // calculate segment cost
177  cost = p->getSegmentCost(p->getPointGlobalIndex(i - 1),
178  p->getPointGlobalIndex(i));
179 
180  // write data
181  // n.b. csv columns are
182  // name,trace_id,seg_id,start_x,start_y,start_z,end_x,end_y,end_z,
183  // cost, cost_mode
184  *out << name << ","; // name
185  *out << tID << ",";
186  *out << i - 1 << ",";
187  *out << p->toGlobal3d(start).x << ",";
188  *out << p->toGlobal3d(start).y << ",";
189  *out << p->toGlobal3d(start).z << ",";
190  *out << p->toGlobal3d(end).x << ",";
191  *out << p->toGlobal3d(end).y << ",";
192  *out << p->toGlobal3d(end).z << ",";
193  *out << cost << ",";
195  }
196  }
197  n++;
198  }
199 
200  // write all children
201  for (unsigned i = 0; i < object->getChildrenNumber(); i++) {
202  ccHObject* o = object->getChild(i);
203  n += _writeTraces(o, out, name);
204  }
205 
206  return n;
207 }
208 
209 int _writeLineations(ccHObject* object,
210  QTextStream* out,
211  const QString& parentName = QString(),
212  bool thicknesses = false) {
213  // get object name
214  QString name;
215  if (parentName.isEmpty()) {
216  name = QStringLiteral("%1").arg(object->getName());
217  } else {
218  name = QStringLiteral("%1.%2").arg(parentName, object->getName());
219  }
220 
221  // is object a lineation made by ccCompass?
222  int n = 0;
223  if (((thicknesses == false) &&
224  ccLineation::isLineation(object)) | // lineation measurement
225  ((thicknesses == true) &&
226  ccThickness::isThickness(object))) // or thickness measurement
227  {
228  // Write object as Name,Sx,Sy,Sz,Ex,Ey,Ez,Trend,Plunge
229  *out << name << ",";
230  *out << object->getMetaData("Sx").toString() << ","
231  << object->getMetaData("Sy").toString() << ","
232  << object->getMetaData("Sz").toString() << ",";
233  *out << object->getMetaData("Ex").toString() << ","
234  << object->getMetaData("Ey").toString() << ","
235  << object->getMetaData("Ez").toString() << ",";
236  *out << object->getMetaData("Trend").toString() << ","
237  << object->getMetaData("Plunge").toString() << ","
238  << object->getMetaData("Length").toString() << QtCompat::endl;
239  n++;
240  }
241 
242  // write all children
243  for (unsigned i = 0; i < object->getChildrenNumber(); i++) {
244  ccHObject* o = object->getChild(i);
245  n += _writeLineations(o, out, name, thicknesses);
246  }
247 
248  return n;
249 }
250 
251 int _writeTracesSVG(const ccGLCameraParameters& cameraParams,
252  ccHObject* object,
253  QTextStream* out,
254  int height,
255  float zoom) {
256  int n = 0;
257 
258  // is this a drawable polyline?
259  if (object->isA(CV_TYPES::POLY_LINE) || ccTrace::isTrace(object)) {
260  // get polyline object
261  ccPolyline* line = static_cast<ccPolyline*>(object);
262 
263  if (!line->isVisible()) {
264  return 0; // as soon as something is not visible we bail
265  }
266 
267  // write polyline header
268  *out << "<polyline fill=\"none\" stroke=\"black\" points=\"";
269 
270  // write point string
271  for (unsigned i = 0; i < line->size(); i++) {
272  // get point in world coordinates
273  CCVector3 P = *line->getPoint(i);
274 
275  // project 3D point into 2D
276  CCVector3d coords2D;
277  cameraParams.project(P, coords2D);
278 
279  // write point
280  *out << QString::asprintf(
281  "%.3f,%.3f ", coords2D.x * zoom,
282  height - (coords2D.y *
283  zoom)); // n.b. we need to flip y-axis
284  }
285 
286  // end polyline
287  *out << "\"/>" << QtCompat::endl;
288 
289  n++; // a polyline has been written
290  }
291 
292  // recurse on children
293  for (unsigned i = 0; i < object->getChildrenNumber(); i++) {
294  n += _writeTracesSVG(cameraParams, object->getChild(i), out, height,
295  zoom);
296  }
297 
298  return n;
299 }
300 
301 // recursively write the provided ccHObject and its children
302 int _writeObjectXML(ccHObject* object, QXmlStreamWriter* out) {
303  int n = 1;
304  // write object header based on type
305  if (ccGeoObject::isGeoObject(object)) {
306  // write GeoObject
307  out->writeStartElement("GEO_OBJECT");
308  } else if (object->isA(CV_TYPES::PLANE)) {
309  // write fitPlane
310  out->writeStartElement("PLANE");
311  } else if (ccTrace::isTrace(object)) {
312  // write trace
313  out->writeStartElement("TRACE");
314  } else if (ccThickness::isThickness(object)) {
315  // write thickness
316  out->writeStartElement("THICKNESS");
317  } else if (ccSNECloud::isSNECloud(object)) {
318  out->writeStartElement("SNE");
319  } else if (ccLineation::isLineation(object)) {
320  // write lineation
321  out->writeStartElement("LINEATION");
322  } else if (object->isA(CV_TYPES::POINT_CLOUD)) {
323  out->writeStartElement("CLOUD");
324  } else if (object->isA(CV_TYPES::POLY_LINE)) {
325  // write polyline (note that this will ignore "trace" polylines as they
326  // have been grabbed earlier)
327  out->writeStartElement("POLYLINE");
328  } else if (object->isA(CV_TYPES::HIERARCHY_OBJECT)) {
329  // write container
330  out->writeStartElement(
331  "CONTAINER"); // QString::asprintf("CONTAINER name = '%s' id =
332  // %d", object->getName(), object->getUniqueID())
333  } else if (object->isA(CV_TYPES::FACET)) {
334  // write container
335  out->writeStartElement("FACET");
336  } else // just write name,id and metadata for unknown objects
337  {
338  out->writeStartElement("OBJECT");
339  }
340 
341  // write name and oid attributes
342  out->writeAttribute("name", object->getName());
343  out->writeAttribute("id", QString::asprintf("%d", object->getUniqueID()));
344 
345  // write metadata tags (these contain the data)
346  const auto& metaData = object->metaData();
347 
348  for (const auto& key : metaData.keys()) {
349  out->writeTextElement(key, metaData.value(key).toString());
350  }
351 
352  // special case - we can calculate all metadata from a plane
353  if (object->isA(CV_TYPES::PLANE)) {
354  ccPlane* P = static_cast<ccPlane*>(object);
355 
356  // write length
357  out->writeTextElement(
358  "Length", QString::asprintf("%f", std::max(P->getXWidth(),
359  P->getYWidth())));
360 
361  // if this is just an ordinary plane, make a corresponding fitplane
362  // object and then steal metadata
363  if (!ccFitPlane::isFitPlane(P)) {
364  // build fitplane object
365  ccFitPlane* temp = new ccFitPlane(P);
366 
367  // write metadata
368  const auto& tempMetaData = temp->metaData();
369 
370  for (const auto& key : tempMetaData.keys()) {
371  out->writeTextElement(key, tempMetaData.value(key).toString());
372  }
373 
374  // cleanup
375  delete temp;
376  }
377  }
378 
379  // if object is a polyline object (or a trace) write trace points and
380  // normals
381  if (object->isA(CV_TYPES::POLY_LINE)) {
382  ccPolyline* poly = static_cast<ccPolyline*>(object);
383  ccTrace* trace = nullptr;
384  if (ccTrace::isTrace(object)) {
385  trace = static_cast<ccTrace*>(object);
386  }
387 
388  QString x;
389  QString y;
390  QString z;
391  QString nx;
392  QString ny;
393  QString nz;
394  QString cost;
395  QString wIDs;
396  QString w_local_ids;
397 
398  // loop through points
399  // position
400  CCVector3 p1;
401  CCVector3 p2;
402 
403  // normal vector (if defined)
404  CCVector3 n1;
405  CCVector3 n2;
406 
407  // becomes true if any valid normals are recieved
408  bool hasNormals = false;
409 
410  if (poly->size() >= 2) {
411  // loop through segments
412  for (unsigned i = 1; i < poly->size(); i++) {
413  // get points
414  poly->getPoint(i - 1, p1); // segment start point
415  poly->getPoint(i, p2); // segment end point
416 
417  // store data to buffers
418  x += QString::asprintf("%f,", p1.x);
419  y += QString::asprintf("%f,", p1.y);
420  z += QString::asprintf("%f,", p1.z);
421 
422  // write data specific to traces
423  if (trace) {
424  int c = trace->getSegmentCost(
425  trace->getPointGlobalIndex(i - 1),
426  trace->getPointGlobalIndex(i));
427  cost += QString::asprintf("%d,", c);
428 
429  // write point normals (if this is a trace)
430  n2 = trace->getPointNormal(i);
431  nx += QString::asprintf("%f,", n1.x);
432  ny += QString::asprintf("%f,", n1.y);
433  nz += QString::asprintf("%f,", n1.z);
434  if (!hasNormals && !(n1.x == 0 && n1.y == 0 && n1.z == 0)) {
435  hasNormals =
436  true; // this was a non-null normal estimate -
437  // we will write normals now
438  }
439  }
440  }
441 
442  // store last point
443  x += QString::asprintf("%f", p2.x);
444  y += QString::asprintf("%f", p2.y);
445  z += QString::asprintf("%f", p2.z);
446  if (hasNormals) // normal
447  {
448  nx += QString::asprintf("%f", n2.x);
449  ny += QString::asprintf("%f", n2.y);
450  nz += QString::asprintf("%f", n2.z);
451  }
452  if (trace) // cost
453  {
454  cost += QStringLiteral("0");
455  }
456 
457  // if this is a trace also write the waypoints
458  if (trace) {
459  // get ids (on the cloud) for waypoints
460  for (int w = 0; w < trace->waypoint_count(); w++) {
461  wIDs += QString::asprintf("%d,", trace->getWaypoint(w));
462  }
463 
464  // get ids (vertex # in polyline) for waypoints
465  for (int w = 0; w < trace->waypoint_count(); w++) {
466  // get id of waypoint in cloud
467  int globalID = trace->getWaypoint(w);
468 
469  // find corresponding point in trace
470  unsigned i = 0;
471  for (; i < trace->size(); i++) {
472  if (trace->getPointGlobalIndex(i) == globalID) {
473  break; // found it!;
474  }
475  }
476 
477  // write this points local index
478  w_local_ids += QString::asprintf("%d,", i);
479  }
480  }
481 
482  // write points
483  out->writeStartElement("POINTS");
484  out->writeAttribute("count", QString::asprintf("%d", poly->size()));
485 
486  if (hasNormals) {
487  out->writeAttribute("normals", "True");
488  } else {
489  out->writeAttribute("normals", "False");
490  }
491 
492  out->writeTextElement("x", x);
493  out->writeTextElement("y", y);
494  out->writeTextElement("z", z);
495 
496  if (hasNormals) {
497  out->writeTextElement("nx", nx);
498  out->writeTextElement("ny", ny);
499  out->writeTextElement("nz", nz);
500  }
501 
502  if (trace) {
503  // write waypoints
504  out->writeTextElement("cost", cost);
505  out->writeTextElement("control_point_cloud_ids", wIDs);
506  out->writeTextElement("control_point_local_ids", w_local_ids);
507  }
508 
509  // fin!
510  out->writeEndElement();
511  }
512  }
513 
514  // if object is a point cloud write global shift and scale
515  if (object->isA(CV_TYPES::POINT_CLOUD)) {
516  ccPointCloud* cloud = static_cast<ccPointCloud*>(object);
517  out->writeTextElement("GLOBAL_SCALE",
518  QString::asprintf("%f", cloud->getGlobalScale()));
519  out->writeTextElement(
520  "GLOBAL_X", QString::asprintf("%f", cloud->getGlobalShift().x));
521  out->writeTextElement(
522  "GLOBAL_Y", QString::asprintf("%f", cloud->getGlobalShift().y));
523  out->writeTextElement(
524  "GLOBAL_Z", QString::asprintf("%f", cloud->getGlobalShift().z));
525 
526  // for SNE clouds write all points, point normals and scalar fields
527  if (ccSNECloud::isSNECloud(object)) {
528  // write header for point data
529  out->writeStartElement("POINTS");
530  out->writeAttribute("count",
531  QString::asprintf("%d", cloud->size()));
532 
533  // gather data strings
534  QString x;
535  QString y;
536  QString z;
537  QString nx;
538  QString ny;
539  QString nz;
540  QString thickness;
541  QString weight;
542  QString trend;
543  QString plunge;
545  cloud->getScalarFieldIndexByName("Weight"));
546  cloudViewer::ScalarField* trendSF = cloud->getScalarField(
547  cloud->getScalarFieldIndexByName("Trend"));
548  cloudViewer::ScalarField* plungeSF = cloud->getScalarField(
549  cloud->getScalarFieldIndexByName("Plunge"));
550 
552  cloud->getScalarFieldIndexByName("Thickness"));
553  for (unsigned p = 0; p < cloud->size(); p++) {
554  x += QString::asprintf("%f,", cloud->getPoint(p)->x);
555  y += QString::asprintf("%f,", cloud->getPoint(p)->y);
556  z += QString::asprintf("%f,", cloud->getPoint(p)->z);
557  nx += QString::asprintf("%f,", cloud->getPointNormal(p).x);
558  ny += QString::asprintf("%f,", cloud->getPointNormal(p).y);
559  nz += QString::asprintf("%f,", cloud->getPointNormal(p).z);
560  weight += QString::asprintf("%f,", wSF->getValue(p));
561  trend += QString::asprintf("%f,", trendSF->getValue(p));
562  plunge += QString::asprintf("%f,", plungeSF->getValue(p));
563 
564  if (tSF !=
565  nullptr) // can be null if no thickness was estimated!
566  {
567  thickness += QString::asprintf("%f,", tSF->getValue(p));
568  }
569  }
570 
571  // write
572  out->writeTextElement("x", x);
573  out->writeTextElement("y", y);
574  out->writeTextElement("z", z);
575  out->writeTextElement("nx", nx);
576  out->writeTextElement("ny", ny);
577  out->writeTextElement("nz", nz);
578  out->writeTextElement("weight", weight);
579  out->writeTextElement("trend", trend);
580  out->writeTextElement("plunge", plunge);
581  if (tSF != nullptr) {
582  out->writeTextElement("thickness", thickness);
583  }
584 
585  // fin
586  out->writeEndElement();
587  }
588  }
589 
590  // write facet data
591  if (object->isA(CV_TYPES::FACET)) {
592  // write orientation
593  ccFacet* f = static_cast<ccFacet*>(object);
594  out->writeTextElement("Nx", QString::asprintf("%f", f->getNormal().x));
595  out->writeTextElement("Ny", QString::asprintf("%f", f->getNormal().y));
596  out->writeTextElement("Nz", QString::asprintf("%f", f->getNormal().z));
597  out->writeTextElement("Cx", QString::asprintf("%f", f->getCenter().x));
598  out->writeTextElement("Cy", QString::asprintf("%f", f->getCenter().y));
599  out->writeTextElement("Cz", QString::asprintf("%f", f->getCenter().z));
600  out->writeTextElement("rms", QString::asprintf("%f", f->getRMS()));
601  out->writeTextElement("surface",
602  QString::asprintf("%f", f->getSurface()));
603  }
604 
605  // write children
606  for (unsigned i = 0; i < object->getChildrenNumber(); i++) {
607  n += _writeObjectXML(object->getChild(i), out);
608  }
609 
610  // close this object
611  out->writeEndElement();
612 
613  return n;
614 }
615 
616 } // namespace
617 
618 namespace ccCompassExport {
619 
620 void saveCSV(ecvMainAppInterface* app, const QString& filename) {
621  // write a whole bunch of .csv files
622 
623  int planes = 0; // keep track of how many objects are being written (used
624  // to delete empty files)
625  int traces = 0;
626  int lineations = 0;
627  int thicknesses = 0;
628 
629  // build filenames
630  QFileInfo fi(filename);
631 
632  QString baseName = fi.absolutePath() + "/" + fi.completeBaseName();
633  QString ext = fi.suffix();
634  if (!ext.isEmpty()) {
635  ext.prepend('.');
636  }
637  QString plane_fn = baseName + QStringLiteral("_planes") + ext;
638  QString trace_fn = baseName + QStringLiteral("_traces") + ext;
639  QString lineation_fn = baseName + QStringLiteral("_lineations") + ext;
640  QString thickness_fn = baseName + QStringLiteral("_thickness") + ext;
641 
642  // create files
643  QFile plane_file(plane_fn);
644  QFile trace_file(trace_fn);
645  QFile lineation_file(lineation_fn);
646  QFile thickness_file(thickness_fn);
647 
648  // open files
649  if (plane_file.open(QIODevice::WriteOnly) &&
650  trace_file.open(QIODevice::WriteOnly) &&
651  lineation_file.open(QIODevice::WriteOnly) &&
652  thickness_file.open(QIODevice::WriteOnly)) {
653  // create text streams for each file
654  QTextStream plane_stream(&plane_file);
655  QTextStream trace_stream(&trace_file);
656  QTextStream lineation_stream(&lineation_file);
657  QTextStream thickness_stream(&thickness_file);
658 
659  // write headers
660  plane_stream << "Name,Strike,Dip,Dip_Dir,Cx,Cy,Cz,Nx,Ny,Nz,Sample_"
661  "Radius,RMS,Gx,Gy,Gz,Length"
662  << QtCompat::endl;
663  trace_stream << "Name,Trace_id,Point_id,Start_x,Start_y,Start_z,End_x,"
664  "End_y,End_z,Cost,Cost_Mode"
665  << QtCompat::endl;
666  lineation_stream << "Name,Sx,Sy,Sz,Ex,Ey,Ez,Trend,Plunge,Length"
667  << QtCompat::endl;
668  thickness_stream << "Name,Sx,Sy,Sz,Ex,Ey,Ez,Trend,Plunge,Thickness"
669  << QtCompat::endl;
670 
671  // write data for all objects in the db tree (n.b. we loop through the
672  // dbRoots children rathern than just passing db_root so the naming is
673  // correct)
674  for (unsigned i = 0; i < app->dbRootObject()->getChildrenNumber();
675  i++) {
676  ccHObject* rootObject = app->dbRootObject();
677  ccHObject* o = rootObject->getChild(i);
678 
679  planes += _writePlanes(rootObject, o, &plane_stream);
680  traces += _writeTraces(o, &trace_stream);
681  lineations +=
682  _writeLineations(o, &lineation_stream, QString(), false);
683  thicknesses +=
684  _writeLineations(o, &thickness_stream, QString(), true);
685  }
686 
687  // cleanup
688  plane_stream.flush();
689  plane_file.close();
690  trace_stream.flush();
691  trace_file.close();
692  lineation_stream.flush();
693  lineation_file.close();
694  thickness_stream.flush();
695  thickness_file.close();
696 
697  // ensure data has been written (and if not, delete the file)
698  if (planes) {
699  app->dispToConsole("[ccCompass] Successfully exported plane data.",
701  } else {
702  app->dispToConsole("[ccCompass] No plane data found.",
704  plane_file.remove();
705  }
706  if (traces) {
707  app->dispToConsole("[ccCompass] Successfully exported trace data.",
709  } else {
710  app->dispToConsole("[ccCompass] No trace data found.",
712  trace_file.remove();
713  }
714  if (lineations) {
715  app->dispToConsole(
716  "[ccCompass] Successfully exported lineation data.",
718  } else {
719  app->dispToConsole("[ccCompass] No lineation data found.",
721  lineation_file.remove();
722  }
723  if (thicknesses) {
724  app->dispToConsole(
725  "[ccCompass] Successfully exported thickness data.",
727  } else {
728  app->dispToConsole("[ccCompass] No thickness data found.",
730  thickness_file.remove();
731  }
732  } else {
733  app->dispToConsole(
734  "[ccCompass] Could not open output files... ensure CC has "
735  "write access to this location.",
737  }
738 }
739 
740 void saveSVG(ecvMainAppInterface* app, const QString& filename, float zoom) {
741  // set all objects except the point clouds invisible
742  std::vector<ccHObject*>
743  hidden; // store objects we hide so we can turn them back on after!
744  ccHObject::Container objects;
745  app->dbRootObject()->filterChildren(objects, true, CV_TYPES::OBJECT,
746  false); // get list of all children!
747  for (ccHObject* o : objects) {
748  if (!o->isA(CV_TYPES::POINT_CLOUD)) {
749  if (o->isVisible()) {
750  hidden.push_back(o);
751  o->setVisible(false);
752  }
753  }
754  }
755 
756  // render the scene
757  // QImage img = app->getActiveWindow()->renderToImage(zoom);
758  QFileInfo fileInfo(filename);
759  QString pngFile = QString("%1/%2.png")
760  .arg(fileInfo.absolutePath())
761  .arg(fileInfo.baseName());
762  ecvDisplayTools::RenderToFile(pngFile, zoom);
763 
764  // restore visibility
765  for (ccHObject* o : hidden) {
766  o->setVisible(true);
767  }
768 
769  // convert image to base64 (png format) to write in svg file
770  QImage img;
771  if (!img.load(pngFile)) {
772  QFile(pngFile).remove();
773  return;
774  }
775 
776  QByteArray ba;
777  QBuffer bu(&ba);
778  bu.open(QIODevice::WriteOnly);
779  img.save(&bu, "PNG");
780  bu.close();
781  QFile(pngFile).remove();
782 
783  // create .svg file
784  QFile svg_file(filename);
785  // open file & create text stream
786  if (svg_file.open(QIODevice::WriteOnly)) {
787  QTextStream svg_stream(&svg_file);
788 
789  // GlWidth and GlHeight are negative on some machines??
790  int width =
791  std::abs(static_cast<int>(ecvDisplayTools::GlWidth() * zoom));
792  int height =
793  std::abs(static_cast<int>(ecvDisplayTools::GlHeight() * zoom));
794 
795  // write svg header
796  svg_stream << QString::asprintf("<svg width=\"%d\" height=\"%d\">",
797  width, height)
798  << QtCompat::endl;
799 
800  // write the image
801  svg_stream << QString::asprintf(
802  "<image height = \"%d\" width = \"%d\" "
803  "xlink:href = \"data:image/png;base64,",
804  height, width)
805  << ba.toBase64() << "\"/>" << QtCompat::endl;
806 
807  // recursively write traces
810  if (params.perspective) {
813  params); // get updated params
814  }
815 
816  int count = _writeTracesSVG(params, app->dbRootObject(), &svg_stream,
817  height, zoom);
818 
819  // TODO: write scale bar
820 
821  // write end tag for svg file
822  svg_stream << "</svg>" << QtCompat::endl;
823 
824  // close file
825  svg_stream.flush();
826  svg_file.close();
827 
828  if (count > 0) {
829  app->dispToConsole(QString::asprintf(
830  "[ccCompass] Successfully saved %d polylines to .svg file.",
831  count));
832  } else {
833  // remove file
834  svg_file.remove();
835  app->dispToConsole(
836  "[ccCompass] Could not write polylines to .svg - no "
837  "polylines found!",
839  }
840  }
841 }
842 
843 void saveXML(ecvMainAppInterface* app, const QString& filename) {
844  // open output stream
845  QFile file(filename);
846 
847  if (file.open(QIODevice::WriteOnly)) // open the file
848  {
849  QXmlStreamWriter xmlWriter(&file); // open xml stream;
850 
851  xmlWriter.setAutoFormatting(true);
852  xmlWriter.writeStartDocument();
853 
854  // find root node
855  ccHObject* rootObject = app->dbRootObject();
856  if (rootObject->getChildrenNumber() == 1) {
857  rootObject = rootObject->getChild(
858  0); // HACK - often the root only has one child (a .bin
859  // file); if so, move down a level
860  }
861 
862  /*ccHObject::Container pointClouds;
863  rootObject->filterChildren(&pointClouds, true, CV_TYPES::POINT_CLOUD,
864  true);*/
865 
866  // write data tree
867  _writeObjectXML(rootObject, &xmlWriter);
868 
869  // write end of document
870  xmlWriter.writeEndDocument();
871 
872  // close
873  file.flush();
874  file.close();
875 
876  app->dispToConsole(
877  "[ccCompass] Successfully exported data-tree to xml.",
879  } else {
880  app->dispToConsole(
881  "[ccCompass] Could not open output files... ensure CC has "
882  "write access to this location.",
884  }
885 }
886 } // namespace ccCompassExport
std::string filename
int width
int size
std::string name
int height
int count
cmdLineReadable * params[]
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
virtual bool isVisible() const
Returns whether entity is visible or not.
Facet.
Definition: ecvFacet.h:25
double getSurface() const
Returns associated surface area.
Definition: ecvFacet.h:70
CCVector3 getNormal() const override
Returns the entity normal.
Definition: ecvFacet.h:63
double getRMS() const
Returns associated RMS.
Definition: ecvFacet.h:68
const CCVector3 & getCenter() const
Returns the facet center.
Definition: ecvFacet.h:78
static bool isFitPlane(ccHObject *object)
Definition: ccFitPlane.cpp:99
Vector3Tpl< T > getTranslationAsVec3D() const
Returns a copy of the translation as a CCVector3.
virtual ccGLMatrix & getTransformation()
Returns the transformation that is currently applied to the vertices.
static bool isGeoObject(ccHObject *object)
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
unsigned getChildrenNumber() const
Returns the number of children.
Definition: ecvHObject.h:312
unsigned filterChildren(Container &filteredChildren, bool recursive=false, CV_CLASS_ENUM filter=CV_TYPES::OBJECT, bool strict=false) const
Collects the children corresponding to a certain pattern.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
ccHObject * getChild(unsigned childPos) const
Returns the ith child.
Definition: ecvHObject.h:325
static bool isLineation(ccHObject *obj)
static void ConvertNormalToStrikeAndDip(const CCVector3 &N, PointCoordinateType &strike_deg, PointCoordinateType &dip_deg)
static void ConvertNormalToDipAndDipDir(const CCVector3 &N, PointCoordinateType &dip_deg, PointCoordinateType &dipDir_deg)
Converts a normal vector to geological 'dip direction & dip' parameters.
const QVariantMap & metaData() const
Returns meta-data map (const only)
Definition: ecvObject.h:184
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
virtual unsigned getUniqueID() const
Returns object unique ID.
Definition: ecvObject.h:86
bool isA(CV_CLASS_ENUM type) const
Definition: ecvObject.h:131
QVariant getMetaData(const QString &key) const
Returns a given associated meta data.
bool isKindOf(CV_CLASS_ENUM type) const
Definition: ecvObject.h:128
Plane (primitive)
Definition: ecvPlane.h:18
CCVector3 getNormal() const override
Returns the entity normal.
Definition: ecvPlane.h:73
PointCoordinateType getXWidth() const
Returns 'X' width.
Definition: ecvPlane.h:50
PointCoordinateType getYWidth() const
Returns 'Y' width.
Definition: ecvPlane.h:53
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
const CCVector3 & getPointNormal(unsigned pointIndex) const override
Returns normal corresponding to a given point.
Colored polyline.
Definition: ecvPolyline.h:24
static bool isSNECloud(ccHObject *obj)
Definition: ccSNECloud.cpp:36
CCVector3d toGlobal3d(const Vector3Tpl< T > &Plocal) const
Returns the point back-projected into the original coordinates system.
virtual const CCVector3d & getGlobalShift() const
Returns the shift applied to original coordinates.
virtual double getGlobalScale() const
Returns the scale applied to original coordinates.
static bool isThickness(ccHObject *obj)
static int COST_MODE
Definition: ccTrace.h:203
size_t waypoint_count() const
Definition: ccTrace.h:130
static bool isTrace(ccHObject *object)
Definition: ccTrace.cpp:1102
CCVector3f getPointNormal(int pointIdx)
Definition: ccTrace.h:110
int getSegmentCost(int p1, int p2)
Definition: ccTrace.cpp:450
int getWaypoint(int n)
Definition: ccTrace.h:93
int getScalarFieldIndexByName(const char *name) const
Returns the index of a scalar field represented by its name.
ScalarField * getScalarField(int index) const
Returns a pointer to a specific scalar field.
unsigned size() const override
Definition: PointCloudTpl.h:38
const CCVector3 * getPoint(unsigned index) const override
unsigned size() const override
Returns the number of points.
virtual unsigned getPointGlobalIndex(unsigned localIndex) const
const CCVector3 * getPoint(unsigned index) const override
Returns the ith point.
A simple scalar field (to be associated to a point cloud)
Definition: ScalarField.h:25
ScalarType & getValue(std::size_t index)
Definition: ScalarField.h:92
static bool RenderToFile(QString filename, float zoomFactor=1.0f, bool dontScaleFeatures=false, bool renderOverlayItems=false)
Renders screen to a file.
static void GetGLCameraParameters(ccGLCameraParameters &params)
Returns the current OpenGL camera parameters.
static int GlWidth()
Returns the OpenGL context width.
static int GlHeight()
Returns the OpenGL context height.
static void SetPerspectiveState(bool state, bool objectCenteredView)
Set perspective state/mode.
Main application interface (for plugins)
virtual ccHObject * dbRootObject()=0
Returns DB root (as a ccHObject)
virtual void dispToConsole(QString message, ConsoleMessageLevel level=STD_CONSOLE_MESSAGE)=0
__host__ __device__ int2 abs(int2 v)
Definition: cutil_math.h:1267
int max(int a, int b)
Definition: cutil_math.h:48
@ HIERARCHY_OBJECT
Definition: CVTypes.h:103
@ POINT_CLOUD
Definition: CVTypes.h:104
@ POLY_LINE
Definition: CVTypes.h:112
@ FACET
Definition: CVTypes.h:109
@ PLANE
Definition: CVTypes.h:120
@ OBJECT
Definition: CVTypes.h:102
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
void saveSVG(ecvMainAppInterface *app, const QString &filename, float zoom)
void saveCSV(ecvMainAppInterface *app, const QString &filename)
void saveXML(ecvMainAppInterface *app, const QString &filename)
OpenGL camera parameters.
bool project(const CCVector3d &input3D, CCVector3d &output2D, bool *inFrustum=nullptr) const
Projects a 3D point in 2D (+ normalized 'z' coordinate)