Unverified Commit 762a4a7a authored by ihumphrey's avatar ihumphrey Committed by GitHub
Browse files

Merge pull request #40 from ihumphrey-usgs/LidarData-json

LidarData json and binary serialization
@makaylas approves this message
parents 52130a4f 01f92643
Loading
Loading
Loading
Loading
+202 −13
Original line number Diff line number Diff line
#include "LidarData.h"

#include <QByteArray>
#include <QDebug>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QList>
#include <QRegExp>
#include <QSharedPointer>
#include <QTextStream>
#include <QStringList>
#include <QTextStream>

#include "Angle.h"
#include "Distance.h"
#include "FileName.h"
#include "IException.h"
#include "iTime.h"
#include "FileName.h"
#include "Latitude.h"
#include "LidarControlPoint.h"
#include "Longitude.h"
#include "SurfacePoint.h"

namespace Isis {

@@ -25,13 +34,15 @@ namespace Isis {


  /**
   * Constructor that takes a Lidar CSV file.
   * Constructor that takes a Lidar CSV file to create LidarControlPoints.
   *
   * This constructor uses the input CSV file to add internal LidarControlPoints from the CSV data.
   * This is NOT for unserializing LidarData from a file - use the read() method.
   *
   * @param FileName lidarFile Name of the Lidar CSV file to use.
   */
  LidarData::LidarData(FileName lidarFile) {


    readCsv(lidarFile);
  }


@@ -58,12 +69,15 @@ namespace Isis {
  /**
   * @brief Reads in a Lidar CSV file.
   *
   * Reads in a CSV file that contains raw Lidar data and then creates LidarControlPoints from
   * this data. (This is NOT an unserialization method - use read() for unserializing LidarData
   * from a file).
   *
   * @param FileName lidarFile Name of the Lidar CSV file to read.
   * @throws If the header for the file does not contain the correct info,
   *         an IException will be thrown.
   */
  void LidarData::read(FileName lidarFile) {

  void LidarData::readCsv(FileName lidarFile) {
    QFile data(lidarFile.expanded());

    if (data.open(QIODevice::ReadOnly|QIODevice::Text)) {
@@ -104,16 +118,191 @@ namespace Isis {
        m_points.insert(key,pt);
      }
    }
    return;
  }


  /**
   * Writes out the Lidar data to a CSV file.
   * @brief Unserialize LidarData.
   *
   * This method unserializes LidarData from a JSON or binary (QByteArray) file. It will
   * automatically determine if it is JSON or binary formatted date.
   *
   * @param FileName lidarFile Name of the serialized LidarData file to read.
   * @throws IException::User Throws User exception if it cannot open the file passed.
   */
  void LidarData::read(FileName lidarDataFile) {
    // Set up the input file
    QFile loadFile(lidarDataFile.expanded());
    // Make sure we can open the file successfully
    if (!loadFile.open(QIODevice::ReadOnly)) {
      QString msg("Could not open " + loadFile.fileName());
      throw IException(IException::User, msg, _FILEINFO_);
    }

    // Load file
    QByteArray saveData = loadFile.readAll();
    // Try to load from JSON (ASCII); if it can not, load as binary.
    QJsonDocument loadDoc(QJsonDocument::fromJson(saveData));
    if (loadDoc.isNull()) {
      loadDoc = QJsonDocument::fromBinaryData(saveData);
    }

    // Unserialize LidarData
    QJsonObject lidarDataObject = loadDoc.object();
    if (lidarDataObject.contains("points") && lidarDataObject["points"].isArray()) {
      // Unserialize LidarControlPoints
      QJsonArray pointArray = lidarDataObject["points"].toArray();
      for (int pointIndex = 0; pointIndex < pointArray.size(); pointIndex++) {
        // Unserialize LidarControlPoint
        QJsonObject pointObject = pointArray[pointIndex].toObject();

        QString id;
        if (pointObject.contains("id") && pointObject["id"].isString()) {
          id = pointObject["id"].toString();
        }

        double range = 0.0;
        if (pointObject.contains("range") && pointObject["range"].isDouble()) {
          range = pointObject["range"].toDouble();
        }

        double sigmaRange = 0.0;
        if (pointObject.contains("sigmaRange") && pointObject["sigmaRange"].isDouble()) {
          sigmaRange = pointObject["sigmaRange"].toDouble();
        }

        double time = 0.0;
        if (pointObject.contains("time") && pointObject["time"].isDouble()) {
          time = pointObject["time"].toDouble();
        }

        double latitude = 0.0;
        if (pointObject.contains("latitude") && pointObject["latitude"].isDouble()) {
          latitude = pointObject["latitude"].toDouble();
        }

        double longitude = 0.0;
        if (pointObject.contains("longitude") && pointObject["longitude"].isDouble()) {
          longitude = pointObject["longitude"].toDouble();
        }

        double radius = 0.0;
        if (pointObject.contains("radius") && pointObject["radius"].isDouble()) {
          radius = pointObject["radius"].toDouble();
        }

        QSharedPointer<LidarControlPoint> lcp =
            QSharedPointer<LidarControlPoint>(new LidarControlPoint(iTime(time), range, sigmaRange));
        lcp->SetId(id);
        lcp->SetAprioriSurfacePoint(SurfacePoint(Latitude(latitude, Angle::Units::Degrees),
                                                 Longitude(longitude, Angle::Units::Degrees),
                                                 Distance(radius, Distance::Units::Kilometers)));

        // Unserialize ControlMeasures
        if (pointObject.contains("measures") && pointObject["measures"].isArray()) {
          QJsonArray measureArray = pointObject["measures"].toArray();
          for (int measureIndex = 0; measureIndex < measureArray.size(); measureIndex++) {
            // Unserialize ControlMeasure
            QJsonObject measureObject = measureArray[measureIndex].toObject();

            double line = 0.0;
            if (measureObject.contains("line") && measureObject["line"].toDouble()) {
              line = measureObject["line"].toDouble();
            }

            double sample = 0.0;
            if (measureObject.contains("sample") && measureObject["sample"].toDouble()) {
              sample = measureObject["sample"].toDouble();
            }

            QString serialNumber;
            if (measureObject.contains("serialNumber") && measureObject["serialNumber"].isString()) {
              serialNumber = measureObject["serialNumber"].toString();
            }

            ControlMeasure *measure = new ControlMeasure();
            measure->SetCoordinate(sample, line);
            measure->SetCubeSerialNumber(serialNumber);
            lcp->Add(measure);
          }
        }

        insert(lcp);
      }
    }
  }


  /**
   * @brief Serializes LidarData.
   *
   * This method serializes the LidarData to either a JSON or binary (QByteArray) file. If JSON,
   * the file extension will be .json; otherwise (if binary), the file extension will be .dat.
   *
   * @param FileName outputFile Name of the file to write to.
   * @param FileName outputFile Name of the file to serialize to.
   * @param LidarData::Format format Format of the serialized file (Json or Binary).
   *
   * @throws IException::User Throws User exception if it cannot open the file for writing.
   */
  void LidarData::write(FileName outputFile) {
  void LidarData::write(FileName outputFile, LidarData::Format format) {
    // Set up the output file
    if (format == Json) {
      outputFile = outputFile.setExtension("json");
    }
    else {
      outputFile = outputFile.setExtension("dat");
    }
    QFile saveFile(outputFile.expanded());

    if (!saveFile.open(QIODevice::WriteOnly)) {
      QString msg("Could not open " + saveFile.fileName());
      throw IException(IException::User, msg, _FILEINFO_);
    }

    // Serialize LidarData
    QJsonObject lidarDataObject;
    QJsonArray pointArray;
    // Serialize the LidarControlPoints it contains
    foreach (QSharedPointer<LidarControlPoint> lcp, points()) {
      // Serialize LidarControlPoint
      QJsonObject pointObject;
      pointObject["id"] = lcp->GetId();
      pointObject["range"] = lcp->range();
      pointObject["sigmaRange"] = lcp->sigmaRange();
      pointObject["time"] = lcp->time().Et();
      // Serialize the lat/lon/radius (AprioriSurfacePoint)
      SurfacePoint aprioriSurfacePoint = lcp->GetAprioriSurfacePoint();
      pointObject["latitude"] = aprioriSurfacePoint.GetLatitude().planetocentric(Angle::Units::Degrees);
      pointObject["longitude"] = aprioriSurfacePoint.GetLongitude().positiveEast(Angle::Units::Degrees);
      pointObject["radius"] = aprioriSurfacePoint.GetLocalRadius().kilometers();

      QJsonArray measureArray;
      // Serialize the ControlMeasures it contains
      foreach (ControlMeasure *measure, lcp->getMeasures()) {
        // Serialize ControlMeasure
        QJsonObject measureObject;
        measureObject["line"] = measure->GetLine();
        measureObject["sample"] = measure->GetSample();
        measureObject["serialNumber"] = measure->GetCubeSerialNumber();
        measureArray.append(measureObject);

      }
      // Add the ControlMeasures to the LidarControlPoint
      pointObject["measures"] = measureArray;

      pointArray.append(pointObject);

    }
    // Add the LidarControlPoints to the LidarData
    lidarDataObject["points"] = pointArray;

    // Write the JSON to the file
    QJsonDocument lidarDataDoc(lidarDataObject);
    if (format == Json) {
      saveFile.write(lidarDataDoc.toJson());
    }
    else {
      saveFile.write(lidarDataDoc.toBinaryData());
    }
  }
}
+19 −5
Original line number Diff line number Diff line
@@ -6,6 +6,8 @@
#include <QPointer>
#include <QString>

class QJsonObject;

namespace Isis {

  class FileName;
@@ -19,21 +21,33 @@ namespace Isis {
   * @internal
   *   @history 2018-01-29 Ian Humphrey - original version.
   *   @history 2018-01-31 Tyler Wilson - Implemented Lidar::read(Filename &).
   *   @history 2018-01-31 Ian Humphrey - Added insert method to insert a LidarControlPoint into
   *                           the LidarData. Added documentation for m_points.
   *   @history 2018-01-31 Ian Humphrey - Added insert method to insert a
   *                           LidarControlPoint into the LidarData. Added
   *                           documentation for m_points.
   *   @history 2018-02-03 Ian Humphrey - Renamed read to readCsv. read() and write()
   *                           methods support JSON or binary serialization. Added
   *                           documentation to new Format enumeration.
   */
  class LidarData {

    public:
      /** Enumerates the file formats for serializing the LidarData class. */
      enum Format {
        Binary, /**< Serializes to a binary (QByteArray) .dat file. */
        Json    /**< Serializes to a JSON .json file. */
      };

      LidarData();
      LidarData(FileName);

      void insert(QSharedPointer<LidarControlPoint> point);
      void readCsv(FileName);

      void insert(QSharedPointer<LidarControlPoint> point);
      QList< QSharedPointer<LidarControlPoint> > points() const;

      // Serialization methods or LidarData
      void read(FileName);
      void write(FileName);
      void write(FileName, Format);

    private:
      /** Hash of the LidarControlPoints this class contains. */
+97 −6
Original line number Diff line number Diff line
#include "LidarData.h"

#include <QDebug>
#include <QString>

#include "Angle.h"
#include "ControlMeasure.h"
#include "Distance.h"
#include "FileName.h"
#include "iTime.h"
#include "Latitude.h"
#include "LidarControlPoint.h"
#include "Longitude.h"
#include "Preference.h"
#include "SurfacePoint.h"

using namespace std;
using namespace Isis;

void print(const LidarData &lidarData);

/**
 * Unit test for the LIDARData class.
 *
@@ -21,7 +32,7 @@ int main(int argc, char *argv[]) {

  LidarData data;
  FileName csvFile("RDR_98E100E_60N62NPointPerRow_csv_table-original.csv");
  data.read(csvFile);
  data.readCsv(csvFile);

  // Test LidarData()
  cout << "Testing default constructor... " << endl;
@@ -40,18 +51,98 @@ int main(int argc, char *argv[]) {
  double sigmaRange = 0.1;
  QSharedPointer<LidarControlPoint> lcp =
      QSharedPointer<LidarControlPoint>(new LidarControlPoint(time, range, sigmaRange));
  lcp->SetId("testLidarControlPoint1");
  lcp->SetId("testLidarControlPoint");
  defaultData.insert(lcp);
  cout << "\tnumber of points: " << defaultData.points().size() << endl;
  cout << "\tname of point:    " << defaultData.points().first()->GetId() << endl;
  cout << endl;

  // Test read()
  cout << "Testing read(FileName)... " << endl;
  // Test write() JSON format
  cout << "Testing write(FileName)... " << endl;
  LidarData mockData;
  double lat, lon, rad;
  lat = 100.0;
  lon = 50.0;
  rad = 1000.0;
  for (int i = 1; i < 11; i++) {
    time += 60.0;
    range += 10.0;
    lcp = QSharedPointer<LidarControlPoint>(new LidarControlPoint(time, range, sigmaRange));
    lcp->SetId("testLidarControlPoint" + QString::number(i));
    lat += 1.0;
    lon += 1.0;
    SurfacePoint sp(Latitude(lat, Angle::Units::Degrees),
                    Longitude(lon, Angle::Units::Degrees),
                    Distance(rad, Distance::Units::Kilometers));
    lcp->SetAprioriSurfacePoint(sp);
    for (int j = 0; j < 2; j++) {
      ControlMeasure *measure = new ControlMeasure();
      measure->SetCoordinate((double) i, (double) j);
      measure->SetCubeSerialNumber("SN_" + QString::number(i) + "-" + QString::number(j));
      lcp->Add(measure);
    }
    mockData.insert(lcp);
  }
  FileName outputFile("./test.json");
  cout << outputFile.extension() << endl;
  mockData.write(outputFile, LidarData::Json);
  cout << endl;

  // Test write()
  cout << "Testing write(FileName)... " << endl;
  // Test write() with no data

  // Test read() with no data

  // Test write() binary format
  cout << outputFile.extension() << endl;
  outputFile = outputFile.setExtension("dat");
  cout << outputFile.extension() << endl;
  cout << outputFile.expanded().toStdString() << endl;
  mockData.write(outputFile, LidarData::Binary);

  // Test read() binary format
  cout << "Testing read(FileName) from binary data... " << endl;
  LidarData fromBinary;
  fromBinary.read(outputFile);
  print(fromBinary);
  cout << endl;

  // Test read()
  cout << "Testing read(FileName) from JSON data... " << endl;
  LidarData fromJson;
  outputFile = outputFile.setExtension("json");
  fromJson.read(outputFile);
  print(fromJson);
  cout << endl;

}


void print(const LidarData &lidarData) {
  QList< QSharedPointer<LidarControlPoint> > points = lidarData.points();
  std::cout << "LidarData:" << std::endl;
  foreach (QSharedPointer<LidarControlPoint> point, points) {
    std::cout << "\tLidarControlPoint:" << std::endl;
    std::cout << "\t\tid: " << point->GetId() << std::endl;;
    SurfacePoint sp = point->GetAprioriSurfacePoint();
    double lat, lon, rad;
    lat = sp.GetLatitude().planetocentric(Angle::Units::Degrees);
    lon = sp.GetLongitude().positiveEast(Angle::Units::Degrees);
    rad = sp.GetLocalRadius().kilometers();
    std::cout << "\t\tlatitude:  " << lat << std::endl;
    std::cout << "\t\tlongitude: " << lon << std::endl;
    std::cout << "\t\tradius:    " << rad << std::endl;
    std::cout << "\t\trange:     " << point->range() << std::endl;
    std::cout << "\t\tsigmaRange:" << point->sigmaRange() << std::endl;
    std::cout << "\t\ttime:      " << point->time().Et() << std::endl;
    QList<ControlMeasure *> measures = point->getMeasures();
    foreach (ControlMeasure *measure, measures) {
      std::cout << "\t\tControlMeasure: " << std::endl;
      std::cout << "\t\t\tline:   " << measure->GetLine() << std::endl;
      std::cout << "\t\t\tsample: " << measure->GetSample() << std::endl;
      std::cout << "\t\t\tSN:     " << measure->GetCubeSerialNumber() << std::endl;
      std::cout << "\t\t#END_ControlMeasure." << std::endl;
    }
    std::cout << "\t#END_LidarControlPoint." << std::endl << std::endl;
  }
  std::cout << std::endl;
}