Skip to content

v0.2.54..v0.2.55 changeset ChangesetCreator.cpp

Garret Voltz edited this page Aug 14, 2020 · 1 revision
diff --git a/hoot-core/src/main/cpp/hoot/core/algorithms/changeset/ChangesetCreator.cpp b/hoot-core/src/main/cpp/hoot/core/algorithms/changeset/ChangesetCreator.cpp
index 3401ed8..1d53c75 100644
--- a/hoot-core/src/main/cpp/hoot/core/algorithms/changeset/ChangesetCreator.cpp
+++ b/hoot-core/src/main/cpp/hoot/core/algorithms/changeset/ChangesetCreator.cpp
@@ -51,6 +51,7 @@
 #include <hoot/core/algorithms/changeset/ChangesetDeriver.h>
 #include <hoot/core/io/OsmChangesetFileWriterFactory.h>
 #include <hoot/core/io/OsmChangesetFileWriter.h>
+#include <hoot/core/util/FileUtils.h>
 
 //GEOS
 #include <geos/geom/Envelope.h>
@@ -64,11 +65,14 @@ namespace hoot
 
 const QString ChangesetCreator::JOB_SOURCE = "Derive Changeset";
 
-ChangesetCreator::ChangesetCreator(const bool printDetailedStats, const QString osmApiDbUrl) :
+ChangesetCreator::ChangesetCreator(
+  const bool printDetailedStats, const QString& statsOutputFile, const QString osmApiDbUrl) :
 _osmApiDbUrl(osmApiDbUrl),
 _numTotalTasks(0),
 _currentTaskNum(0),
+_includeReviews(false),
 _printDetailedStats(printDetailedStats),
+_statsOutputFile(statsOutputFile),
 _singleInput(false),
 _numCreateChanges(0),
 _numModifyChanges(0),
@@ -97,13 +101,9 @@ void ChangesetCreator::create(const QString& output, const QString& input1, cons
     "Creating changeset from inputs: " << input1 << " and " << input2 << " to output: " <<
     output << "...");
 
-  QFileInfo outputInfo(output);
-  LOG_VARD(outputInfo.dir().absolutePath());
-  const bool outputDirSuccess = QDir().mkpath(outputInfo.dir().absolutePath());
-  if (!outputDirSuccess)
-  {
-    throw IllegalArgumentException("Unable to create output path for: " + output);
-  }
+  // write the output dir now so we don't get a nasty surprise at the end of a long job that it
+  // can't be written
+  IoUtils::writeOutputDir(output);
 
   _singleInput = input2.trimmed().isEmpty();
   LOG_VARD(_singleInput);
@@ -133,6 +133,10 @@ void ChangesetCreator::create(const QString& output, const QString& input1, cons
         _numTotalTasks++;
       }
     }
+    if (!_includeReviews)
+    {
+      _numTotalTasks--;
+    }
   }
 
   _currentTaskNum = 1;
@@ -149,8 +153,8 @@ void ChangesetCreator::create(const QString& output, const QString& input1, cons
   //sortedElements2 is the newer state of the data
   ElementInputStreamPtr sortedElements2;
 
-  // TODO: We could use OsmUtils::checkVersionLessThanOneCountAndLogWarning() somewhere in here like
-  // we do with ChangesetDeriveReplacementCommand and ConflateCmd.
+  // TODO: We could use VersionUtils::checkVersionLessThanOneCountAndLogWarning() somewhere in here
+  // like we do with ChangesetDeriveReplacementCommand and ConflateCmd.
 
   // If we have two inputs, we'll determine the difference between them as the changeset.
   // Otherwise, we're passing all the input data through to the output changeset, so put it in
@@ -247,17 +251,18 @@ void ChangesetCreator::create(const QList<OsmMapPtr>& map1Inputs,
     OsmMapWriterFactory::writeDebugMap(map1, "map1-before-changeset-derivation-" + map1->getName());
     OsmMapWriterFactory::writeDebugMap(map2, "map2-before-changeset-derivation-" + map2->getName());
 
-    // don't want to include review relations - may need to remove this depending on what happens
-    // with #3361
-    std::shared_ptr<TagKeyCriterion> elementCriterion(
-      new TagKeyCriterion(MetadataTags::HootReviewNeeds()));
-    RemoveElementsVisitor removeElementsVisitor;
-    removeElementsVisitor.setRecursive(false);
-    removeElementsVisitor.addCriterion(elementCriterion);
-    map1->visitRw(removeElementsVisitor);
-    map2->visitRw(removeElementsVisitor);
+    if (!_includeReviews)
+    {
+      std::shared_ptr<TagKeyCriterion> elementCriterion(
+        new TagKeyCriterion(MetadataTags::HootReviewNeeds()));
+      RemoveElementsVisitor removeElementsVisitor;
+      removeElementsVisitor.setRecursive(false);
+      removeElementsVisitor.addCriterion(elementCriterion);
+      map1->visitRw(removeElementsVisitor);
+      map2->visitRw(removeElementsVisitor);
+    }
 
-    // Truncate tags over 255 characters to push into OSM API.
+    // Truncate tags over max tag length characters to push into OSM API.
     ApiTagTruncateVisitor truncateTags;
     map1->visitRw(truncateTags);
     map2->visitRw(truncateTags);
@@ -366,7 +371,7 @@ void ChangesetCreator::_handleUnstreamableConvertOpsInMemory(const QString& inpu
           QString("(OsmMapOperation) on two data sources with overlapping element IDs: ") +
           e.what());
       }
-      //  Rethrow the original exception
+      //  rethrow the original exception
       throw;
     }
   }
@@ -521,24 +526,26 @@ void ChangesetCreator::_readInputsFully(const QString& input1, const QString& in
     _currentTaskNum++;
   }
 
-  // We don't want to include review relations.
-  progress.set(
-    (float)(_currentTaskNum - 1) / (float)_numTotalTasks, "Removing review relations...");
-  std::shared_ptr<TagKeyCriterion> elementCriterion(
-    new TagKeyCriterion(MetadataTags::HootReviewNeeds()));
-  RemoveElementsVisitor removeElementsVisitor;
-  removeElementsVisitor.setRecursive(false);
-  removeElementsVisitor.addCriterion(elementCriterion);
-  map1->visitRw(removeElementsVisitor);
-  if (!_singleInput)
+  if (!_includeReviews)
   {
-    map2->visitRw(removeElementsVisitor);
+    progress.set(
+      (float)(_currentTaskNum - 1) / (float)_numTotalTasks, "Removing review relations...");
+    std::shared_ptr<TagKeyCriterion> elementCriterion(
+      new TagKeyCriterion(MetadataTags::HootReviewNeeds()));
+    RemoveElementsVisitor removeElementsVisitor;
+    removeElementsVisitor.setRecursive(false);
+    removeElementsVisitor.addCriterion(elementCriterion);
+    map1->visitRw(removeElementsVisitor);
+    if (!_singleInput)
+    {
+      map2->visitRw(removeElementsVisitor);
+    }
+    OsmMapWriterFactory::writeDebugMap(map1, "after-remove-reviews-map-1");
+    OsmMapWriterFactory::writeDebugMap(map2, "after-remove-reviews-map-2");
+    _currentTaskNum++;
   }
-  OsmMapWriterFactory::writeDebugMap(map1, "after-remove-reviews-map-1");
-  OsmMapWriterFactory::writeDebugMap(map2, "after-remove-reviews-map-2");
-  _currentTaskNum++;
 
-  // Truncate tags over 255 characters to push into OSM API.
+  // Truncate tags over max tag length characters to push into OSM API.
   progress.set(
     (float)(_currentTaskNum - 1) / (float)_numTotalTasks, "Preparing tags for changeset...");
   ApiTagTruncateVisitor truncateTags;
@@ -592,12 +599,15 @@ ElementInputStreamPtr ChangesetCreator::_getFilteredInputStream(const QString& i
   LOG_DEBUG("Retrieving filtered input stream for: " << input.right(25) << "...");
 
   QList<ElementVisitorPtr> visitors;
-  // We don't want to include review relations.
-  std::shared_ptr<ElementCriterion> elementCriterion(
-    new NotCriterion(
-      std::shared_ptr<TagKeyCriterion>(
-        new TagKeyCriterion(MetadataTags::HootReviewNeeds()))));
-  // Tags need to be truncated if they are over 255 characters.
+  std::shared_ptr<ElementCriterion> elementCriterion;
+  if (!_includeReviews)
+  {
+    elementCriterion.reset(
+      new NotCriterion(
+        std::shared_ptr<TagKeyCriterion>(
+          new TagKeyCriterion(MetadataTags::HootReviewNeeds()))));
+  }
+  // Tags need to be truncated if they are over max tag length characters.
   visitors.append(std::shared_ptr<ApiTagTruncateVisitor>(new ApiTagTruncateVisitor()));
 
   // open a stream to the input data
@@ -607,8 +617,16 @@ ElementInputStreamPtr ChangesetCreator::_getFilteredInputStream(const QString& i
   reader->setUseDataSourceIds(true);
   reader->open(input);
   ElementInputStreamPtr inputStream = std::dynamic_pointer_cast<ElementInputStream>(reader);
-  ElementInputStreamPtr filteredInputStream(
-    new ElementCriterionVisitorInputStream(inputStream, elementCriterion, visitors));
+  ElementInputStreamPtr filteredInputStream;
+  if (elementCriterion)
+  {
+    filteredInputStream.reset(
+      new ElementCriterionVisitorInputStream(inputStream, elementCriterion, visitors));
+  }
+  else
+  {
+    filteredInputStream.reset(new ElementVisitorInputStream(inputStream, visitors.at(0)));
+  }
 
   // Add convert ops supporting streaming into the pipeline, if there are any. TODO: Any
   // OsmMapOperations in the bunch need to operate on the entire map made up of both inputs to
@@ -661,7 +679,19 @@ void ChangesetCreator::_streamChangesetOutput(const QList<ElementInputStreamPtr>
   {
     if (output.endsWith(".osc")) // detailed stats currently only implemented for xml output
     {
-      detailedStats = writer->getStatsTable();
+      // Get the stats output format from the file extension, or if no extension is there assume a
+      // text table output to the display.
+      ChangesetStatsFormat statsFormat;
+      if (!_statsOutputFile.isEmpty())
+      {
+        QFileInfo statsFileInfo(_statsOutputFile);
+        statsFormat.setFormat(ChangesetStatsFormat::fromString(statsFileInfo.completeSuffix()));
+      }
+      else
+      {
+        statsFormat.setFormat(ChangesetStatsFormat::Text);
+      }
+      detailedStats = writer->getStatsTable(statsFormat);
     }
     else
     {
@@ -715,7 +745,14 @@ void ChangesetCreator::_streamChangesetOutput(const QList<ElementInputStreamPtr>
   LOG_VART(_printDetailedStats);
   if (_printDetailedStats && !detailedStats.isEmpty())
   {
-    LOG_STATUS("Changeset Stats:\n" << detailedStats);
+    if (_statsOutputFile.isEmpty())
+    {
+      LOG_STATUS("Changeset Stats:\n" << detailedStats);
+    }
+    else
+    {
+      FileUtils::writeFully(_statsOutputFile, detailedStats);
+    }
   }
   else
   {
Clone this wiki locally