Skip to content

v0.2.54..v0.2.55 changeset SuperfluousConflateOpRemover.cpp

Garret Voltz edited this page Aug 14, 2020 · 1 revision
diff --git a/hoot-core/src/main/cpp/hoot/core/conflate/SuperfluousConflateOpRemover.cpp b/hoot-core/src/main/cpp/hoot/core/conflate/SuperfluousConflateOpRemover.cpp
new file mode 100644
index 0000000..b77c322
--- /dev/null
+++ b/hoot-core/src/main/cpp/hoot/core/conflate/SuperfluousConflateOpRemover.cpp
@@ -0,0 +1,290 @@
+/*
+ * This file is part of Hootenanny.
+ *
+ * Hootenanny is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --------------------------------------------------------------------
+ *
+ * The following copyright notices are generated automatically. If you
+ * have a new notice to add, please use the format:
+ * " * @copyright Copyright ..."
+ * This will properly maintain the copyright information. DigitalGlobe
+ * copyrights will be updated automatically.
+ *
+ * @copyright Copyright (C) 2020 DigitalGlobe (http://www.digitalglobe.com/)
+ */
+#include "SuperfluousConflateOpRemover.h"
+
+// hoot
+#include <hoot/core/util/ConfigOptions.h>
+#include <hoot/core/util/Log.h>
+#include <hoot/core/criterion/PointCriterion.h>
+#include <hoot/core/criterion/LinearCriterion.h>
+#include <hoot/core/criterion/PolygonCriterion.h>
+#include <hoot/core/conflate/matching/MatchFactory.h>
+#include <hoot/core/util/Factory.h>
+#include <hoot/core/elements/ElementVisitor.h>
+#include <hoot/core/ops/MapCleaner.h>
+
+namespace hoot
+{
+
+QSet<QString> SuperfluousConflateOpRemover::_geometryTypeClassNameCache;
+
+void SuperfluousConflateOpRemover::removeSuperfluousOps()
+{
+  // get all crits involved in the current matcher configuration
+  const QSet<QString> matcherCrits = _getMatchCreatorGeometryTypeCrits();
+
+  QSet<QString> removedOps;
+
+  // for each of the conflate pre/post and map cleaner transforms (if conflate pre/post specifies
+  // MapCleaner) filter out any that aren't associated with the same ElementCriterion as the ones
+  // associated with the matchers
+
+  const QStringList modifiedPreConflateOps =
+    _filterOutUnneededOps(
+      matcherCrits, ConfigOptions().getConflatePreOps(), removedOps);
+  if (modifiedPreConflateOps.size() != ConfigOptions().getConflatePreOps().size())
+  {
+    conf().set(ConfigOptions::getConflatePreOpsKey(), modifiedPreConflateOps);
+  }
+
+  const QStringList modifiedPostConflateOps =
+    _filterOutUnneededOps(
+      matcherCrits, ConfigOptions().getConflatePostOps(), removedOps);
+  if (modifiedPostConflateOps.size() != ConfigOptions().getConflatePostOps().size())
+  {
+    conf().set(ConfigOptions::getConflatePostOpsKey(), modifiedPostConflateOps);
+  }
+
+  const QString mapCleanerName = QString::fromStdString(MapCleaner::className());
+  if (modifiedPreConflateOps.contains(mapCleanerName) ||
+      modifiedPostConflateOps.contains(mapCleanerName))
+  {
+    const QStringList modifiedCleaningOps =
+      _filterOutUnneededOps(
+        matcherCrits, ConfigOptions().getMapCleanerTransforms(), removedOps);
+    if (modifiedCleaningOps.size() != ConfigOptions().getMapCleanerTransforms().size())
+    {
+      conf().set(ConfigOptions::getMapCleanerTransformsKey(), modifiedCleaningOps);
+    }
+  }
+
+  if (removedOps.size() > 0)
+  {
+    QStringList removedOpsList = removedOps.values();
+    qSort(removedOpsList);
+    LOG_INFO(
+      "Removed the following conflate pre/post operations with no relevance to the selected " <<
+      "matchers: " << removedOpsList.join(", "));
+  }
+}
+
+QStringList SuperfluousConflateOpRemover::_filterOutUnneededOps(
+  const QSet<QString>& geometryTypeCrits, const QStringList& ops, QSet<QString>& removedOps)
+{
+  LOG_TRACE("ops before: " << ops);
+
+  QStringList modifiedOps;
+
+  for (int i = 0; i < ops.size(); i++)
+  {
+    const QString opName = ops.at(i);
+    LOG_VART(opName);
+
+    // MapCleaner's ops are configured with map.cleaner.transforms, so don't exclude it here.
+    if (opName == QString::fromStdString(MapCleaner::className()))
+    {
+      modifiedOps.append(opName);
+      continue;
+    }
+
+    // All the ops should be map ops or element vis and, thus, support
+    // FilteredByGeometryTypeCriteria, but we'll check anyway to be safe.
+    std::shared_ptr<FilteredByGeometryTypeCriteria> op;
+    if (Factory::getInstance().hasBase<OsmMapOperation>(opName.toStdString()))
+    {
+      op =
+        std::dynamic_pointer_cast<FilteredByGeometryTypeCriteria>(
+          std::shared_ptr<OsmMapOperation>(
+            Factory::getInstance().constructObject<OsmMapOperation>(opName)));
+    }
+    else if (Factory::getInstance().hasBase<ElementVisitor>(opName.toStdString()))
+    {
+      op =
+        std::dynamic_pointer_cast<FilteredByGeometryTypeCriteria>(
+          std::shared_ptr<ElementVisitor>(
+            Factory::getInstance().constructObject<ElementVisitor>(opName)));
+    }
+
+    if (op)
+    {
+      // get all the class names of the crits that this op is associated with
+      const QStringList opCrits = op->getCriteria();
+      LOG_VART(opCrits);
+
+      // If the op is not associated with any crit, we assume it should never be disabled based on
+      // the feature type being conflated.
+      if (opCrits.isEmpty())
+      {
+        modifiedOps.append(opName);
+        continue;
+      }
+
+      // If any of the op's crits match with those in the matchers' list, we'll use it. Otherwise,
+      // we disable it.
+      bool opAdded = false;
+      for (int j = 0; j < opCrits.size(); j++)
+      {
+        const QString opCrit = opCrits.at(j);
+
+        if (!_isGeometryTypeCrit(opCrit))
+        {
+          throw HootException(
+            "FilteredByGeometryTypeCriteria::getCriteria implementation in " + opName +
+            " returned a non-GeometryTypeCriterion class: " + opCrit);
+        }
+
+        if (!_geometryTypeClassNameCache.contains(opCrit))
+        {
+          _geometryTypeClassNameCache.insert(opCrit);
+        }
+
+        if (geometryTypeCrits.contains(opCrit))
+        {
+          modifiedOps.append(opName);
+          opAdded = true;
+          break;
+        }
+      }
+      if (!opAdded)
+      {
+        removedOps.insert(opName);
+      }
+    }
+    else
+    {
+      removedOps.insert(opName);
+    }
+  }
+
+  LOG_TRACE("ops after: " << modifiedOps);
+  LOG_VART(removedOps);
+  return modifiedOps;
+}
+
+QSet<QString> SuperfluousConflateOpRemover::_getMatchCreatorGeometryTypeCrits()
+{
+  QSet<QString> matcherCrits;
+
+  // get all of the matchers from our current config
+  std::vector<std::shared_ptr<MatchCreator>> matchCreators =
+      MatchFactory::getInstance().getCreators();
+  for (std::vector<std::shared_ptr<MatchCreator>>::const_iterator it = matchCreators.begin();
+       it != matchCreators.end(); ++it)
+  {
+    std::shared_ptr<MatchCreator> matchCreator = *it;
+    std::shared_ptr<FilteredByGeometryTypeCriteria> critFilter =
+      std::dynamic_pointer_cast<FilteredByGeometryTypeCriteria>(matchCreator);
+    const QStringList crits = critFilter->getCriteria();
+
+    // Technically not sure we'd have to error out here, but it will be good to know if any
+    // matchers weren't configured with crits to keep conflate bugs from sneaking in over time.
+    if (crits.size() == 0)
+    {
+      throw HootException(
+        "Match creator: " + matchCreator->getName() +
+        " does not specify any associated feature type criteria.");
+    }
+
+    for (int i = 0; i < crits.size(); i++)
+    {
+      const QString critStr = crits.at(i);
+      LOG_VART(critStr);
+
+      // doublecheck this is a valid crit
+      if (!_isGeometryTypeCrit(critStr))
+      {
+        throw HootException(
+          "FilteredByGeometryTypeCriteria::getCriteria implementation in " +
+          matchCreator->getName() + " returned a non-GeometryTypeCriterion class: " + critStr);
+      }
+
+      if (!_geometryTypeClassNameCache.contains(critStr))
+      {
+        _geometryTypeClassNameCache.insert(critStr);
+      }
+
+      // add the crit
+      matcherCrits.insert(critStr);
+
+      // also add any generic geometry crits the crit inherits from
+
+      const QStringList pointCrits =
+        GeometryTypeCriterion::getCriterionClassNamesByGeometryType(
+          GeometryTypeCriterion::GeometryType::Point);
+      LOG_VART(pointCrits);
+      if (pointCrits.contains(critStr))
+      {
+        matcherCrits.insert(QString::fromStdString(PointCriterion::className()));
+      }
+
+      const QStringList lineCrits =
+        GeometryTypeCriterion::getCriterionClassNamesByGeometryType(
+          GeometryTypeCriterion::GeometryType::Line);
+      LOG_VART(lineCrits);
+      if (lineCrits.contains(critStr))
+      {
+        matcherCrits.insert(QString::fromStdString(LinearCriterion::className()));
+      }
+
+      const QStringList polyCrits =
+        GeometryTypeCriterion::getCriterionClassNamesByGeometryType(
+          GeometryTypeCriterion::GeometryType::Polygon);
+      LOG_VART(polyCrits);
+      if (polyCrits.contains(critStr))
+      {
+        matcherCrits.insert(QString::fromStdString(PolygonCriterion::className()));
+      }
+    }
+  }
+
+  LOG_VART(matcherCrits);
+  return matcherCrits;
+}
+
+bool SuperfluousConflateOpRemover::_isGeometryTypeCrit(const QString& className)
+{
+  if (_geometryTypeClassNameCache.contains(className))
+  {
+    return true;
+  }
+
+  // can't use hasBase with GeometryTypeCriterion here, since GeometryTypeCriterion are registered
+  // as ElementCriterion
+  std::shared_ptr<GeometryTypeCriterion> crit;
+  if (Factory::getInstance().hasBase<ElementCriterion>(className.toStdString()))
+  {
+    crit =
+      std::dynamic_pointer_cast<GeometryTypeCriterion>(
+        std::shared_ptr<ElementCriterion>(
+          Factory::getInstance().constructObject<ElementCriterion>(className.toStdString())));
+    return crit.get();
+  }
+
+  return false;
+}
+
+}
Clone this wiki locally