ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
WriterImpl.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010 Stan Coleby (scoleby@intelisum.com)
3  * Copyright (c) 2020 PTC Inc.
4  *
5  * Permission is hereby granted, free of charge, to any person or organization
6  * obtaining a copy of the software and accompanying documentation covered by
7  * this license (the "Software") to use, reproduce, display, distribute,
8  * execute, and transmit the Software, and to prepare derivative works of the
9  * Software, and to permit third-parties to whom the Software is furnished to
10  * do so, all subject to the following:
11  *
12  * The copyright notices in the Software and this entire statement, including
13  * the above license grant, this restriction and the following disclaimer,
14  * must be included in all copies of the Software, in whole or in part, and
15  * all derivative works of the Software, unless such copies or derivative
16  * works are solely in the form of machine-executable object code generated by
17  * a source language processor.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25  * DEALINGS IN THE SOFTWARE.
26  */
27 
28 #include "WriterImpl.h"
29 
30 #include "Common.h"
31 #include <cmath>
32 
33 namespace e57
34 {
35 
36  WriterImpl::WriterImpl( const ustring &filePath, const ustring &coordinateMetadata ) :
37  imf_( filePath, "w" ), root_( imf_.root() ), data3D_( imf_, true ), images2D_( imf_, true )
38  {
39  // We are using the E57 v1.0 data format standard fieldnames.
40  // The standard fieldnames are used without an extension prefix (in the default namespace).
41  // We explicitly register it for completeness (the reference implementaion would do it for us, if we didn't).
42  imf_.extensionsAdd( "", E57_V1_0_URI );
43 
44  // Set per-file properties.
45  // Path names: "/formatName", "/majorVersion", "/minorVersion", "/coordinateMetadata"
46  root_.set( "formatName", StringNode( imf_, "ASTM E57 3D Imaging Data File" ) );
47  root_.set( "guid", StringNode( imf_, generateRandomGUID() ) );
48 
49  // Get ASTM version number supported by library, so can write it into file
50  int astmMajor;
51  int astmMinor;
52  ustring libraryId;
53  Utilities::getVersions( astmMajor, astmMinor, libraryId );
54 
55  root_.set( "versionMajor", IntegerNode( imf_, astmMajor ) );
56  root_.set( "versionMinor", IntegerNode( imf_, astmMinor ) );
57  root_.set( "e57LibraryVersion", StringNode( imf_, libraryId ) );
58 
59  // Save a dummy string for coordinate system.
60  // Really should be a valid WKT string identifying the coordinate reference system (CRS).
61  if ( !coordinateMetadata.empty() )
62  {
63  root_.set( "coordinateMetadata", StringNode( imf_, coordinateMetadata ) );
64  }
65 
66 // Create creationDateTime structure
67 // Path name: "/creationDateTime
68 // TODO currently no support for handling UTC <-> GPS time conversions
69 // note that "creationDateTime" is optional in the standard
70 #if 0
71  StructureNode creationDateTime = StructureNode(imf_);
72  creationDateTime.set("dateTimeValue", FloatNode(imf_, GetGPSTime()));
73  creationDateTime.set("isAtomicClockReferenced", IntegerNode(imf_,0));
74  root_.set("creationDateTime", creationDateTime);
75 #endif
76 
77  root_.set( "data3D", data3D_ );
78  root_.set( "images2D", images2D_ );
79  }
80 
82  {
83  if ( IsOpen() )
84  {
85  Close();
86  }
87  }
88 
89  bool WriterImpl::IsOpen() const
90  {
91  return imf_.isOpen();
92  }
93 
95  {
96  if ( !IsOpen() )
97  {
98  return false;
99  }
100 
101  imf_.close();
102  return true;
103  }
104 
106  {
107  return root_;
108  }
109 
111  {
112  return data3D_;
113  }
114 
116  {
117  return images2D_;
118  }
119 
121  {
122  return imf_;
123  }
124 
125  int64_t WriterImpl::NewImage2D( Image2D &image2DHeader )
126  {
127  int64_t pos = -1;
128 
130  images2D_.append( image );
131  pos = images2D_.childCount() - 1;
132 
133  if ( image2DHeader.guid.empty() )
134  {
135  image2DHeader.guid = generateRandomGUID();
136  }
137 
138  image.set( "guid", StringNode( imf_, image2DHeader.guid ) ); // required
139 
140  if ( !image2DHeader.name.empty() )
141  {
142  image.set( "name", StringNode( imf_, image2DHeader.name ) );
143  }
144  if ( !image2DHeader.description.empty() )
145  {
146  image.set( "description", StringNode( imf_, image2DHeader.description ) );
147  }
148 
149  // Add various sensor and version strings to image.
150  if ( !image2DHeader.sensorVendor.empty() )
151  {
152  image.set( "sensorVendor", StringNode( imf_, image2DHeader.sensorVendor ) );
153  }
154  if ( !image2DHeader.sensorModel.empty() )
155  {
156  image.set( "sensorModel", StringNode( imf_, image2DHeader.sensorModel ) );
157  }
158  if ( !image2DHeader.sensorSerialNumber.empty() )
159  {
160  image.set( "sensorSerialNumber", StringNode( imf_, image2DHeader.sensorSerialNumber ) );
161  }
162 
163  if ( !image2DHeader.associatedData3DGuid.empty() )
164  {
165  image.set( "associatedData3DGuid", StringNode( imf_, image2DHeader.associatedData3DGuid ) );
166  }
167 
168  if ( image2DHeader.acquisitionDateTime.dateTimeValue > 0. )
169  {
170  StructureNode acquisitionDateTime = StructureNode( imf_ );
171  image.set( "acquisitionDateTime", acquisitionDateTime );
172  acquisitionDateTime.set( "dateTimeValue", FloatNode( imf_, image2DHeader.acquisitionDateTime.dateTimeValue ) );
173  acquisitionDateTime.set( "isAtomicClockReferenced",
175  }
176 
177  // Create pose structure for image.
178  if ( image2DHeader.pose != RigidBodyTransform{} )
179  {
180  StructureNode pose = StructureNode( imf_ );
181  image.set( "pose", pose );
182 
183  StructureNode rotation = StructureNode( imf_ );
184  pose.set( "rotation", rotation );
185  rotation.set( "w", FloatNode( imf_, image2DHeader.pose.rotation.w ) );
186  rotation.set( "x", FloatNode( imf_, image2DHeader.pose.rotation.x ) );
187  rotation.set( "y", FloatNode( imf_, image2DHeader.pose.rotation.y ) );
188  rotation.set( "z", FloatNode( imf_, image2DHeader.pose.rotation.z ) );
189 
190  StructureNode translation = StructureNode( imf_ );
191  pose.set( "translation", translation );
192  translation.set( "x", FloatNode( imf_, image2DHeader.pose.translation.x ) );
193  translation.set( "y", FloatNode( imf_, image2DHeader.pose.translation.y ) );
194  translation.set( "z", FloatNode( imf_, image2DHeader.pose.translation.z ) );
195  }
196 
197  if ( image2DHeader.visualReferenceRepresentation.jpegImageSize > 0 ||
198  image2DHeader.visualReferenceRepresentation.pngImageSize > 0 )
199  {
200  StructureNode visualReferenceRepresentation = StructureNode( imf_ );
201  image.set( "visualReferenceRepresentation", visualReferenceRepresentation );
202 
203  if ( image2DHeader.visualReferenceRepresentation.jpegImageSize > 0 )
204  {
205  visualReferenceRepresentation.set(
206  "jpegImage", BlobNode( imf_, image2DHeader.visualReferenceRepresentation.jpegImageSize ) );
207  }
208  else if ( image2DHeader.visualReferenceRepresentation.pngImageSize > 0 )
209  {
210  visualReferenceRepresentation.set(
211  "pngImage", BlobNode( imf_, image2DHeader.visualReferenceRepresentation.pngImageSize ) );
212  }
213  if ( image2DHeader.visualReferenceRepresentation.imageMaskSize > 0 )
214  {
215  visualReferenceRepresentation.set(
216  "imageMask", BlobNode( imf_, image2DHeader.visualReferenceRepresentation.imageMaskSize ) );
217  }
218 
219  visualReferenceRepresentation.set(
220  "imageHeight", IntegerNode( imf_, image2DHeader.visualReferenceRepresentation.imageHeight ) );
221  visualReferenceRepresentation.set(
222  "imageWidth", IntegerNode( imf_, image2DHeader.visualReferenceRepresentation.imageWidth ) );
223  }
224  else if ( image2DHeader.pinholeRepresentation.jpegImageSize > 0 ||
225  image2DHeader.pinholeRepresentation.pngImageSize > 0 )
226  {
227  StructureNode pinholeRepresentation = StructureNode( imf_ );
228  image.set( "pinholeRepresentation", pinholeRepresentation );
229 
230  if ( image2DHeader.pinholeRepresentation.jpegImageSize > 0 )
231  {
232  pinholeRepresentation.set( "jpegImage",
233  BlobNode( imf_, image2DHeader.pinholeRepresentation.jpegImageSize ) );
234  }
235  else if ( image2DHeader.pinholeRepresentation.pngImageSize > 0 )
236  {
237  pinholeRepresentation.set( "pngImage", BlobNode( imf_, image2DHeader.pinholeRepresentation.pngImageSize ) );
238  }
239  if ( image2DHeader.pinholeRepresentation.imageMaskSize > 0 )
240  {
241  pinholeRepresentation.set( "imageMask",
242  BlobNode( imf_, image2DHeader.pinholeRepresentation.imageMaskSize ) );
243  }
244 
245  pinholeRepresentation.set( "focalLength", FloatNode( imf_, image2DHeader.pinholeRepresentation.focalLength ) );
246  pinholeRepresentation.set( "imageHeight",
247  IntegerNode( imf_, image2DHeader.pinholeRepresentation.imageHeight ) );
248  pinholeRepresentation.set( "imageWidth", IntegerNode( imf_, image2DHeader.pinholeRepresentation.imageWidth ) );
249  pinholeRepresentation.set( "pixelHeight", FloatNode( imf_, image2DHeader.pinholeRepresentation.pixelHeight ) );
250  pinholeRepresentation.set( "pixelWidth", FloatNode( imf_, image2DHeader.pinholeRepresentation.pixelWidth ) );
251  pinholeRepresentation.set( "principalPointX",
252  FloatNode( imf_, image2DHeader.pinholeRepresentation.principalPointX ) );
253  pinholeRepresentation.set( "principalPointY",
254  FloatNode( imf_, image2DHeader.pinholeRepresentation.principalPointY ) );
255  }
256  else if ( image2DHeader.sphericalRepresentation.jpegImageSize > 0 ||
257  image2DHeader.sphericalRepresentation.pngImageSize > 0 )
258  {
259  StructureNode sphericalRepresentation = StructureNode( imf_ );
260  image.set( "sphericalRepresentation", sphericalRepresentation );
261 
262  if ( image2DHeader.sphericalRepresentation.jpegImageSize > 0 )
263  {
264  sphericalRepresentation.set( "jpegImage",
265  BlobNode( imf_, image2DHeader.sphericalRepresentation.jpegImageSize ) );
266  }
267  else if ( image2DHeader.sphericalRepresentation.pngImageSize > 0 )
268  {
269  sphericalRepresentation.set( "pngImage",
270  BlobNode( imf_, image2DHeader.sphericalRepresentation.pngImageSize ) );
271  }
272  if ( image2DHeader.sphericalRepresentation.imageMaskSize > 0 )
273  {
274  sphericalRepresentation.set( "imageMask",
275  BlobNode( imf_, image2DHeader.sphericalRepresentation.imageMaskSize ) );
276  }
277 
278  sphericalRepresentation.set( "imageHeight",
279  IntegerNode( imf_, image2DHeader.sphericalRepresentation.imageHeight ) );
280  sphericalRepresentation.set( "imageWidth",
281  IntegerNode( imf_, image2DHeader.sphericalRepresentation.imageWidth ) );
282  sphericalRepresentation.set( "pixelHeight",
283  FloatNode( imf_, image2DHeader.sphericalRepresentation.pixelHeight ) );
284  sphericalRepresentation.set( "pixelWidth",
285  FloatNode( imf_, image2DHeader.sphericalRepresentation.pixelWidth ) );
286  }
287  else if ( image2DHeader.cylindricalRepresentation.jpegImageSize > 0 ||
288  image2DHeader.cylindricalRepresentation.pngImageSize > 0 )
289  {
290  StructureNode cylindricalRepresentation = StructureNode( imf_ );
291  image.set( "cylindricalRepresentation", cylindricalRepresentation );
292 
293  if ( image2DHeader.cylindricalRepresentation.jpegImageSize > 0 )
294  {
295  cylindricalRepresentation.set( "jpegImage",
296  BlobNode( imf_, image2DHeader.cylindricalRepresentation.jpegImageSize ) );
297  }
298  else if ( image2DHeader.cylindricalRepresentation.pngImageSize > 0 )
299  {
300  cylindricalRepresentation.set( "pngImage",
301  BlobNode( imf_, image2DHeader.cylindricalRepresentation.pngImageSize ) );
302  }
303  if ( image2DHeader.cylindricalRepresentation.imageMaskSize > 0 )
304  {
305  cylindricalRepresentation.set( "imageMask",
306  BlobNode( imf_, image2DHeader.cylindricalRepresentation.imageMaskSize ) );
307  }
308 
309  cylindricalRepresentation.set( "imageHeight",
310  IntegerNode( imf_, image2DHeader.cylindricalRepresentation.imageHeight ) );
311  cylindricalRepresentation.set( "imageWidth",
312  IntegerNode( imf_, image2DHeader.cylindricalRepresentation.imageWidth ) );
313  cylindricalRepresentation.set( "pixelHeight",
314  FloatNode( imf_, image2DHeader.cylindricalRepresentation.pixelHeight ) );
315  cylindricalRepresentation.set( "pixelWidth",
316  FloatNode( imf_, image2DHeader.cylindricalRepresentation.pixelWidth ) );
317  cylindricalRepresentation.set( "principalPointY",
318  FloatNode( imf_, image2DHeader.cylindricalRepresentation.principalPointY ) );
319  cylindricalRepresentation.set( "radius", FloatNode( imf_, image2DHeader.cylindricalRepresentation.radius ) );
320  }
321  return pos;
322  }
323 
324  // This function reads one of the image blobs
325  int64_t WriterImpl::WriteImage2DNode( StructureNode image, Image2DType imageType, void *pBuffer, int64_t start,
326  int64_t count )
327  {
328  int64_t transferred = 0;
329  switch ( imageType )
330  {
331  case E57_NO_IMAGE:
332  {
333  return 0;
334  }
335  case E57_JPEG_IMAGE:
336  {
337  if ( image.isDefined( "jpegImage" ) )
338  {
339  BlobNode jpegImage( image.get( "jpegImage" ) );
340  jpegImage.write( (uint8_t *)pBuffer, start, (size_t)count );
341  transferred = count;
342  }
343  break;
344  }
345  case E57_PNG_IMAGE:
346  {
347  if ( image.isDefined( "pngImage" ) )
348  {
349  BlobNode pngImage( image.get( "pngImage" ) );
350  pngImage.write( (uint8_t *)pBuffer, start, (size_t)count );
351  transferred = count;
352  }
353  break;
354  }
355  case E57_PNG_IMAGE_MASK:
356  {
357  if ( image.isDefined( "imageMask" ) )
358  {
359  BlobNode imageMask( image.get( "imageMask" ) );
360  imageMask.write( (uint8_t *)pBuffer, start, (size_t)count );
361  transferred = count;
362  }
363  break;
364  }
365  }
366  return transferred;
367  }
368 
369  int64_t WriterImpl::WriteImage2DData( int64_t imageIndex, Image2DType imageType, Image2DProjection imageProjection,
370  void *pBuffer, int64_t start, int64_t count )
371  {
372  if ( ( imageIndex < 0 ) || ( imageIndex >= images2D_.childCount() ) )
373  {
374  return 0;
375  }
376 
377  int64_t transferred = 0;
378  StructureNode image( images2D_.get( imageIndex ) );
379 
380  switch ( imageProjection )
381  {
382  case E57_NO_PROJECTION:
383  return 0;
384  case E57_VISUAL:
385  if ( image.isDefined( "visualReferenceRepresentation" ) )
386  {
387  StructureNode visualReferenceRepresentation( image.get( "visualReferenceRepresentation" ) );
388  transferred = WriteImage2DNode( visualReferenceRepresentation, imageType, pBuffer, start, count );
389  }
390  break;
391  case E57_PINHOLE:
392  if ( image.isDefined( "pinholeRepresentation" ) )
393  {
394  StructureNode pinholeRepresentation( image.get( "pinholeRepresentation" ) );
395  transferred = WriteImage2DNode( pinholeRepresentation, imageType, pBuffer, start, count );
396  }
397  break;
398  case E57_SPHERICAL:
399  if ( image.isDefined( "sphericalRepresentation" ) )
400  {
401  StructureNode sphericalRepresentation( image.get( "sphericalRepresentation" ) );
402  transferred = WriteImage2DNode( sphericalRepresentation, imageType, pBuffer, start, count );
403  }
404  break;
405  case E57_CYLINDRICAL:
406  if ( image.isDefined( "cylindricalRepresentation" ) )
407  {
408  StructureNode cylindricalRepresentation( image.get( "cylindricalRepresentation" ) );
409  transferred = WriteImage2DNode( cylindricalRepresentation, imageType, pBuffer, start, count );
410  }
411  break;
412  }
413  return transferred;
414  }
415 
416  int64_t WriterImpl::NewData3D( Data3D &data3DHeader )
417  {
418  int64_t pos = -1;
419 
420  if ( data3DHeader.guid.empty() )
421  {
422  data3DHeader.guid = generateRandomGUID();
423  }
424 
425  StructureNode scan = StructureNode( imf_ );
426  data3D_.append( scan );
427  pos = data3D_.childCount() - 1;
428 
429  scan.set( "guid", StringNode( imf_, data3DHeader.guid ) ); // required
430 
431  if ( !data3DHeader.name.empty() )
432  {
433  scan.set( "name", StringNode( imf_, data3DHeader.name ) );
434  }
435 
436  if ( !data3DHeader.description.empty() )
437  {
438  scan.set( "description", StringNode( imf_, data3DHeader.description ) );
439  }
440 
441  if ( data3DHeader.originalGuids.size() > 0 )
442  {
443  scan.set( "originalGuids", VectorNode( imf_ ) );
444  VectorNode originalGuids( scan.get( "originalGuids" ) );
445  int i;
446  for ( i = 0; i < (int)data3DHeader.originalGuids.size(); i++ )
447  {
448  originalGuids.append( StringNode( imf_, data3DHeader.originalGuids[i] ) );
449  }
450  }
451 
452  // Add various sensor and version strings to scan.
453  // Path names: "/data3D/0/sensorVendor", etc...
454  if ( !data3DHeader.sensorVendor.empty() )
455  {
456  scan.set( "sensorVendor", StringNode( imf_, data3DHeader.sensorVendor ) );
457  }
458  if ( !data3DHeader.sensorModel.empty() )
459  {
460  scan.set( "sensorModel", StringNode( imf_, data3DHeader.sensorModel ) );
461  }
462  if ( !data3DHeader.sensorSerialNumber.empty() )
463  {
464  scan.set( "sensorSerialNumber", StringNode( imf_, data3DHeader.sensorSerialNumber ) );
465  }
466  if ( !data3DHeader.sensorHardwareVersion.empty() )
467  {
468  scan.set( "sensorHardwareVersion", StringNode( imf_, data3DHeader.sensorHardwareVersion ) );
469  }
470  if ( !data3DHeader.sensorSoftwareVersion.empty() )
471  {
472  scan.set( "sensorSoftwareVersion", StringNode( imf_, data3DHeader.sensorSoftwareVersion ) );
473  }
474  if ( !data3DHeader.sensorFirmwareVersion.empty() )
475  {
476  scan.set( "sensorFirmwareVersion", StringNode( imf_, data3DHeader.sensorFirmwareVersion ) );
477  }
478 
479  // Add temp/humidity to scan.
480  // Path names: "/data3D/0/temperature", etc...
481  if ( data3DHeader.temperature != E57_FLOAT_MAX )
482  {
483  scan.set( "temperature", FloatNode( imf_, data3DHeader.temperature ) );
484  }
485 
486  if ( data3DHeader.relativeHumidity != E57_FLOAT_MAX )
487  {
488  scan.set( "relativeHumidity", FloatNode( imf_, data3DHeader.relativeHumidity ) );
489  }
490 
491  if ( data3DHeader.atmosphericPressure != E57_FLOAT_MAX )
492  {
493  scan.set( "atmosphericPressure", FloatNode( imf_, data3DHeader.atmosphericPressure ) );
494  }
495 
496  if ( data3DHeader.indexBounds != IndexBounds{} )
497  {
498  StructureNode ibox = StructureNode( imf_ );
499 
500  if ( ( data3DHeader.indexBounds.rowMinimum != 0 ) || ( data3DHeader.indexBounds.rowMaximum != 0 ) )
501  {
502  ibox.set( "rowMinimum", IntegerNode( imf_, data3DHeader.indexBounds.rowMinimum ) );
503  ibox.set( "rowMaximum", IntegerNode( imf_, data3DHeader.indexBounds.rowMaximum ) );
504  }
505  if ( ( data3DHeader.indexBounds.columnMinimum != 0 ) || ( data3DHeader.indexBounds.columnMaximum != 0 ) )
506  {
507  ibox.set( "columnMinimum", IntegerNode( imf_, data3DHeader.indexBounds.columnMinimum ) );
508  ibox.set( "columnMaximum", IntegerNode( imf_, data3DHeader.indexBounds.columnMaximum ) );
509  }
510  if ( ( data3DHeader.indexBounds.returnMinimum != 0 ) || ( data3DHeader.indexBounds.returnMaximum != 0 ) )
511  {
512  ibox.set( "returnMinimum", IntegerNode( imf_, data3DHeader.indexBounds.returnMinimum ) );
513  ibox.set( "returnMaximum", IntegerNode( imf_, data3DHeader.indexBounds.returnMaximum ) );
514  }
515  scan.set( "indexBounds", ibox );
516  }
517 
518  if ( ( data3DHeader.intensityLimits.intensityMaximum != 0. ) ||
519  ( data3DHeader.intensityLimits.intensityMinimum != 0. ) )
520  {
521  StructureNode intbox = StructureNode( imf_ );
522  if ( data3DHeader.pointFields.intensityScaledInteger > 0. )
523  {
524  double offset = 0.;
525  double scale = data3DHeader.pointFields.intensityScaledInteger;
526 
527  int64_t rawIntegerMinimum =
528  (int64_t)floor( ( data3DHeader.intensityLimits.intensityMinimum - offset ) / scale + .5 );
529  int64_t rawIntegerMaximum =
530  (int64_t)floor( ( data3DHeader.intensityLimits.intensityMaximum - offset ) / scale + .5 );
531 
532  intbox.set( "intensityMaximum", ScaledIntegerNode( imf_, rawIntegerMaximum, rawIntegerMinimum,
533  rawIntegerMaximum, scale, offset ) );
534 
535  intbox.set( "intensityMinimum", ScaledIntegerNode( imf_, rawIntegerMinimum, rawIntegerMinimum,
536  rawIntegerMaximum, scale, offset ) );
537  }
538  else if ( data3DHeader.pointFields.intensityScaledInteger == 0. )
539  {
540  intbox.set( "intensityMaximum", FloatNode( imf_, data3DHeader.intensityLimits.intensityMaximum ) );
541  intbox.set( "intensityMinimum", FloatNode( imf_, data3DHeader.intensityLimits.intensityMinimum ) );
542  }
543  else
544  {
545  intbox.set( "intensityMaximum",
546  IntegerNode( imf_, (int64_t)data3DHeader.intensityLimits.intensityMaximum ) );
547  intbox.set( "intensityMinimum",
548  IntegerNode( imf_, (int64_t)data3DHeader.intensityLimits.intensityMinimum ) );
549  }
550  scan.set( "intensityLimits", intbox );
551  }
552 
553  if ( ( data3DHeader.colorLimits.colorRedMaximum != 0. ) || ( data3DHeader.colorLimits.colorRedMinimum != 0. ) )
554  {
555  StructureNode colorbox = StructureNode( imf_ );
556  colorbox.set( "colorRedMaximum", IntegerNode( imf_, (int64_t)data3DHeader.colorLimits.colorRedMaximum ) );
557  colorbox.set( "colorRedMinimum", IntegerNode( imf_, (int64_t)data3DHeader.colorLimits.colorRedMinimum ) );
558  colorbox.set( "colorGreenMaximum", IntegerNode( imf_, (int64_t)data3DHeader.colorLimits.colorGreenMaximum ) );
559  colorbox.set( "colorGreenMinimum", IntegerNode( imf_, (int64_t)data3DHeader.colorLimits.colorGreenMinimum ) );
560  colorbox.set( "colorBlueMaximum", IntegerNode( imf_, (int64_t)data3DHeader.colorLimits.colorBlueMaximum ) );
561  colorbox.set( "colorBlueMinimum", IntegerNode( imf_, (int64_t)data3DHeader.colorLimits.colorBlueMinimum ) );
562  scan.set( "colorLimits", colorbox );
563  }
564 
565  // Add Cartesian bounding box to scan.
566  // Path names: "/data3D/0/cartesianBounds/xMinimum", etc...
567  if ( ( data3DHeader.cartesianBounds.xMinimum != -E57_DOUBLE_MAX ) ||
568  ( data3DHeader.cartesianBounds.xMaximum != E57_DOUBLE_MAX ) )
569  {
570  StructureNode bbox = StructureNode( imf_ );
571  bbox.set( "xMinimum", FloatNode( imf_, data3DHeader.cartesianBounds.xMinimum ) );
572  bbox.set( "xMaximum", FloatNode( imf_, data3DHeader.cartesianBounds.xMaximum ) );
573  bbox.set( "yMinimum", FloatNode( imf_, data3DHeader.cartesianBounds.yMinimum ) );
574  bbox.set( "yMaximum", FloatNode( imf_, data3DHeader.cartesianBounds.yMaximum ) );
575  bbox.set( "zMinimum", FloatNode( imf_, data3DHeader.cartesianBounds.zMinimum ) );
576  bbox.set( "zMaximum", FloatNode( imf_, data3DHeader.cartesianBounds.zMaximum ) );
577  scan.set( "cartesianBounds", bbox );
578  }
579 
580  if ( ( data3DHeader.sphericalBounds.rangeMinimum != 0. ) ||
581  ( data3DHeader.sphericalBounds.rangeMaximum != E57_DOUBLE_MAX ) )
582  {
583  StructureNode sbox = StructureNode( imf_ );
584  sbox.set( "rangeMinimum", FloatNode( imf_, data3DHeader.sphericalBounds.rangeMinimum ) );
585  sbox.set( "rangeMaximum", FloatNode( imf_, data3DHeader.sphericalBounds.rangeMaximum ) );
586  sbox.set( "elevationMinimum", FloatNode( imf_, data3DHeader.sphericalBounds.elevationMinimum ) );
587  sbox.set( "elevationMaximum", FloatNode( imf_, data3DHeader.sphericalBounds.elevationMaximum ) );
588  sbox.set( "azimuthStart", FloatNode( imf_, data3DHeader.sphericalBounds.azimuthStart ) );
589  sbox.set( "azimuthEnd", FloatNode( imf_, data3DHeader.sphericalBounds.azimuthEnd ) );
590  scan.set( "sphericalBounds", sbox );
591  }
592 
593  // Create pose structure for scan.
594  // Path names: "/data3D/0/pose/rotation/w", etc...
595  // "/data3D/0/pose/translation/x", etc...
596  if ( data3DHeader.pose != RigidBodyTransform{} )
597  {
598  StructureNode pose = StructureNode( imf_ );
599  scan.set( "pose", pose );
600 
601  StructureNode rotation = StructureNode( imf_ );
602  rotation.set( "w", FloatNode( imf_, data3DHeader.pose.rotation.w ) );
603  rotation.set( "x", FloatNode( imf_, data3DHeader.pose.rotation.x ) );
604  rotation.set( "y", FloatNode( imf_, data3DHeader.pose.rotation.y ) );
605  rotation.set( "z", FloatNode( imf_, data3DHeader.pose.rotation.z ) );
606  pose.set( "rotation", rotation );
607 
608  StructureNode translation = StructureNode( imf_ );
609  translation.set( "x", FloatNode( imf_, data3DHeader.pose.translation.x ) );
610  translation.set( "y", FloatNode( imf_, data3DHeader.pose.translation.y ) );
611  translation.set( "z", FloatNode( imf_, data3DHeader.pose.translation.z ) );
612  pose.set( "translation", translation );
613  }
614 
615  // Add start/stop acquisition times to scan.
616  // Path names: "/data3D/0/acquisitionStart/dateTimeValue",
617  // "/data3D/0/acquisitionEnd/dateTimeValue"
618  if ( data3DHeader.acquisitionStart.dateTimeValue > 0. )
619  {
620  StructureNode acquisitionStart = StructureNode( imf_ );
621  scan.set( "acquisitionStart", acquisitionStart );
622  acquisitionStart.set( "dateTimeValue", FloatNode( imf_, data3DHeader.acquisitionStart.dateTimeValue ) );
623  acquisitionStart.set( "isAtomicClockReferenced",
624  IntegerNode( imf_, data3DHeader.acquisitionStart.isAtomicClockReferenced ) );
625  }
626  if ( data3DHeader.acquisitionEnd.dateTimeValue > 0. )
627  {
628  StructureNode acquisitionEnd = StructureNode( imf_ );
629  scan.set( "acquisitionEnd", acquisitionEnd );
630  acquisitionEnd.set( "dateTimeValue", FloatNode( imf_, data3DHeader.acquisitionEnd.dateTimeValue ) );
631  acquisitionEnd.set( "isAtomicClockReferenced",
632  IntegerNode( imf_, data3DHeader.acquisitionEnd.isAtomicClockReferenced ) );
633  }
634 
635  // Add grouping scheme area
636  // Path name: "/data3D/0/pointGroupingSchemes"
637  if ( !data3DHeader.pointGroupingSchemes.groupingByLine.idElementName.empty() )
638  {
639  StructureNode pointGroupingSchemes = StructureNode( imf_ );
640  scan.set( "pointGroupingSchemes", pointGroupingSchemes );
641 
642  // Add a line grouping scheme
643  // Path name: "/data3D/0/pointGroupingSchemes/groupingByLine"
644  StructureNode groupingByLine = StructureNode( imf_ );
645  pointGroupingSchemes.set( "groupingByLine", groupingByLine );
646 
647  // data3DHeader.pointGroupingSchemes.groupingByLine.idElementName));
648  bool byColumn = true; // default should be "columnIndex"
649  if ( data3DHeader.pointGroupingSchemes.groupingByLine.idElementName.compare( "rowIndex" ) == 0 )
650  {
651  byColumn = false;
652  }
653 
654  // Add idElementName to groupingByLine, specify a line is column or row oriented
655  // Path name: "/data3D/0/pointGroupingSchemes/groupingByLine/idElementName"
656  if ( byColumn )
657  {
658  groupingByLine.set( "idElementName", StringNode( imf_, "columnIndex" ) );
659  }
660  else
661  {
662  groupingByLine.set( "idElementName", StringNode( imf_, "rowIndex" ) );
663  }
664 
665  // Make a prototype of datatypes that will be stored in LineGroupRecord.
666  // This prototype will be used in creating the groups CompressedVector.
667  // Will define path names like:
668  // "/data3D/0/pointGroupingSchemes/groupingByLine/groups/0/idElementValue"
669  int64_t groupsSize = data3DHeader.pointGroupingSchemes.groupingByLine.groupsSize;
670  int64_t countSize = data3DHeader.pointGroupingSchemes.groupingByLine.pointCountSize;
671  int64_t pointsSize = data3DHeader.pointsSize;
672 
673  StructureNode lineGroupProto = StructureNode( imf_ );
674  lineGroupProto.set( "startPointIndex", IntegerNode( imf_, 0, 0, pointsSize - 1 ) );
675  lineGroupProto.set( "idElementValue", IntegerNode( imf_, 0, 0, groupsSize - 1 ) );
676  lineGroupProto.set( "pointCount", IntegerNode( imf_, 0, 0, countSize ) );
677 
678  // Not supported in this Simple API for now
679  /*
680  StructureNode bbox = StructureNode(imf_);
681  bbox.set("xMinimum", FloatNode(imf_, 0., E57_SINGLE,
682  data3DHeader.pointFields.pointRangeMinimum, data3DHeader.pointFields.pointRangeMaximum));
683  bbox.set("xMaximum", FloatNode(imf_, 0., E57_SINGLE,
684  data3DHeader.pointFields.pointRangeMinimum, data3DHeader.pointFields.pointRangeMaximum));
685  bbox.set("yMinimum", FloatNode(imf_, 0., E57_SINGLE,
686  data3DHeader.pointFields.pointRangeMinimum, data3DHeader.pointFields.pointRangeMaximum));
687  bbox.set("yMaximum", FloatNode(imf_, 0., E57_SINGLE,
688  data3DHeader.pointFields.pointRangeMinimum, data3DHeader.pointFields.pointRangeMaximum));
689  bbox.set("zMinimum", FloatNode(imf_, 0., E57_SINGLE,
690  data3DHeader.pointFields.pointRangeMinimum, data3DHeader.pointFields.pointRangeMaximum));
691  bbox.set("zMaximum", FloatNode(imf_, 0., E57_SINGLE,
692  data3DHeader.pointFields.pointRangeMinimum, data3DHeader.pointFields.pointRangeMaximum));
693  lineGroupProto.set("cartesianBounds", bbox);
694 
695  StructureNode sbox = StructureNode(imf_);
696  sbox.set("rangeMinimum", FloatNode(imf_, 0., E57_SINGLE,
697  data3DHeader.pointFields.pointRangeMinimum, data3DHeader.pointFields.pointRangeMaximum));
698  sbox.set("rangeMaximum", FloatNode(imf_, 0., E57_SINGLE,
699  data3DHeader.pointFields.pointRangeMinimum, data3DHeader.pointFields.pointRangeMaximum));
700  sbox.set("elevationMinimum", FloatNode(imf_, 0., E57_SINGLE,
701  data3DHeader.pointFields.angleMinimum, data3DHeader.pointFields.angleMaximum));
702  sbox.set("elevationMaximum", FloatNode(imf_, 0., E57_SINGLE,
703  data3DHeader.pointFields.angleMinimum, data3DHeader.pointFields.angleMaximum));
704  sbox.set("azimuthStart", FloatNode(imf_, 0., E57_SINGLE,
705  data3DHeader.pointFields.angleMinimum, data3DHeader.pointFields.angleMaximum));
706  sbox.set("azimuthEnd", FloatNode(imf_, 0., E57_SINGLE,
707  data3DHeader.pointFields.angleMinimum, data3DHeader.pointFields.angleMaximum));
708  lineGroupProto.set("sphericalBounds", sbox);
709  */
710  // Make empty codecs vector for use in creating groups CompressedVector.
711  // If this vector is empty, it is assumed that all fields will use the BitPack codec.
712  VectorNode lineGroupCodecs = VectorNode( imf_, true );
713 
714  // Create CompressedVector for storing groups.
715  // Path Name: "/data3D/0/pointGroupingSchemes/groupingByLine/groups".
716  // We use the prototype and empty codecs tree from above.
717  // The CompressedVector will be filled by code below.
718  CompressedVectorNode groups = CompressedVectorNode( imf_, lineGroupProto, lineGroupCodecs );
719  groupingByLine.set( "groups", groups );
720  }
721 
722  // Make a prototype of datatypes that will be stored in points record.
723  // This prototype will be used in creating the points CompressedVector.
724  // Using this proto in a CompressedVector will define path names like:
725  // "/data3D/0/points/0/cartesianX"
726  StructureNode proto = StructureNode( imf_ );
727 
728  // Because ScaledInteger min/max are the raw integer min/max, we must calculate them from the data min/max
729  const double pointRangeScale = data3DHeader.pointFields.pointRangeScaledInteger;
730  const double pointRangeOffset = 0.;
731  int64_t pointRangeMinimum =
732  (int64_t)floor( ( data3DHeader.pointFields.pointRangeMinimum - pointRangeOffset ) / pointRangeScale + .5 );
733  int64_t pointRangeMaximum =
734  (int64_t)floor( ( data3DHeader.pointFields.pointRangeMaximum - pointRangeOffset ) / pointRangeScale + .5 );
735 
736  const auto getPointProto = [=]() -> Node {
737  if ( pointRangeScale > E57_NOT_SCALED_USE_FLOAT )
738  {
739  return ScaledIntegerNode( imf_, 0, pointRangeMinimum, pointRangeMaximum, pointRangeScale,
740  pointRangeOffset );
741  }
742  else
743  {
744  return FloatNode( imf_, 0., ( pointRangeScale < E57_NOT_SCALED_USE_FLOAT ) ? E57_DOUBLE : E57_SINGLE,
745  data3DHeader.pointFields.pointRangeMinimum, data3DHeader.pointFields.pointRangeMaximum );
746  }
747  };
748 
749  if ( data3DHeader.pointFields.cartesianXField )
750  {
751  proto.set( "cartesianX", getPointProto() );
752  }
753  if ( data3DHeader.pointFields.cartesianYField )
754  {
755  proto.set( "cartesianY", getPointProto() );
756  }
757 
758  if ( data3DHeader.pointFields.cartesianZField )
759  {
760  proto.set( "cartesianZ", getPointProto() );
761  }
762 
763  if ( data3DHeader.pointFields.sphericalRangeField )
764  {
765  proto.set( "sphericalRange", getPointProto() );
766  }
767 
768  const double angleScale = data3DHeader.pointFields.angleScaledInteger;
769  const double angleOffset = 0.;
770  int64_t angleMinimum =
771  (int64_t)std::floor( ( data3DHeader.pointFields.angleMinimum - angleOffset ) / angleScale + .5 );
772  int64_t angleMaximum =
773  (int64_t)std::floor( ( data3DHeader.pointFields.angleMaximum - angleOffset ) / angleScale + .5 );
774 
775  const auto getAngleProto = [=]() -> Node {
776  if ( angleScale > E57_NOT_SCALED_USE_FLOAT )
777  {
778  return ScaledIntegerNode( imf_, 0, angleMinimum, angleMaximum, angleScale, angleOffset );
779  }
780  else
781  {
782  return FloatNode( imf_, 0., ( angleScale < E57_NOT_SCALED_USE_FLOAT ) ? E57_DOUBLE : E57_SINGLE,
783  data3DHeader.pointFields.angleMinimum, data3DHeader.pointFields.angleMaximum );
784  }
785  };
786 
787  if ( data3DHeader.pointFields.sphericalAzimuthField )
788  {
789  proto.set( "sphericalAzimuth", getAngleProto() );
790  }
791 
792  if ( data3DHeader.pointFields.sphericalElevationField )
793  {
794  proto.set( "sphericalElevation", getAngleProto() );
795  }
796 
797  if ( data3DHeader.pointFields.intensityField )
798  {
799  if ( data3DHeader.pointFields.intensityScaledInteger > 0. )
800  {
801  double offset = 0; // could be data3DHeader.intensityLimits.intensityMinimum;
802  double scale = data3DHeader.pointFields.intensityScaledInteger;
803  int64_t rawIntegerMinimum =
804  (int64_t)floor( ( data3DHeader.intensityLimits.intensityMinimum - offset ) / scale + .5 );
805  int64_t rawIntegerMaximum =
806  (int64_t)floor( ( data3DHeader.intensityLimits.intensityMaximum - offset ) / scale + .5 );
807  proto.set( "intensity", ScaledIntegerNode( imf_, 0, rawIntegerMinimum, rawIntegerMaximum, scale, offset ) );
808  }
809  else if ( data3DHeader.pointFields.intensityScaledInteger == E57_NOT_SCALED_USE_FLOAT )
810  {
811  proto.set( "intensity", FloatNode( imf_, 0., E57_SINGLE, data3DHeader.intensityLimits.intensityMinimum,
812  data3DHeader.intensityLimits.intensityMaximum ) );
813  }
814  else
815  {
816  proto.set( "intensity", IntegerNode( imf_, 0, (int64_t)data3DHeader.intensityLimits.intensityMinimum,
817  (int64_t)data3DHeader.intensityLimits.intensityMaximum ) );
818  }
819  }
820 
821  if ( data3DHeader.pointFields.colorRedField )
822  {
823  proto.set( "colorRed", IntegerNode( imf_, 0, (int64_t)data3DHeader.colorLimits.colorRedMinimum,
824  (int64_t)data3DHeader.colorLimits.colorRedMaximum ) );
825  }
826  if ( data3DHeader.pointFields.colorGreenField )
827  {
828  proto.set( "colorGreen", IntegerNode( imf_, 0, (int64_t)data3DHeader.colorLimits.colorGreenMinimum,
829  (int64_t)data3DHeader.colorLimits.colorGreenMaximum ) );
830  }
831  if ( data3DHeader.pointFields.colorBlueField )
832  {
833  proto.set( "colorBlue", IntegerNode( imf_, 0, (int64_t)data3DHeader.colorLimits.colorBlueMinimum,
834  (int64_t)data3DHeader.colorLimits.colorBlueMaximum ) );
835  }
836 
837  if ( data3DHeader.pointFields.returnIndexField )
838  {
839  proto.set( "returnIndex", IntegerNode( imf_, 0, E57_UINT8_MIN, data3DHeader.pointFields.returnMaximum ) );
840  }
841  if ( data3DHeader.pointFields.returnCountField )
842  {
843  proto.set( "returnCount", IntegerNode( imf_, 0, E57_UINT8_MIN, data3DHeader.pointFields.returnMaximum ) );
844  }
845 
846  if ( data3DHeader.pointFields.rowIndexField )
847  {
848  proto.set( "rowIndex", IntegerNode( imf_, 0, E57_UINT32_MIN, data3DHeader.pointFields.rowIndexMaximum ) );
849  }
850  if ( data3DHeader.pointFields.columnIndexField )
851  {
852  proto.set( "columnIndex",
853  IntegerNode( imf_, 0, E57_UINT32_MIN, data3DHeader.pointFields.columnIndexMaximum ) );
854  }
855 
856  if ( data3DHeader.pointFields.timeStampField )
857  {
858  if ( data3DHeader.pointFields.timeScaledInteger > 0. )
859  {
860  double offset = 0;
861  double scale = data3DHeader.pointFields.timeScaledInteger;
862  int64_t rawIntegerMinimum =
863  (int64_t)floor( ( data3DHeader.pointFields.timeMinimum - offset ) / scale + .5 );
864  int64_t rawIntegerMaximum =
865  (int64_t)floor( ( data3DHeader.pointFields.timeMaximum - offset ) / scale + .5 );
866  proto.set( "timeStamp", ScaledIntegerNode( imf_, 0, rawIntegerMinimum, rawIntegerMaximum, scale, offset ) );
867  }
868  else if ( data3DHeader.pointFields.timeScaledInteger == E57_NOT_SCALED_USE_FLOAT )
869  {
870  if ( data3DHeader.pointFields.timeMaximum == E57_FLOAT_MAX )
871  {
872  proto.set( "timeStamp", FloatNode( imf_, 0., E57_SINGLE, E57_FLOAT_MIN, E57_FLOAT_MAX ) );
873  }
874  else if ( data3DHeader.pointFields.timeMaximum == E57_DOUBLE_MAX )
875  {
876  proto.set( "timeStamp", FloatNode( imf_, 0., E57_DOUBLE, E57_DOUBLE_MIN, E57_DOUBLE_MAX ) );
877  }
878  }
879  else
880  {
881  proto.set( "timeStamp", IntegerNode( imf_, 0, (int64_t)data3DHeader.pointFields.timeMinimum,
882  (int64_t)data3DHeader.pointFields.timeMaximum ) );
883  }
884  }
885 
886  if ( data3DHeader.pointFields.cartesianInvalidStateField )
887  {
888  proto.set( "cartesianInvalidState", IntegerNode( imf_, 0, 0, 2 ) );
889  }
890  if ( data3DHeader.pointFields.sphericalInvalidStateField )
891  {
892  proto.set( "sphericalInvalidState", IntegerNode( imf_, 0, 0, 2 ) );
893  }
894  if ( data3DHeader.pointFields.isIntensityInvalidField )
895  {
896  proto.set( "isIntensityInvalid", IntegerNode( imf_, 0, 0, 1 ) );
897  }
898  if ( data3DHeader.pointFields.isColorInvalidField )
899  {
900  proto.set( "isColorInvalid", IntegerNode( imf_, 0, 0, 1 ) );
901  }
902  if ( data3DHeader.pointFields.isTimeStampInvalidField )
903  {
904  proto.set( "isTimeStampInvalid", IntegerNode( imf_, 0, 0, 1 ) );
905  }
906 
907  // E57_EXT_surface_normals
908  if ( data3DHeader.pointFields.normalX || data3DHeader.pointFields.normalY || data3DHeader.pointFields.normalZ )
909  {
910  // make sure we declare the extesion before using the fields with prefix
911  ustring norExtUri;
912  if ( !imf_.extensionsLookupPrefix( "nor", norExtUri ) )
913  {
914  imf_.extensionsAdd( "nor", "http://www.libe57.org/E57_NOR_surface_normals.txt" );
915  }
916  }
917 
918  // currently we support writing normals only as float32
919  if ( data3DHeader.pointFields.normalX )
920  {
921  proto.set( "nor:normalX", FloatNode( imf_, 0., E57_SINGLE, -1., 1. ) );
922  }
923  if ( data3DHeader.pointFields.normalY )
924  {
925  proto.set( "nor:normalY", FloatNode( imf_, 0., E57_SINGLE, -1., 1. ) );
926  }
927  if ( data3DHeader.pointFields.normalZ )
928  {
929  proto.set( "nor:normalZ", FloatNode( imf_, 0., E57_SINGLE, -1., 1. ) );
930  }
931 
932  // Make empty codecs vector for use in creating points CompressedVector.
933  // If this vector is empty, it is assumed that all fields will use the BitPack codec.
934  VectorNode codecs = VectorNode( imf_, true );
935 
936  // Create CompressedVector for storing points. Path Name: "/data3D/0/points".
937  // We use the prototype and empty codecs tree from above.
938  // The CompressedVector will be filled by code below.
939  CompressedVectorNode points = CompressedVectorNode( imf_, proto, codecs );
940  scan.set( "points", points );
941  return pos;
942  }
943 
944  template <typename COORDTYPE>
945  CompressedVectorWriter WriterImpl::SetUpData3DPointsData( int64_t dataIndex, size_t count,
946  const Data3DPointsData_t<COORDTYPE> &buffers )
947  {
948  StructureNode scan( data3D_.get( dataIndex ) );
949  CompressedVectorNode points( scan.get( "points" ) );
950  StructureNode proto( points.prototype() );
951 
952  std::vector<SourceDestBuffer> sourceBuffers;
953  if ( proto.isDefined( "cartesianX" ) && ( buffers.cartesianX != nullptr ) )
954  {
955  sourceBuffers.emplace_back( imf_, "cartesianX", buffers.cartesianX, count, true, true );
956  }
957  if ( proto.isDefined( "cartesianY" ) && ( buffers.cartesianY != nullptr ) )
958  {
959  sourceBuffers.emplace_back( imf_, "cartesianY", buffers.cartesianY, count, true, true );
960  }
961  if ( proto.isDefined( "cartesianZ" ) && ( buffers.cartesianZ != nullptr ) )
962  {
963  sourceBuffers.emplace_back( imf_, "cartesianZ", buffers.cartesianZ, count, true, true );
964  }
965 
966  if ( proto.isDefined( "sphericalRange" ) && ( buffers.sphericalRange != nullptr ) )
967  {
968  sourceBuffers.emplace_back( imf_, "sphericalRange", buffers.sphericalRange, count, true, true );
969  }
970  if ( proto.isDefined( "sphericalAzimuth" ) && ( buffers.sphericalAzimuth != nullptr ) )
971  {
972  sourceBuffers.emplace_back( imf_, "sphericalAzimuth", buffers.sphericalAzimuth, count, true, true );
973  }
974  if ( proto.isDefined( "sphericalElevation" ) && ( buffers.sphericalElevation != nullptr ) )
975  {
976  sourceBuffers.emplace_back( imf_, "sphericalElevation", buffers.sphericalElevation, count, true, true );
977  }
978 
979  if ( proto.isDefined( "intensity" ) && ( buffers.intensity != nullptr ) )
980  {
981  sourceBuffers.emplace_back( imf_, "intensity", buffers.intensity, count, true, true );
982  }
983 
984  if ( proto.isDefined( "colorRed" ) && ( buffers.colorRed != nullptr ) )
985  {
986  sourceBuffers.emplace_back( imf_, "colorRed", buffers.colorRed, count, true );
987  }
988  if ( proto.isDefined( "colorGreen" ) && ( buffers.colorGreen != nullptr ) )
989  {
990  sourceBuffers.emplace_back( imf_, "colorGreen", buffers.colorGreen, count, true );
991  }
992  if ( proto.isDefined( "colorBlue" ) && ( buffers.colorBlue != nullptr ) )
993  {
994  sourceBuffers.emplace_back( imf_, "colorBlue", buffers.colorBlue, count, true );
995  }
996 
997  if ( proto.isDefined( "returnIndex" ) && ( buffers.returnIndex != nullptr ) )
998  {
999  sourceBuffers.emplace_back( imf_, "returnIndex", buffers.returnIndex, count, true );
1000  }
1001  if ( proto.isDefined( "returnCount" ) && ( buffers.returnCount != nullptr ) )
1002  {
1003  sourceBuffers.emplace_back( imf_, "returnCount", buffers.returnCount, count, true );
1004  }
1005 
1006  if ( proto.isDefined( "rowIndex" ) && ( buffers.rowIndex != nullptr ) )
1007  {
1008  sourceBuffers.emplace_back( imf_, "rowIndex", buffers.rowIndex, count, true );
1009  }
1010  if ( proto.isDefined( "columnIndex" ) && ( buffers.columnIndex != nullptr ) )
1011  {
1012  sourceBuffers.emplace_back( imf_, "columnIndex", buffers.columnIndex, count, true );
1013  }
1014 
1015  if ( proto.isDefined( "timeStamp" ) && ( buffers.timeStamp != nullptr ) )
1016  {
1017  sourceBuffers.emplace_back( imf_, "timeStamp", buffers.timeStamp, count, true, true );
1018  }
1019 
1020  if ( proto.isDefined( "cartesianInvalidState" ) && ( buffers.cartesianInvalidState != nullptr ) )
1021  {
1022  sourceBuffers.emplace_back( imf_, "cartesianInvalidState", buffers.cartesianInvalidState, count, true );
1023  }
1024  if ( proto.isDefined( "sphericalInvalidState" ) && ( buffers.sphericalInvalidState != nullptr ) )
1025  {
1026  sourceBuffers.emplace_back( imf_, "sphericalInvalidState", buffers.sphericalInvalidState, count, true );
1027  }
1028  if ( proto.isDefined( "isIntensityInvalid" ) && ( buffers.isIntensityInvalid != nullptr ) )
1029  {
1030  sourceBuffers.emplace_back( imf_, "isIntensityInvalid", buffers.isIntensityInvalid, count, true );
1031  }
1032  if ( proto.isDefined( "isColorInvalid" ) && ( buffers.isColorInvalid != nullptr ) )
1033  {
1034  sourceBuffers.emplace_back( imf_, "isColorInvalid", buffers.isColorInvalid, count, true );
1035  }
1036  if ( proto.isDefined( "isTimeStampInvalid" ) && ( buffers.isTimeStampInvalid != nullptr ) )
1037  {
1038  sourceBuffers.emplace_back( imf_, "isTimeStampInvalid", buffers.isTimeStampInvalid, count, true );
1039  }
1040 
1041  // E57_EXT_surface_normals
1042  ustring norExtUri;
1043  if ( imf_.extensionsLookupPrefix( "nor", norExtUri ) )
1044  {
1045  if ( proto.isDefined( "nor:normalX" ) && ( buffers.normalX != nullptr ) )
1046  {
1047  sourceBuffers.emplace_back( imf_, "nor:normalX", buffers.normalX, count, true, true );
1048  }
1049  if ( proto.isDefined( "nor:normalY" ) && ( buffers.normalY != nullptr ) )
1050  {
1051  sourceBuffers.emplace_back( imf_, "nor:normalY", buffers.normalY, count, true, true );
1052  }
1053  if ( proto.isDefined( "nor:normalZ" ) && ( buffers.normalZ != nullptr ) )
1054  {
1055  sourceBuffers.emplace_back( imf_, "nor:normalZ", buffers.normalZ, count, true, true );
1056  }
1057  }
1058 
1059  // create the writer, all buffers must be setup before this call
1060  CompressedVectorWriter writer = points.writer( sourceBuffers );
1061 
1062  return writer;
1063  }
1064 
1065  // Explicit template instantiation
1066  template CompressedVectorWriter WriterImpl::SetUpData3DPointsData( int64_t dataIndex, size_t pointCount,
1067  const Data3DPointsData_t<float> &buffers );
1068 
1069  template CompressedVectorWriter WriterImpl::SetUpData3DPointsData( int64_t dataIndex, size_t pointCount,
1070  const Data3DPointsData_t<double> &buffers );
1071 
1072  // This funtion writes out the group data
1073  bool WriterImpl::WriteData3DGroupsData( int64_t dataIndex, int64_t groupCount, int64_t *idElementValue,
1074  int64_t *startPointIndex, int64_t *pointCount )
1075  {
1076  if ( ( dataIndex < 0 ) || ( dataIndex >= data3D_.childCount() ) )
1077  {
1078  return false;
1079  }
1080 
1081  StructureNode scan( data3D_.get( dataIndex ) );
1082 
1083  if ( !scan.isDefined( "pointGroupingSchemes" ) )
1084  {
1085  return false;
1086  }
1087  StructureNode pointGroupingSchemes( scan.get( "pointGroupingSchemes" ) );
1088 
1089  if ( !pointGroupingSchemes.isDefined( "groupingByLine" ) )
1090  {
1091  return false;
1092  }
1093  StructureNode groupingByLine( pointGroupingSchemes.get( "groupingByLine" ) );
1094 
1095  if ( !groupingByLine.isDefined( "groups" ) )
1096  {
1097  return false;
1098  }
1099  CompressedVectorNode groups( groupingByLine.get( "groups" ) );
1100 
1101  std::vector<SourceDestBuffer> groupSDBuffers;
1102  groupSDBuffers.emplace_back( imf_, "idElementValue", idElementValue, groupCount, true );
1103  groupSDBuffers.emplace_back( imf_, "startPointIndex", startPointIndex, groupCount, true );
1104  groupSDBuffers.emplace_back( imf_, "pointCount", pointCount, groupCount, true );
1105 
1106  CompressedVectorWriter writer = groups.writer( groupSDBuffers );
1107  writer.write( groupCount );
1108  writer.close();
1109 
1110  return true;
1111  }
1112 
1113 } // end namespace e57
std::shared_ptr< core::Tensor > image
int count
int offset
int points
An E57 element encoding an fixed-length sequence of bytes with an opaque format.
An E57 element containing ordered vector of child nodes, stored in an efficient binary format.
An E57 element encoding a single or double precision IEEE floating point number.
An E57 element encoding an integer value.
Generic handle to any of the 8 types of E57 element objects.
An E57 element encoding a fixed point number.
An E57 element encoding a Unicode character string value.
An E57 element containing named child nodes.
An E57 element containing ordered vector of child nodes.
CompressedVectorWriter writer(std::vector< SourceDestBuffer > &sbufs)
Create an iterator object for writing a series of blocks of data to a CompressedVectorNode.
Definition: E57Format.cpp:3032
void write(const size_t recordCount)
Request transfer of blocks of data to CompressedVectorNode from previously designated source buffers.
Definition: E57Format.cpp:2563
void close()
End the write operation.
Definition: E57Format.cpp:2666
bool isOpen() const
Test whether ImageFile is still open for accessing.
Definition: E57Format.cpp:4586
void extensionsAdd(const ustring &prefix, const ustring &uri)
Declare the use of an E57 extension in an ImageFile being written.
Definition: E57Format.cpp:4697
void close()
Complete any write operations on an ImageFile, and close the file on the disk.
Definition: E57Format.cpp:4557
bool isDefined(const ustring &pathName) const
Is the given pathName defined relative to this node.
Definition: E57Format.cpp:1297
void set(const ustring &pathName, const Node &n)
Add a new child at a given path.
Definition: E57Format.cpp:1388
Node get(int64_t index) const
Get a child element by positional index.
Definition: E57Format.cpp:1319
Node get(int64_t index) const
Get a child element by positional index.
Definition: E57Format.cpp:1641
void append(const Node &n)
Append a child element to end of VectorNode.
Definition: E57Format.cpp:1701
int64_t childCount() const
Get number of child elements in this VectorNode.
Definition: E57Format.cpp:1599
ImageFile GetRawIMF()
Definition: WriterImpl.cpp:120
int64_t NewData3D(Data3D &data3DHeader)
Definition: WriterImpl.cpp:416
VectorNode GetRawData3D()
Definition: WriterImpl.cpp:110
bool IsOpen() const
Definition: WriterImpl.cpp:89
int64_t NewImage2D(Image2D &image2DHeader)
Definition: WriterImpl.cpp:125
int64_t WriteImage2DData(int64_t imageIndex, Image2DType imageType, Image2DProjection imageProjection, void *pBuffer, int64_t start, int64_t count)
Definition: WriterImpl.cpp:369
VectorNode GetRawImages2D()
Definition: WriterImpl.cpp:115
WriterImpl(const ustring &filePath, const ustring &coordinateMetaData)
Definition: WriterImpl.cpp:36
StructureNode GetRawE57Root()
Definition: WriterImpl.cpp:105
MiniVec< float, N > floor(const MiniVec< float, N > &a)
Definition: MiniVec.h:75
E57_DLL void getVersions(int &astmMajor, int &astmMinor, std::string &libraryId)
Get the version of ASTM E57 standard that the API implementation supports, and library id string.
constexpr char E57_V1_0_URI[]
Verify all checksums. This is the default. (slow)
Definition: E57Format.h:107
@ E57_SINGLE
32 bit IEEE floating point number format
Definition: E57Format.h:72
@ E57_DOUBLE
64 bit IEEE floating point number format
Definition: E57Format.h:73
Image2DProjection
Identifies the representation for the image data.
@ E57_SPHERICAL
SphericalRepresentation for the image data.
@ E57_CYLINDRICAL
CylindricalRepresentation for the image data.
@ E57_NO_PROJECTION
No representation for the image data is present.
@ E57_VISUAL
VisualReferenceRepresentation for the image data.
@ E57_PINHOLE
PinholeRepresentation for the image data.
Image2DType
Identifies the format representation for the image data.
@ E57_JPEG_IMAGE
JPEG format image data.
@ E57_PNG_IMAGE
PNG format image data.
@ E57_NO_IMAGE
No image data.
@ E57_PNG_IMAGE_MASK
PNG format image mask.
constexpr double E57_NOT_SCALED_USE_FLOAT
Indicates to use FloatNode instead of ScaledIntegerNode in fields that can use both.
Definition: E57SimpleData.h:40
std::string ustring
UTF-8 encodeded Unicode string.
Definition: E57Format.h:54
std::string generateRandomGUID()
generates a new random GUID
Definition: Common.cpp:11
double pixelHeight
The height of a pixel in the image (in meters). Shall be positive.
int32_t imageWidth
The image width (in pixels). Shall be positive.
int32_t imageHeight
The image height (in pixels). Shall be positive.
double pixelWidth
The width of a pixel in the image (in radians). Shall be positive.
int64_t jpegImageSize
Size of JPEG format image data in Blob.
int64_t pngImageSize
Size of PNG format image data in Blob.
int64_t imageMaskSize
Size of PNG format image mask in Blob.
Stores pointers to user-provided buffers.
float * normalZ
The Z component of a surface normal vector (E57_EXT_surface_normals).
COORDTYPE * cartesianX
pointer to a buffer with the X coordinate (in meters) of the point in Cartesian coordinates
float * normalX
The X component of a surface normal vector (E57_EXT_surface_normals).
int8_t * isColorInvalid
Value = 0 if the color is considered valid, 1 otherwise.
int8_t * cartesianInvalidState
Value = 0 if the point is considered valid, 1 otherwise.
float * normalY
The Y component of a surface normal vector (E57_EXT_surface_normals).
COORDTYPE * sphericalAzimuth
pointer to a buffer with the Azimuth angle (in radians) of point in spherical coordinates
COORDTYPE * sphericalElevation
pointer to a buffer with the Elevation angle (in radians) of point in spherical coordinates
COORDTYPE * cartesianY
pointer to a buffer with the Y coordinate (in meters) of the point in Cartesian coordinates
int8_t * sphericalInvalidState
Value = 0 if the range is considered valid, 1 otherwise.
uint8_t * colorRed
pointer to a buffer with the Red color coefficient. Unit is unspecified
float * intensity
pointer to a buffer with the Point response intensity. Unit is unspecified
int8_t * isIntensityInvalid
Value = 0 if the intensity is considered valid, 1 otherwise.
COORDTYPE * sphericalRange
pointer to a buffer with the range (in meters) of points in spherical coordinates....
int8_t * isTimeStampInvalid
Value = 0 if the timeStamp is considered valid, 1 otherwise.
COORDTYPE * cartesianZ
pointer to a buffer with the Z coordinate (in meters) of the point in Cartesian coordinates
uint8_t * colorGreen
pointer to a buffer with the Green color coefficient. Unit is unspecified
uint8_t * colorBlue
pointer to a buffer with the Blue color coefficient. Unit is unspecified
Stores the top-level information for a single lidar scan.
float relativeHumidity
ustring guid
A globally unique identification string for the current version of the Data3D object.
ustring sensorHardwareVersion
The version number for the sensor hardware at the time of data collection.
ustring sensorFirmwareVersion
std::vector< ustring > originalGuids
ustring name
A user-defined name for the Data3D.
IndexBounds indexBounds
The bounds of the row, column, and return number of all the points in this Data3D.
ustring sensorSerialNumber
The serial number for the sensor.
float temperature
ustring description
A user-defined description of the Image.
ustring sensorModel
The model name or number for the sensor.
float atmosphericPressure
ustring sensorVendor
The name of the manufacturer for the sensor used to collect the points in this Data3D.
ustring sensorSoftwareVersion
The version number for the software used for the data collection.
int32_t isAtomicClockReferenced
double dateTimeValue
The time, in seconds, since GPS time was zero. This time specification may include fractions of a sec...
Stores an image from a camera.
ustring sensorVendor
The name of the manufacturer for the sensor used to collect the points in this Data3D.
ustring guid
A globally unique identification string for the current version of the Image2D object.
ustring description
A user-defined description of the Image2D.
ustring name
A user-defined name for the Image2D.
PinholeRepresentation pinholeRepresentation
Representation for an image using the pinhole camera projection model.
ustring sensorModel
The model name or number for the sensor.
SphericalRepresentation sphericalRepresentation
Representation for an image using the spherical camera projection model.
ustring associatedData3DGuid
RigidBodyTransform pose
ustring sensorSerialNumber
The serial number for the sensor.
CylindricalRepresentation cylindricalRepresentation
Representation for an image using the cylindrical camera projection model.
DateTime acquisitionDateTime
The date and time that the image was taken.
VisualReferenceRepresentation visualReferenceRepresentation
Stores the minimum and maximum of rowIndex, columnIndex, and returnIndex fields for a set of points.
double principalPointY
The Y coordinate in the image of the principal point (in pixels).
int64_t imageMaskSize
Size of PNG format image mask in BlobNode.
int32_t imageHeight
The image height (in pixels). Shall be positive.
double focalLength
The camera's focal length (in meters). Shall be positive.
int64_t jpegImageSize
Size of JPEG format image data in BlobNode.
double pixelWidth
The width of the pixels in the camera (in meters). Shall be positive.
double pixelHeight
The height of the pixels in the camera (in meters). Shall be positive.
int32_t imageWidth
The image width (in pixels). Shall be positive.
int64_t pngImageSize
Size of PNG format image data in BlobNode.
double z
The k coefficient of the quaternion.
Definition: E57SimpleData.h:77
double y
The j coefficient of the quaternion.
Definition: E57SimpleData.h:76
double x
The i coefficient of the quaternion.
Definition: E57SimpleData.h:75
double w
The real part of the quaternion. Shall be nonnegative.
Definition: E57SimpleData.h:74
Defines a rigid body transform in cartesian coordinates.
Definition: E57SimpleData.h:98
Translation translation
The translation point vector, t, of the transform.
Quaternion rotation
A unit quaternion representing the rotation, R, of the transform.
Definition: E57SimpleData.h:99
int32_t imageHeight
The image height (in pixels). Shall be positive.
int64_t imageMaskSize
Size of PNG format image mask in BlobNode.
double pixelHeight
The height of a pixel in the image (in radians). Shall be positive.
int64_t jpegImageSize
Size of JPEG format image data in BlobNode.
double pixelWidth
The width of a pixel in the image (in radians). Shall be positive.
int32_t imageWidth
The image width (in pixels). Shall be positive.
int64_t pngImageSize
Size of PNG format image data in BlobNode.
double y
The Y coordinate of the translation (in meters)
Definition: E57SimpleData.h:53
double x
The X coordinate of the translation (in meters)
Definition: E57SimpleData.h:52
double z
The Z coordinate of the translation (in meters)
Definition: E57SimpleData.h:54
int32_t imageHeight
The image height (in pixels). Shall be positive.
int64_t imageMaskSize
Size of PNG format image mask in BlobNode.
int32_t imageWidth
The image width (in pixels). Shall be positive.
int64_t jpegImageSize
Size of JPEG format image data in BlobNode.
int64_t pngImageSize
Size of PNG format image data in BlobNode.