text: /*
   * DemoWrite01.cpp - small write demo using the E57 format Foundation API.
   *
   * Copyright (c) 2009 - 2010 Kevin Ackley (kackley@gwi.net)
   * Copyright (c) 2020 Michele Adduci (adduci@tutanota.com)
   *
   * Permission is hereby granted, free of charge, to any person or organization
   * obtaining a copy of the software and accompanying documentation covered by
   * this license (the "Software") to use, reproduce, display, distribute,
   * execute, and transmit the Software, and to prepare derivative works of the
   * Software, and to permit third-parties to whom the Software is furnished to
   * do so, all subject to the following:
   *
   * The copyright notices in the Software and this entire statement, including
   * the above license grant, this restriction and the following disclaimer,
   * must be included in all copies of the Software, in whole or in part, and
   * all derivative works of the Software, unless such copies or derivative
   * works are solely in the form of machine-executable object code generated by
   * a source language processor.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
   * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
   * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   * DEALINGS IN THE SOFTWARE.
   */
  #include <openE57/openE57.h>
  using namespace e57;
  using namespace std;
  
  int main(int /*argc*/, char** /*argv*/)
  {
    try
    {
      /// Open new file for writing, get the initialized root node (a Structure).
      /// Path name: "/"
      ImageFile     imf("foo.e57", "w");
      StructureNode root = imf.root();
  
      /// Register extension with URI=www.example.com/DemoExtension and prefix=demo
      imf.extensionsAdd("demo", "http://www.example.com/DemoExtension");
  
      /// Set per-file properties.
      /// Path names: "/formatName", "/majorVersion", "/minorVersion", "/coordinateMetadata"
      root.set("formatName", StringNode(imf, "ASTM E57 3D Imaging Data File"));
      root.set("guid", StringNode(imf, "3F2504E0-4F89-11D3-9A0C-0305E82C3300"));
  
      /// Get ASTM version number supported by library, so can write it into file
      int     astmMajor;
      int     astmMinor;
      ustring libraryId;
      E57Utilities().getVersions(astmMajor, astmMinor, libraryId);
      root.set("versionMajor", IntegerNode(imf, astmMajor));
      root.set("versionMinor", IntegerNode(imf, astmMinor));
  
      /// Save a dummy string for coordinate system.
      /// Really should be a valid WKT string identifying the coordinate reference system (CRS).
      root.set("coordinateMetadata", StringNode(imf, "...A WKT string here..."));
  
      /// Create creationDateTime structure
      /// Path name: "/creationDateTime
      StructureNode creationDateTime = StructureNode(imf);
      root.set("creationDateTime", creationDateTime);
      creationDateTime.set("dateTimeValue", FloatNode(imf, 123.456)); //!!! convert time() to GPStime
  
      /// Create 3D data area.
      /// Path name: "/data3D"
      VectorNode data3D = VectorNode(imf, true);
      root.set("data3D", data3D);
  
      /// Add first scan
      /// Path name: "/data3D/0"
      StructureNode scan0 = StructureNode(imf);
      data3D.append(scan0);
  
      /// Add guid to scan0.
      /// Path name: "/data3D/0/guid".
      const char* scanGuid0 = "3F2504E0-4F89-11D3-9A0C-0305E82C3301";
      scan0.set("guid", StringNode(imf, scanGuid0));
  
      /// Add an extension field as an example
      scan0.set("demo:extra1", StringNode(imf, "used by demo extension"));
  
      /// Make a prototype of datatypes that will be stored in points record.
      /// This prototype will be used in creating the points CompressedVector.
      /// Using this proto in a CompressedVector will define path names like:
      ///      "/data3D/0/points/0/cartesianX"
      StructureNode proto = StructureNode(imf);
      proto.set("cartesianX", ScaledIntegerNode(imf, 0, 0, 32767, 0.001, 0));
      proto.set("cartesianY", ScaledIntegerNode(imf, 0, 0, 32767, 0.001, 0));
      proto.set("cartesianZ", ScaledIntegerNode(imf, 0, 0, 32767, 0.001, 0));
      proto.set("cartesianInvalidState", IntegerNode(imf, 0, 0, 2));
      proto.set("rowIndex", IntegerNode(imf, 0, 0, 1));
      proto.set("columnIndex", IntegerNode(imf, 0, 0, 4));
      proto.set("returnIndex", IntegerNode(imf, 0, 0, 0));
      proto.set("returnCount", IntegerNode(imf, 1, 1, 1));
      proto.set("timeStamp", FloatNode(imf, 0.0, E57_DOUBLE));
      proto.set("intensity", IntegerNode(imf, 0, 0, 255));
      proto.set("colorRed", FloatNode(imf, 0.0, E57_SINGLE, 0.0, 1.0));
      proto.set("colorGreen", FloatNode(imf, 0.0, E57_SINGLE, 0.0, 1.0));
      proto.set("colorBlue", FloatNode(imf, 0.0, E57_SINGLE, 0.0, 1.0));
      proto.set("demo:extra2", StringNode(imf));
  
      /// Make empty codecs vector for use in creating points CompressedVector.
      /// If this vector is empty, it is assumed that all fields will use the BitPack codec.
      VectorNode codecs = VectorNode(imf, true);
  
      /// Create CompressedVector for storing points.  Path Name: "/data3D/0/points".
      /// We use the prototype and empty codecs tree from above.
      /// The CompressedVector will be filled by code below.
      CompressedVectorNode points = CompressedVectorNode(imf, proto, codecs);
      scan0.set("points", points);
  
      /// Create pose structure for scan.
      /// Path names: "/data3D/0/pose/rotation/w", etc...
      ///             "/data3D/0/pose/translation/x", etc...
      StructureNode pose = StructureNode(imf);
      scan0.set("pose", pose);
      StructureNode rotation = StructureNode(imf);
      pose.set("rotation", rotation);
      rotation.set("w", FloatNode(imf, 1.0));
      rotation.set("x", FloatNode(imf, 0.0));
      rotation.set("y", FloatNode(imf, 0.0));
      rotation.set("z", FloatNode(imf, 0.0));
      StructureNode translation = StructureNode(imf);
      pose.set("translation", translation);
      translation.set("x", FloatNode(imf, 0.0));
      translation.set("y", FloatNode(imf, 0.0));
      translation.set("z", FloatNode(imf, 0.0));
  
      ///================
      /// Add grouping scheme area
      /// Path name: "/data3D/0/pointGroupingSchemes"
      StructureNode pointGroupingSchemes = StructureNode(imf);
      scan0.set("pointGroupingSchemes", pointGroupingSchemes);
  
      /// Add a line grouping scheme
      /// Path name: "/data3D/0/pointGroupingSchemes/groupingByLine"
      StructureNode groupingByLine = StructureNode(imf);
      pointGroupingSchemes.set("groupingByLine", groupingByLine);
  
      /// Add idElementName to groupingByLine, specify a line is column oriented
      /// Path name: "/data3D/0/pointGroupingSchemes/groupingByLine/idElementName"
      groupingByLine.set("idElementName", StringNode(imf, "columnIndex"));
  
      /// Make a prototype of datatypes that will be stored in LineGroupRecord.
      /// This prototype will be used in creating the groups CompressedVector.
      /// Will define path names like:
      ///     "/data3D/0/pointGroupingSchemes/groupingByLine/groups/0/idElementValue"
      StructureNode lineGroupProto = StructureNode(imf);
      lineGroupProto.set("idElementValue", IntegerNode(imf, 0, 0, 4));
      lineGroupProto.set("startPointIndex", IntegerNode(imf, 0, 0, 9));
      lineGroupProto.set("pointCount", IntegerNode(imf, 1, 1, 2));
  
      /// Add cartesian bounds to line group prototype
      /// Will define path names like:
      ///     "/data3D/0/pointGroupingSchemes/groupingByLine/groups/0/cartesianBounds/xMinimum"
      StructureNode lineGroupBbox = StructureNode(imf);
      lineGroupProto.set("cartesianBounds", lineGroupBbox);
      lineGroupBbox.set("xMinimum", FloatNode(imf, 0.0));
      lineGroupBbox.set("xMaximum", FloatNode(imf, 0.0));
      lineGroupBbox.set("yMinimum", FloatNode(imf, 0.0));
      lineGroupBbox.set("yMaximum", FloatNode(imf, 0.0));
      lineGroupBbox.set("zMinimum", FloatNode(imf, 0.0));
      lineGroupBbox.set("zMaximum", FloatNode(imf, 0.0));
  
      /// Make empty codecs vector for use in creating groups CompressedVector.
      /// If this vector is empty, it is assumed that all fields will use the BitPack codec.
      VectorNode lineGroupCodecs = VectorNode(imf, true);
  
      /// Create CompressedVector for storing groups.
      /// Path Name: "/data3D/0/pointGroupingSchemes/groupingByLine/groups".
      /// We use the prototype and empty codecs tree from above.
      /// The CompressedVector will be filled by code below.
      CompressedVectorNode groups = CompressedVectorNode(imf, lineGroupProto, lineGroupCodecs);
      groupingByLine.set("groups", groups);
  
      ///================
      /// Add name and description to scan
      /// Path names: "/data3D/0/name", "/data3D/0/description".
      scan0.set("name", StringNode(imf, "Station 3F"));
      scan0.set("description", StringNode(imf, "An example scan"));
  
      /// Add Cartesian bounding box to scan.
      /// Path names: "/data3D/0/cartesianBounds/xMinimum", etc...
      StructureNode bbox = StructureNode(imf);
      bbox.set("xMinimum", FloatNode(imf, 0.0));
      bbox.set("xMaximum", FloatNode(imf, 1.0));
      bbox.set("yMinimum", FloatNode(imf, 0.0));
      bbox.set("yMaximum", FloatNode(imf, 1.0));
      bbox.set("zMinimum", FloatNode(imf, 0.0));
      bbox.set("zMaximum", FloatNode(imf, 1.0));
      scan0.set("cartesianBounds", bbox);
  
      /// Add start/stop acquisition times to scan.
      /// Path names: "/data3D/0/acquisitionStart/dateTimeValue",
      ///             "/data3D/0/acquisitionEnd/dateTimeValue"
      StructureNode acquisitionStart = StructureNode(imf);
      scan0.set("acquisitionStart", acquisitionStart);
      acquisitionStart.set("dateTimeValue", FloatNode(imf, 1235.));
      StructureNode acquisitionEnd = StructureNode(imf);
      scan0.set("acquisitionEnd", acquisitionEnd);
      acquisitionEnd.set("dateTimeValue", FloatNode(imf, 1235.));
  
      /// Add various sensor and version strings to scan.
      /// Path names: "/data3D/0/sensorVendor", etc...
      scan0.set("sensorVendor", StringNode(imf, "Scan Co"));
      scan0.set("sensorModel", StringNode(imf, "Scanmatic 2000"));
      scan0.set("sensorSerialNumber", StringNode(imf, "123-321"));
      scan0.set("sensorHardwareVersion", StringNode(imf, "3.3.0"));
      scan0.set("sensorSoftwareVersion", StringNode(imf, "27.0.3"));
      scan0.set("sensorFirmwareVersion", StringNode(imf, "27.0.3"));
  
      /// Add temp/humidity to scan.
      /// Path names: "/data3D/0/temperature", etc...
      scan0.set("temperature", FloatNode(imf, 20.0));
      scan0.set("relativeHumidity", FloatNode(imf, 40.0));
  
      ///================
      /// Create 2D image area.
      /// Path name: "/images2D"
      VectorNode images2D = VectorNode(imf, true);
      root.set("images2D", images2D);
  
      /// Add a first picture area to the list.
      /// Path name: "/images2D/0"
      StructureNode picture0 = StructureNode(imf);
      images2D.append(picture0);
  
      /// Attach a guid to first picture.
      /// Path name: "/images2D/0/guid"
      picture0.set("guid", StringNode(imf, "3F2504E0-4F89-11D3-9A0C-0305E82C3302"));
  
      /// Make an area holding pinhole projection parameters for first picture.
      /// Path name: "/images2D/0/pinholeRepresentation"
      StructureNode pinhole = StructureNode(imf);
      picture0.set("pinholeRepresentation", pinhole);
  
      /// Add pinhole parameters
      /// Path names: "/images2D/0/pinholeRepresentation/imageWidth", etc...
      pinhole.set("imageWidth", IntegerNode(imf, 1024));
      pinhole.set("imageHeight", IntegerNode(imf, 1024));
      pinhole.set("focalLength", FloatNode(imf, 1.0));
      pinhole.set("pixelWidth", FloatNode(imf, 1e-3));
      pinhole.set("pixelHeight", FloatNode(imf, 1e-3));
      pinhole.set("principalPointX", FloatNode(imf, 512.0));
      pinhole.set("principalPointY", FloatNode(imf, 512.0));
  
      /// Create a toy 10 byte blob (instead of copying a big .jpg into .e57 file)
      /// Path name: "/images2D/0/pinholeRepresentation/jpegImage"
      BlobNode jpegImage = BlobNode(imf, 10);
      pinhole.set("jpegImage", jpegImage);
  
      /// Save some fake data in the blob
      uint8_t fakeBlobData[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      jpegImage.write(fakeBlobData, 0, 10);
  
      /// Attach a pose to picture
      /// Path names: "/images2D/0/pose/rotation/w", etc...
      ///             "/images2D/0/pose/translation/x", etc...
      StructureNode picturePose = StructureNode(imf);
      picture0.set("pose", picturePose);
      StructureNode pictureRotation = StructureNode(imf);
      picturePose.set("rotation", pictureRotation);
      pictureRotation.set("w", FloatNode(imf, 1.0));
      pictureRotation.set("x", FloatNode(imf, 0.0));
      pictureRotation.set("y", FloatNode(imf, 0.0));
      pictureRotation.set("z", FloatNode(imf, 0.0));
      StructureNode pictureTranslation = StructureNode(imf);
      picturePose.set("translation", pictureTranslation);
      pictureTranslation.set("x", FloatNode(imf, 0.0));
      pictureTranslation.set("y", FloatNode(imf, 0.0));
      pictureTranslation.set("z", FloatNode(imf, 0.0));
  
      /// Add name, description, time, associated guid to first picture.
      /// Path names: "/images2D/0/name", etc...
      picture0.set("name", StringNode(imf, "pic123"));
      picture0.set("description", StringNode(imf, "trial picture"));
      picture0.set("associatedData3DGuid", StringNode(imf, scanGuid0));
      StructureNode acquisitionDateTime = StructureNode(imf);
      picture0.set("acquisitionDateTime", acquisitionDateTime);
      acquisitionDateTime.set("dateTimeValue", FloatNode(imf, 123.456)); //!!! convert time() to GPStime
  
      ///================
      /// Prepare vector of source buffers for writing in the CompressedVector of points
      const int                N                        = 10;
      double                   cartesianX[N]            = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0};
      double                   cartesianY[N]            = {1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 8.1, 9.1, 10.1};
      double                   cartesianZ[N]            = {1.2, 2.2, 3.2, 4.2, 5.2, 6.2, 7.2, 8.2, 9.1, 10.2};
      int32_t                  cartesianInvalidState[N] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int32_t                  rowIndex[N]              = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1};
      int32_t                  columnIndex[N]           = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4};
      int32_t                  returnIndex[N]           = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      int32_t                  returnCount[N]           = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
      double                   timeStamp[N]             = {.1, .2, .3, .4, .5, .6, .7, .8, .9, 1.0};
      int32_t                  intensity[N]             = {1, 2, 3, 2, 1, 1, 2, 3, 2, 1};
      double                   colorRed[N]              = {.1, .2, .3, .4, .5, 6., .7, .8, .9, 1.0};
      double                   colorGreen[N]            = {.5, .5, .5, .5, .5, .5, .5, .5, .5, .5};
      double                   colorBlue[N]             = {1.0, .9, .8, .7, .6, .5, .4, .3, .2, .1};
      ustring                  demoExtra2[N]            = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};
      vector<SourceDestBuffer> sourceBuffers;
      sourceBuffers.push_back(SourceDestBuffer(imf, "cartesianX", cartesianX, N, true, true));
      sourceBuffers.push_back(SourceDestBuffer(imf, "cartesianY", cartesianY, N, true, true));
      sourceBuffers.push_back(SourceDestBuffer(imf, "cartesianZ", cartesianZ, N, true, true));
      sourceBuffers.push_back(SourceDestBuffer(imf, "cartesianInvalidState", cartesianInvalidState, N, true));
      sourceBuffers.push_back(SourceDestBuffer(imf, "rowIndex", rowIndex, N, true));
      sourceBuffers.push_back(SourceDestBuffer(imf, "columnIndex", columnIndex, N, true));
      sourceBuffers.push_back(SourceDestBuffer(imf, "returnIndex", returnIndex, N, true));
      sourceBuffers.push_back(SourceDestBuffer(imf, "returnCount", returnCount, N, true));
      sourceBuffers.push_back(SourceDestBuffer(imf, "timeStamp", timeStamp, N, true));
      sourceBuffers.push_back(SourceDestBuffer(imf, "intensity", intensity, N, true));
      sourceBuffers.push_back(SourceDestBuffer(imf, "colorRed", colorRed, N, true));
      sourceBuffers.push_back(SourceDestBuffer(imf, "colorGreen", colorGreen, N, true));
      sourceBuffers.push_back(SourceDestBuffer(imf, "colorBlue", colorBlue, N, true));
  
      /// Create a vector<ustring> from c++ array of values
      vector<ustring> demoExtra2v;
      for (int i = 0; i < N; i++)
        demoExtra2v.push_back(demoExtra2[i]);
      sourceBuffers.push_back(SourceDestBuffer(imf, "demo:extra2", &demoExtra2v));
  
      /// Write source buffers into CompressedVector
      {
        CompressedVectorWriter writer = points.writer(sourceBuffers);
        writer.write(N);
        writer.close();
      }
  
      ///================
      /// Prepare vector of source buffers for writing in the CompressedVector of groups
      const int                NG                  = 5;
      int32_t                  idElementValue[NG]  = {0, 1, 2, 3, 4};
      int32_t                  startPointIndex[NG] = {0, 2, 4, 6, 8};
      int32_t                  pointCount[NG]      = {2, 2, 2, 2, 2};
      double                   xMinimum[NG]        = {1.0, 3.0, 5.0, 7.0, 9.0};
      double                   xMaximum[NG]        = {2.0, 4.0, 6.0, 8.0, 10.0};
      double                   yMinimum[NG]        = {1.1, 3.1, 5.1, 7.1, 9.1};
      double                   yMaximum[NG]        = {2.1, 4.1, 6.1, 8.1, 10.1};
      double                   zMinimum[NG]        = {1.2, 3.2, 5.2, 7.2, 9.2};
      double                   zMaximum[NG]        = {2.2, 4.2, 6.2, 8.2, 10.2};
      vector<SourceDestBuffer> groupSDBuffers;
      groupSDBuffers.push_back(SourceDestBuffer(imf, "idElementValue", idElementValue, NG, true));
      groupSDBuffers.push_back(SourceDestBuffer(imf, "startPointIndex", startPointIndex, NG, true));
      groupSDBuffers.push_back(SourceDestBuffer(imf, "pointCount", pointCount, NG, true));
      groupSDBuffers.push_back(SourceDestBuffer(imf, "cartesianBounds/xMinimum", xMinimum, NG, true));
      groupSDBuffers.push_back(SourceDestBuffer(imf, "cartesianBounds/xMaximum", xMaximum, NG, true));
      groupSDBuffers.push_back(SourceDestBuffer(imf, "cartesianBounds/yMinimum", yMinimum, NG, true));
      groupSDBuffers.push_back(SourceDestBuffer(imf, "cartesianBounds/yMaximum", yMaximum, NG, true));
      groupSDBuffers.push_back(SourceDestBuffer(imf, "cartesianBounds/zMinimum", zMinimum, NG, true));
      groupSDBuffers.push_back(SourceDestBuffer(imf, "cartesianBounds/zMaximum", zMaximum, NG, true));
  
      /// Write source buffers into CompressedVector
      {
        CompressedVectorWriter writer = groups.writer(groupSDBuffers);
        writer.write(NG);
        writer.close();
      }
  
      imf.close();
    }
    catch (E57Exception& ex)
    {
      ex.report(__FILE__, __LINE__, __FUNCTION__);
    }
    catch (std::exception& ex)
    {
      cerr << "Got an std::exception, what=" << ex.what() << endl;
    }
    catch (...)
    {
      cerr << "Got an unknown exception" << endl;
    }
    return (0);
  }
  