Skip to content

v0.2.48..v0.2.49 changeset ChangesetCreator.cpp

Garret Voltz edited this page Oct 2, 2019 · 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 aecc0d4..7d374da 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
@@ -50,6 +50,7 @@
 #include <hoot/core/visitors/RemoveElementsVisitor.h>
 #include <hoot/core/visitors/RemoveUnknownVisitor.h>
 #include <hoot/core/util/ConfigUtils.h>
+#include <hoot/core/algorithms/changeset/ChangesetDeriver.h>
 
 //GEOS
 #include <geos/geom/Envelope.h>
@@ -64,7 +65,10 @@ _osmApiDbUrl(osmApiDbUrl),
 _numTotalTasks(0),
 _currentTaskNum(0),
 _printStats(printStats),
-_singleInput(false)
+_singleInput(false),
+_numCreateChanges(0),
+_numModifyChanges(0),
+_numDeleteChanges(0)
 {
 }
 
@@ -128,6 +132,9 @@ 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.
+
   // 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
   // the sortedElements2 newer data and leave the first one empty. The result will be a changeset
@@ -195,51 +202,77 @@ void ChangesetCreator::create(const QString& output, const QString& input1, cons
 
 void ChangesetCreator::create(OsmMapPtr& map1, OsmMapPtr& map2, const QString& output)
 {
-  LOG_DEBUG(
-    "Creating changeset from inputs: " << map1->getName() << " and " << map2->getName() <<
-    " to output: " << output << "...");
-  OsmMapWriterFactory::writeDebugMap(map1, "map1-before-changeset-derivation");
-  OsmMapWriterFactory::writeDebugMap(map2, "map2-before-changeset-derivation");
+  QList<OsmMapPtr> map1Inputs;
+  map1Inputs.append(map1);
+  QList<OsmMapPtr> map2Inputs;
+  map2Inputs.append(map2);
+  create(map1Inputs, map2Inputs, output);
+}
 
-  // 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);
+void ChangesetCreator::create(const QList<OsmMapPtr>& map1Inputs,
+                              const QList<OsmMapPtr>& map2Inputs, const QString& output)
+{
+  if (map1Inputs.size() != map2Inputs.size())
+  {
+    throw IllegalArgumentException("Changeset input data inputs are not the same size.");
+  }
 
-  // Truncate tags over 255 characters to push into OSM API.
-  ApiTagTruncateVisitor truncateTags;
-  map1->visitRw(truncateTags);
-  map2->visitRw(truncateTags);
+  QList<ElementInputStreamPtr> sortedInputs1;
+  QList<ElementInputStreamPtr> sortedInputs2;
+  for (int i = 0; i < map1Inputs.size(); i++)
+  {
+    OsmMapPtr map1 = map1Inputs.at(i);
+    OsmMapPtr map2 = map2Inputs.at(i);
+    if (map2->isEmpty())
+    {
+      // An empty map2 makes no sense, b/c you would just have an empty changeset.
+      LOG_INFO(
+        "Second map is empty. Skipping changeset generation for maps at index: " << i + 1 << "...");
+      continue;
+    }
+    LOG_DEBUG(
+      "Creating changeset from inputs: " << map1->getName() << " and " << map2->getName() <<
+      " to output: " << output << "...");
+    OsmMapWriterFactory::writeDebugMap(map1, "map1-before-changeset-derivation");
+    OsmMapWriterFactory::writeDebugMap(map2, "map2-before-changeset-derivation");
+
+    // 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);
 
-  LOG_VARD(MapProjector::toWkt(map1->getProjection()));
-  LOG_VARD(MapProjector::toWkt(map2->getProjection()));
+    // Truncate tags over 255 characters to push into OSM API.
+    ApiTagTruncateVisitor truncateTags;
+    map1->visitRw(truncateTags);
+    map2->visitRw(truncateTags);
 
-  //sortedElements1 is the former state of the data
-  ElementInputStreamPtr sortedElements1;
-  //sortedElements2 is the newer state of the data
-  ElementInputStreamPtr sortedElements2;
+    LOG_VART(MapProjector::toWkt(map1->getProjection()));
+    LOG_VART(MapProjector::toWkt(map2->getProjection()));
 
-  // no need to implement application of ops for this logic path
+    // no need to implement application of ops for this logic path
 
-  if (map2)
-  {
-    sortedElements1 = _sortElementsInMemory(map1);
-    sortedElements2 = _sortElementsInMemory(map2);
-  }
-  else
-  {
-    sortedElements1 = _getEmptyInputStream();
-    sortedElements2 = _sortElementsInMemory(map1);
+    // sortedInputs1 is the former state of the data
+    if (map1->isEmpty())
+    {
+      // An empty former map, means the changeset will be made up of entirely statements based on
+      // map2.
+      sortedInputs1.append(_getEmptyInputStream());
+    }
+    else
+    {
+      sortedInputs1.append(_sortElementsInMemory(map1));
+    }
+    // sortedInputs2 is the newer state of the data
+    sortedInputs2.append(_sortElementsInMemory(map2));
   }
 
   // write out the changeset file
-  assert(sortedElements1.get() && sortedElements2.get());;
-  _streamChangesetOutput(sortedElements1, sortedElements2, output);
+  _streamChangesetOutput(sortedInputs1, sortedInputs2, output);
 }
 
 bool ChangesetCreator::_isSupportedOutputFormat(const QString& format) const
@@ -584,59 +617,103 @@ ElementInputStreamPtr ChangesetCreator::_sortElementsExternally(const QString& i
   return sorted;
 }
 
-void ChangesetCreator::_streamChangesetOutput(ElementInputStreamPtr input1,
-                                              ElementInputStreamPtr input2, const QString& output)
+void ChangesetCreator::_streamChangesetOutput(const QList<ElementInputStreamPtr>& inputs1,
+                                              const QList<ElementInputStreamPtr>& inputs2,
+                                              const QString& output)
 {
+  if (inputs1.size() != inputs2.size())
+  {
+    throw IllegalArgumentException(
+      "Changeset input data inputs are not the same size for streaming to output.");
+  }
+
   LOG_INFO("Streaming changeset output to " << output.right(25) << "...")
 
   QString stats;
-  LOG_VARD(output);
+  _numCreateChanges = 0;
+  _numModifyChanges = 0;
+  _numDeleteChanges = 0;
 
   // Could this eventually be cleaned up to use OsmChangeWriterFactory and the OsmChange interface
   // instead?
-  _changesetDeriver.reset(new ChangesetDeriver(input1, input2));
+
+  QList<ChangesetProviderPtr> changesetProviders;
+  for (int i = 0; i < inputs1.size(); i++)
+  {
+    changesetProviders.append(
+      ChangesetDeriverPtr(new ChangesetDeriver(inputs1.at(i), inputs2.at(i))));
+  }
+
   if (output.endsWith(".osc"))
   {
     OsmXmlChangesetFileWriter writer;
-    writer.write(output, _changesetDeriver);
+    writer.write(output, changesetProviders);
     stats = writer.getStatsTable();
   }
   else if (output.endsWith(".osc.sql"))
   {
     assert(!_osmApiDbUrl.isEmpty());
-    OsmApiDbSqlChangesetFileWriter(QUrl(_osmApiDbUrl)).write(output, _changesetDeriver);
+    OsmApiDbSqlChangesetFileWriter(QUrl(_osmApiDbUrl)).write(output, changesetProviders);
   }
 
-  LOG_VARD(_changesetDeriver->getNumCreateChanges());
-  LOG_VARD(_changesetDeriver->getNumModifyChanges());
-  LOG_VARD(_changesetDeriver->getNumDeleteChanges());
-  LOG_VARD(_changesetDeriver->getNumFromElementsParsed());
-  LOG_VARD(_changesetDeriver->getNumToElementsParsed());
-  if (_changesetDeriver->getNumChanges() == 0)
+  assert(inputs1.size() == changesetProviders.size());
+  for (int i = 0; i < inputs1.size(); i++)
   {
-    LOG_WARN("No changes written to changeset.");
+    ChangesetDeriverPtr changesetDeriver =
+      std::dynamic_pointer_cast<ChangesetDeriver>(changesetProviders.at(i));
+    LOG_DEBUG("Derived changeset: " << i + 1 << " / " << inputs1.size() << ": ");
+    LOG_VARD(changesetDeriver->getNumCreateChanges());
+    _numCreateChanges += changesetDeriver->getNumCreateChanges();
+    LOG_VARD(changesetDeriver->getNumModifyChanges());
+    _numModifyChanges += changesetDeriver->getNumModifyChanges();
+    LOG_VARD(changesetDeriver->getNumDeleteChanges());
+    _numDeleteChanges += changesetDeriver->getNumDeleteChanges();
+    LOG_VARD(changesetDeriver->getNumFromElementsParsed());
+    LOG_VARD(changesetDeriver->getNumToElementsParsed());
+    if (changesetDeriver->getNumChanges() == 0)
+    {
+      LOG_WARN("No changes written to changeset.");
+    }
+
+    // close the output stream
+    ElementInputStreamPtr input1 = inputs1.at(i);
+    std::shared_ptr<PartialOsmMapReader> partialReader1 =
+      std::dynamic_pointer_cast<PartialOsmMapReader>(input1);
+    if (partialReader1)
+    {
+      partialReader1->finalizePartial();
+    }
+    input1->close();
+    ElementInputStreamPtr input2 = inputs2.at(i);
+    std::shared_ptr<PartialOsmMapReader> partialReader2 =
+      std::dynamic_pointer_cast<PartialOsmMapReader>(input2);
+    if (partialReader2)
+    {
+      partialReader2->finalizePartial();
+    }
+    input2->close();
   }
 
-  // close the output stream
-  std::shared_ptr<PartialOsmMapReader> partialReader1 =
-    std::dynamic_pointer_cast<PartialOsmMapReader>(input1);
-  if (partialReader1)
+  if (_printStats)
   {
-    partialReader1->finalizePartial();
+    LOG_STATUS("Changeset Stats:\n" << stats);
   }
-  input1->close();
-  std::shared_ptr<PartialOsmMapReader> partialReader2 =
-    std::dynamic_pointer_cast<PartialOsmMapReader>(input2);
-  if (partialReader2)
+  else
   {
-    partialReader2->finalizePartial();
+    LOG_VARD(_numCreateChanges);
+    LOG_VARD(_numModifyChanges);
+    LOG_VARD(_numDeleteChanges);
   }
-  input2->close();
+}
 
-  if (_printStats)
-  {
-    LOG_INFO("Changeset Stats:\n" << stats);
-  }
+void ChangesetCreator::_streamChangesetOutput(ElementInputStreamPtr input1,
+                                              ElementInputStreamPtr input2, const QString& output)
+{
+  QList<ElementInputStreamPtr> input1List;
+  input1List.append(input1);
+  QList<ElementInputStreamPtr> input2List;
+  input2List.append(input2);
+  _streamChangesetOutput(input1List, input2List, output);
 }
 
 }
Clone this wiki locally