Skip to content

v0.2.47..v0.2.48 changeset UnconnectedWaySnapper.cpp

Garret Voltz edited this page Sep 27, 2019 · 1 revision
diff --git a/hoot-core/src/main/cpp/hoot/core/ops/UnconnectedWaySnapper.cpp b/hoot-core/src/main/cpp/hoot/core/ops/UnconnectedWaySnapper.cpp
index 2a3f585..9e84da9 100644
--- a/hoot-core/src/main/cpp/hoot/core/ops/UnconnectedWaySnapper.cpp
+++ b/hoot-core/src/main/cpp/hoot/core/ops/UnconnectedWaySnapper.cpp
@@ -42,6 +42,8 @@
 #include <hoot/core/util/MapProjector.h>
 #include <hoot/core/util/StringUtils.h>
 #include <hoot/core/visitors/IndexElementsVisitor.h>
+#include <hoot/core/io/OsmMapWriterFactory.h>
+#include <hoot/core/criterion/OrCriterion.h>
 
 // tgs
 #include <tgs/RStarTree/MemoryPageStore.h>
@@ -66,6 +68,7 @@ _maxSnapDistance(5.0),
 _snapToWayDiscretizationSpacing(1.0),
 _addCeToSearchDistance(false),
 _markSnappedNodes(false),
+_markSnappedWays(false),
 _wayToSnapToCriterionClassName("hoot::WayCriterion"),
 _wayToSnapCriterionClassName("hoot::WayCriterion"),
 _wayNodeToSnapToCriterionClassName("hoot::WayNodeCriterion"),
@@ -97,9 +100,10 @@ void UnconnectedWaySnapper::setConfiguration(const Settings& conf)
   setAddCeToSearchDistance(confOpts.getSnapUnconnectedWaysAddCircularErrorToSearchRadius());
   setWayDiscretizationSpacing(confOpts.getSnapUnconnectedWaysDiscretizationSpacing());
   setMarkSnappedNodes(confOpts.getSnapUnconnectedWaysMarkSnappedNodes());
+  setMarkSnappedWays(confOpts.getSnapUnconnectedWaysMarkSnappedWays());
 
-  setSnapWayStatus(Status::fromString(confOpts.getSnapUnconnectedWaysSnapWayStatus()));
-  setSnapToWayStatus(Status::fromString(confOpts.getSnapUnconnectedWaysSnapToWayStatus()));
+  setSnapWayStatus(confOpts.getSnapUnconnectedWaysSnapWayStatus().trimmed());
+  setSnapToWayStatus(confOpts.getSnapUnconnectedWaysSnapToWayStatus().trimmed());
 
   setWayToSnapCriterionClassName(confOpts.getSnapUnconnectedWaysSnapWayCriterion().trimmed());
   setWayToSnapToCriterionClassName(confOpts.getSnapUnconnectedWaysSnapToWayCriterion().trimmed());
@@ -122,6 +126,7 @@ void UnconnectedWaySnapper::setMaxNodeReuseDistance(double distance)
   }
   _maxNodeReuseDistance = distance;
 }
+
 void UnconnectedWaySnapper::setMaxSnapDistance(double distance)
 {
   if (distance <= 0.0)
@@ -132,6 +137,7 @@ void UnconnectedWaySnapper::setMaxSnapDistance(double distance)
   }
   _maxSnapDistance = distance;
 }
+
 void UnconnectedWaySnapper::setWayDiscretizationSpacing(double spacing)
 {
   if (spacing <= 0.0)
@@ -158,6 +164,7 @@ void UnconnectedWaySnapper::setWayToSnapToCriterionClassName(const QString& name
     _wayToSnapToCriterionClassName = name.trimmed();
   }
 }
+
 void UnconnectedWaySnapper::setWayToSnapCriterionClassName(const QString& name)
 {
   if (name.trimmed().isEmpty())
@@ -173,6 +180,7 @@ void UnconnectedWaySnapper::setWayToSnapCriterionClassName(const QString& name)
     _wayToSnapCriterionClassName = name.trimmed();
   }
 }
+
 void UnconnectedWaySnapper::setWayNodeToSnapToCriterionClassName(const QString& name)
 {
   if (name.trimmed().isEmpty())
@@ -197,6 +205,8 @@ void UnconnectedWaySnapper::apply(OsmMapPtr& map)
   _numSnappedToWayNodes = 0;
   _snappedWayNodeIds.clear();
 
+  OsmMapWriterFactory::writeDebugMap(map, "UnconnectedWaySnapper-before-snapping");
+
   // need to be in planar for all of this
   MapProjector::projectToPlanar(_map);
 
@@ -212,8 +222,11 @@ void UnconnectedWaySnapper::apply(OsmMapPtr& map)
     wayNodeToSnapToCrit =
       _createFeatureCriterion(_wayNodeToSnapToCriterionClassName, _snapToWayStatus);
   }
+  // The status of what contains an unconnected node doesn't matter, so we don't filter by status.
+  ElementCriterionPtr unnconnectedWayNodeCrit =
+    _createFeatureCriterion(_wayToSnapCriterionClassName, "");
 
-  // create needed geospatial indexes for surrounding feature searches; if the way node reuse option
+  // create needed geospatial indexes for surrounding feature searches; If the way node reuse option
   // was turned off, then no need to index way nodes.
   if (wayNodeToSnapToCrit)
   {
@@ -223,45 +236,98 @@ void UnconnectedWaySnapper::apply(OsmMapPtr& map)
   _createFeatureIndex(
     wayToSnapToCrit, _snapToWayIndex, _snapToWayIndexToEid, ElementType::Way);
 
-  const WayMap ways = _map->getWays();
+  WayMap ways = _map->getWays();
   LOG_VART(ways.size());
   long waysProcessed = 0;
-  for (WayMap::const_iterator wayItr = ways.begin(); wayItr != ways.end(); ++wayItr)
+  for (WayMap::iterator wayItr = ways.begin(); wayItr != ways.end(); ++wayItr)
   {
-    const ConstWayPtr& wayToSnap = wayItr->second;
+    WayPtr wayToSnap = wayItr->second;
     LOG_VART(wayToSnap->getElementId());
-
-    // find unconnected endpoints on the way, if the way satisfies the specified crit
-    const std::set<long> unconnectedEndNodeIds =
-      _getUnconnectedEndNodeIds(wayToSnap, wayToSnapCrit);
-    for (std::set<long>::const_iterator unconnectedEndNodeIdItr = unconnectedEndNodeIds.begin();
-         unconnectedEndNodeIdItr != unconnectedEndNodeIds.end(); ++unconnectedEndNodeIdItr)
+    // ensure the way has the status we require for snapping
+    if (wayToSnapCrit->isSatisfied(wayToSnap))
     {
-      // for each unconnected endpoint
-      const long unconnectedEndNodeId = *unconnectedEndNodeIdItr;
-      // don't snap the same node more than once
-      if (!_snappedWayNodeIds.contains(unconnectedEndNodeId))
+      // find unconnected endpoints on the way, if the way satisfies the specified crit
+      const std::set<long> unconnectedEndNodeIds =
+        _getUnconnectedEndNodeIds(wayToSnap, unnconnectedWayNodeCrit);
+      for (std::set<long>::const_iterator unconnectedEndNodeIdItr = unconnectedEndNodeIds.begin();
+           unconnectedEndNodeIdItr != unconnectedEndNodeIds.end(); ++unconnectedEndNodeIdItr)
       {
-        NodePtr unconnectedEndNode = _map->getNode(unconnectedEndNodeId);
-
-        // Try to find the nearest way node satisifying the specifying way node criteria that
-        // is within the max reuse distance (if one was specified) and snap the unconnected way
-        // node.  This reuse of existing way nodes on way being snapped to is done in order to cut
-        // back on the number of new way nodes being added.
         bool snapOccurred = false;
-        if (_snapToExistingWayNodes)
+
+        // for each unconnected endpoint
+        const long unconnectedEndNodeId = *unconnectedEndNodeIdItr;
+        // don't snap the same node more than once
+        if (!_snappedWayNodeIds.contains(unconnectedEndNodeId))
         {
-          snapOccurred = _snapUnconnectedNodeToWayNode(unconnectedEndNode);
+          NodePtr unconnectedEndNode = _map->getNode(unconnectedEndNodeId);
+
+          // Try to find the nearest way node satisfying the specifying way node criteria that
+          // is within the max reuse distance (if one was specified) and snap the unconnected way
+          // node.  This reuse of existing way nodes on way being snapped to is done in order to cut
+          // back on the number of new way nodes being added.
+          if (_snapToExistingWayNodes)
+          {
+            snapOccurred = _snapUnconnectedNodeToWayNode(unconnectedEndNode);
+          }
+
+          if (!snapOccurred)
+          {
+            // If we weren't able to snap to a nearby way node or the snapping directly to way nodes
+            // option was turned off, we're going to try to find a nearby way and snap onto the
+            // closest location on it.
+            snapOccurred = _snapUnconnectedNodeToWay(unconnectedEndNode);
+          }
+
+          if (snapOccurred)
+          {
+            assert(_snappedToWay);
+            LOG_TRACE(
+              "Snapped " << wayToSnap->getElementId() << " to " << _snappedToWay->getElementId());
+
+            // retain the parent id of the snapped to way; See related notes in WayJoinerAdvanced
+            const long pid = _getPid(_snappedToWay);
+            if (pid != WayData::PID_EMPTY)
+            {
+              wayToSnap->setPid(pid);
+              wayToSnap->getTags()[MetadataTags::HootSplitParentId()] = QString::number(pid);
+              LOG_TRACE(
+                "Set pid: " <<  pid << " of snapped to way: " << _snappedToWay->getElementId() <<
+                " on snapped way: " << wayToSnap->getElementId());
+            }
+
+            // retain the status of the snapped to way
+            wayToSnap->setStatus(_snappedToWay->getStatus());
+            LOG_TRACE(
+              "Set status: " <<  _snappedToWay->getStatus() << " of snapped to way: " <<
+              _snappedToWay->getElementId() << " on snapped way: " << wayToSnap->getElementId());
+
+            LOG_VART(wayToSnap);
+            LOG_VART(_snappedToWay);
+
+            // It seems like the geospatial indices should have to be updated after every snap, but
+            // haven't any direct evidence of that being necessary for proper snapping yet. If it
+            // is needed, that would likely slow things down a lot.
+
+            // This could be very expensive, so leave it disabled by default.
+/*            OsmMapWriterFactory::writeDebugMap(
+              _map,
+              "UnconnectedWaySnapper-after-snap-#" +
+                QString::number(_numSnappedToWays + _numSnappedToWayNodes))*/;
+          }
         }
 
         if (!snapOccurred)
         {
-          // If we weren't able to snap to a nearby way node or the snapping directly to way nodes
-          // option was turned off, we're going to try to find a nearby way and snap onto the
-          // closest location on it.
-          /*snapOccurred =*/ _snapUnconnectedNodeToWay(unconnectedEndNode);
+          LOG_TRACE(
+            "No snap occurred for " << wayToSnap->getElementId() << " with unconnected end node " <<
+            ElementId(ElementType::Node, unconnectedEndNodeId));
         }
       }
+
+      if (unconnectedEndNodeIds.size() == 0)
+      {
+        LOG_TRACE("No unconnected end nodes to snap for " << wayToSnap->getElementId());
+      }
     }
 
     waysProcessed++;
@@ -277,30 +343,80 @@ void UnconnectedWaySnapper::apply(OsmMapPtr& map)
   LOG_DEBUG(_numSnappedToWayNodes << " ways snapped directly to way nodes");
 }
 
-ElementCriterionPtr UnconnectedWaySnapper::_createFeatureCriterion(const QString& criterionClassName, const Status& status)
+long UnconnectedWaySnapper::_getPid(const ConstWayPtr& way) const
+{
+  LOG_VART(way->hasPid());
+  long pid = WayData::PID_EMPTY;
+  if (way->hasPid())
+  {
+    pid = way->getPid();
+  }
+  else if (way->getTags().contains(MetadataTags::HootSplitParentId()))
+  {
+    pid = way->getTags()[MetadataTags::HootSplitParentId()].toLong();
+  }
+  return pid;
+}
+
+ElementCriterionPtr UnconnectedWaySnapper::_createFeatureCriterion(const QString& criterionClassName,
+                                                                   const QString& status)
 {
   const QString critClass = criterionClassName.trimmed();
   if (!critClass.isEmpty())
   {
     LOG_TRACE(
       "Creating feature filtering criterion: " << criterionClassName << ", status: " <<
-      status.toString() << "...");
-    ElementCriterionPtr critTemp(
+      status << "...");
+
+    // configure our element criterion, in case it needs it
+    ElementCriterionPtr typeCrit(
       Factory::getInstance().constructObject<ElementCriterion>(critClass));
     std::shared_ptr<Configurable> configurable =
-      std::dynamic_pointer_cast<Configurable>(critTemp);
+      std::dynamic_pointer_cast<Configurable>(typeCrit);
     if (configurable)
     {
       configurable->setConfiguration(_conf);
     }
     std::shared_ptr<ConstOsmMapConsumer> mapConsumer =
-      std::dynamic_pointer_cast<ConstOsmMapConsumer>(critTemp);
+      std::dynamic_pointer_cast<ConstOsmMapConsumer>(typeCrit);
     if (mapConsumer)
     {
       mapConsumer->setOsmMap(_map.get());
     }
-    return
-      std::shared_ptr<ChainCriterion>(new ChainCriterion(new StatusCriterion(status), critTemp));
+
+    if (!status.trimmed().isEmpty())
+    {
+      // create our criterion for the feature status
+      ElementCriterionPtr statusCrit;
+      if (!status.contains(";"))
+      {
+        statusCrit.reset(new StatusCriterion(Status::fromString(status)));
+      }
+      else
+      {
+        const QStringList statuses = status.split(";");
+        QList<std::shared_ptr<StatusCriterion>> statusCrits;
+        for (int i = 0; i < statuses.size(); i++)
+        {
+          statusCrits.append(
+            std::shared_ptr<StatusCriterion>(
+              new StatusCriterion(Status::fromString(statuses.at(i)))));
+        }
+        std::shared_ptr<OrCriterion> orCrit(new OrCriterion());
+        for (int i = 0; i < statusCrits.size(); i++)
+        {
+          orCrit->addCriterion(statusCrits.at(i));
+        }
+        statusCrit = orCrit;
+      }
+
+      // combine our element type and status crits into a single crit
+      return std::shared_ptr<ChainCriterion>(new ChainCriterion(statusCrit, typeCrit));
+    }
+    else
+    {
+      return typeCrit;
+    }
   }
   return ElementCriterionPtr();
 }
@@ -362,6 +478,8 @@ std::set<long> UnconnectedWaySnapper::_getUnconnectedEndNodeIds(
     OsmUtils::getContainingWayIdsByNodeId(firstEndNodeId, _map, wayCrit);
   const std::set<long> filteredWaysContainingSecondEndNode =
     OsmUtils::getContainingWayIdsByNodeId(secondEndNodeId, _map, wayCrit);
+  LOG_VART(filteredWaysContainingFirstEndNode);
+  LOG_VART(filteredWaysContainingSecondEndNode);
 
   // If only one way is connected to the end node, then we'll call that node an unconnected end
   // way node.
@@ -389,34 +507,40 @@ Meters UnconnectedWaySnapper::_getWayNodeSearchRadius(const ConstElementPtr& e)
     _addCeToSearchDistance ? _maxNodeReuseDistance + e->getCircularError() : _maxNodeReuseDistance;
 }
 
-std::set<ElementId> UnconnectedWaySnapper::_getNearbyFeaturesToSnapTo(
+QList<ElementId> UnconnectedWaySnapper::_getNearbyFeaturesToSnapTo(
   const ConstNodePtr& node, const ElementType& elementType) const
 {
   LOG_TRACE("Retrieving nearby features to snap to for: " << node->getElementId() << "...");
 
-  // The neighbors do not seem to be returned sorted by distance...hasn't seemed to matter in the
-  // output quality so far but may need some attention later.
-
-  std::set<ElementId> neighborIds;
+  QList<ElementId> neighborIds;
   std::shared_ptr<geos::geom::Envelope> env(node->getEnvelope(_map));
   if (elementType == ElementType::Node)
   {
     env->expandBy(_getWayNodeSearchRadius(node));
+    // The node neighbors must be sorted by distance to get the best way node snapping results. Not
+    // sure yet if they need to be when snapping to ways.
     neighborIds =
-      IndexElementsVisitor::findNeighbors(
-        *env, _snapToWayNodeIndex, _snapToWayNodeIndexToEid, _map);
+      IndexElementsVisitor::findSortedNodeNeighbors(
+        node, *env, _snapToWayNodeIndex, _snapToWayNodeIndexToEid, _map);
   }
   else
   {
     env->expandBy(_getWaySearchRadius(node));
-    neighborIds =
-      IndexElementsVisitor::findNeighbors(*env, _snapToWayIndex, _snapToWayIndexToEid, _map);
+    const std::set<ElementId> neighborIdsSet =
+      IndexElementsVisitor::findNeighbors(
+        *env, _snapToWayIndex, _snapToWayIndexToEid, _map, elementType, false);
+    for (std::set<ElementId>::const_iterator neighborIdsItr = neighborIdsSet.begin();
+         neighborIdsItr != neighborIdsSet.end(); ++neighborIdsItr)
+    {
+      neighborIds.append(*neighborIdsItr);
+    }
   }
   LOG_VART(neighborIds);
   return neighborIds;
 }
 
-int UnconnectedWaySnapper::_getNodeToSnapWayInsertIndex(const NodePtr& nodeToSnap, const ConstWayPtr& wayToSnapTo) const
+int UnconnectedWaySnapper::_getNodeToSnapWayInsertIndex(const NodePtr& nodeToSnap,
+                                                        const ConstWayPtr& wayToSnapTo) const
 {
   LOG_TRACE(
     "Calculating way snap insert index for: " << nodeToSnap->getElementId() << " going into: " <<
@@ -490,10 +614,17 @@ bool UnconnectedWaySnapper::_snapUnconnectedNodeToWayNode(const NodePtr& nodeToS
 
   // Find all way nodes near the input way node that don't belong to the same way the input way
   // node belongs to.
-  const std::set<ElementId> wayNodesToSnapTo =
+  const QList<ElementId> wayNodesToSnapTo =
     _getNearbyFeaturesToSnapTo(nodeToSnap, ElementType::Node);
+  if (wayNodesToSnapTo.size() == 0)
+  {
+    LOG_TRACE(
+      "No nearby way nodes to snap to for " << nodeToSnap->getElementId() <<
+      ". Skipping snapping...");
+  }
+
   // For each way node neighbor, let's try snapping to the first one that we can.
-  for (std::set<ElementId>::const_iterator wayNodesToSnapToItr = wayNodesToSnapTo.begin();
+  for (QList<ElementId>::const_iterator wayNodesToSnapToItr = wayNodesToSnapTo.begin();
        wayNodesToSnapToItr != wayNodesToSnapTo.end(); ++wayNodesToSnapToItr)
   {
     const long wayNodeToSnapToId = (*wayNodesToSnapToItr).getId();
@@ -503,19 +634,26 @@ bool UnconnectedWaySnapper::_snapUnconnectedNodeToWayNode(const NodePtr& nodeToS
     if (wayNodeToSnapToId != nodeToSnap->getId())
     {
       NodePtr wayNodeToSnapTo = _map->getNode(wayNodeToSnapToId);
-      LOG_VART(wayNodeToSnapTo->getId())
+      // not exactly sure what could cause this, but it has happened
+      if (!wayNodeToSnapTo)
+      {
+        LOG_TRACE(
+          "Way node to snap to with ID: " << wayNodeToSnapToId <<
+          " does not exist in the map. Skipping snap...");
+        continue;
+      }
+      LOG_VART(wayNodeToSnapTo->getId());
 
       // Compare all the ways that a contain the neighbor and all the ways that contain our input
       // node.  If there's overlap, then we pass b/c we don't want to try to snap the input way
       // node to a way its already on.
-      if (!OsmUtils::nodesAreContainedByTheSameWay(wayNodeToSnapToId, nodeToSnap->getId(), _map) /*&&
+      if (!OsmUtils::nodesAreContainedInTheSameWay(wayNodeToSnapToId, nodeToSnap->getId(), _map) /*&&
           // I don't think this distance check is necessary...leaving here disabled for the time
           // being just in case. See similar check in _snapUnconnectedNodeToWay
           Distance::euclidean(wayNodeToSnapTo->toCoordinate(), nodeToSnap->toCoordinate()) <=
             _maxNodeReuseDistance*/)
       {
         // Snap the input way node to the nearest way node neighbor we found.
-
         LOG_TRACE(
           "Snapping way node: " << nodeToSnap->getId() << " to way node: " << wayNodeToSnapToId);
 
@@ -526,10 +664,31 @@ bool UnconnectedWaySnapper::_snapUnconnectedNodeToWayNode(const NodePtr& nodeToS
         // Add the optional custom tag for tracking purposes.
         if (_markSnappedNodes)
         {
-          wayNodeToSnapTo->getTags().set(MetadataTags::HootSnappedWayNode(), "snapped_to_way_node");
+          wayNodeToSnapTo->getTags().set(MetadataTags::HootSnapped(), "snapped_to_way_node");
+        }
+        if (_markSnappedWays)
+        {
+          std::set<long> owningWayIds =
+            OsmUtils::getContainingWayIdsByNodeId(nodeToSnap->getId(), _map);
+          // assert(wayIds.size() == 1);
+          const long owningWayId = *owningWayIds.begin();
+          _map->getWay(owningWayId)->getTags().set(MetadataTags::HootSnapped(), "snapped_way");
         }
+        // get the snapped to way so we can retain the parent id; size should be equal to 1;
+        // this could be optimized, since we're doing way containing way node checks already above
+        const std::set<long>& waysContainingWayNodeToSnapTo =
+          _map->getIndex().getNodeToWayMap()->getWaysByNode(wayNodeToSnapToId);
+        LOG_TRACE(waysContainingWayNodeToSnapTo.size());
+        assert(waysContainingWayNodeToSnapTo.size() > 0);
+        _snappedToWay = _map->getWay(*waysContainingWayNodeToSnapTo.begin());
+        LOG_VART(_snappedToWay);
 
         // Replace the snapped node with the node we snapped it to.
+        // TODO: Should we also set the status of the snapped node to that of the way it was snapped
+        // to?
+        LOG_TRACE(
+          "Replacing " << nodeToSnap->getElementId() << " with " <<
+          wayNodeToSnapTo->getElementId());
         ReplaceElementOp elementReplacer(
           nodeToSnap->getElementId(), wayNodeToSnapTo->getElementId(), true);
         elementReplacer.apply(_map);
@@ -541,6 +700,12 @@ bool UnconnectedWaySnapper::_snapUnconnectedNodeToWayNode(const NodePtr& nodeToS
         // Don't snap the node more than once.
         return true;
       }
+      else
+      {
+        LOG_TRACE(
+          "Nodes " << wayNodeToSnapTo->getElementId() << " and " << nodeToSnap->getElementId() <<
+          " are contained in the same way. Skipping snapping...")
+      }
     }
   }
 
@@ -552,9 +717,9 @@ bool UnconnectedWaySnapper::_snapUnconnectedNodeToWay(const NodePtr& nodeToSnap)
   LOG_TRACE("Attempting to snap unconnected node: " << nodeToSnap->getId() << " to a way...");
 
   // get nearby ways
-  const std::set<ElementId> waysToSnapTo =
+  const QList<ElementId> waysToSnapTo =
     _getNearbyFeaturesToSnapTo(nodeToSnap, ElementType::Way);
-  for (std::set<ElementId>::const_iterator waysToSnapToItr = waysToSnapTo.begin();
+  for (QList<ElementId>::const_iterator waysToSnapToItr = waysToSnapTo.begin();
        waysToSnapToItr != waysToSnapTo.end(); ++waysToSnapToItr)
   {
     // for each neighboring way
@@ -574,7 +739,8 @@ bool UnconnectedWaySnapper::_snapUnconnectedNodeToWay(const NodePtr& nodeToSnap)
   return false;
 }
 
-bool UnconnectedWaySnapper::snapClosestEndpointToWay(OsmMapPtr map, const WayPtr& disconnected, const WayPtr& connectTo)
+bool UnconnectedWaySnapper::snapClosestEndpointToWay(OsmMapPtr map, const WayPtr& disconnected,
+                                                     const WayPtr& connectTo)
 {
   //  Create object for static call
   UnconnectedWaySnapper uws;
@@ -583,7 +749,8 @@ bool UnconnectedWaySnapper::snapClosestEndpointToWay(OsmMapPtr map, const WayPtr
   return uws._snapClosestEndpointToWay(disconnected, connectTo);
 }
 
-bool UnconnectedWaySnapper::_snapClosestEndpointToWay(const WayPtr& disconnected, const WayPtr& connectTo)
+bool UnconnectedWaySnapper::_snapClosestEndpointToWay(const WayPtr& disconnected,
+                                                      const WayPtr& connectTo)
 {
   //  Validate the parameters
   if (!disconnected || !connectTo || disconnected->getId() == connectTo->getId())
@@ -607,12 +774,17 @@ bool UnconnectedWaySnapper::_snapClosestEndpointToWay(const WayPtr& disconnected
   return _snapUnconnectedNodeToWay(endpoint, connectTo);
 }
 
-bool UnconnectedWaySnapper::_snapUnconnectedNodeToWay(const NodePtr& nodeToSnap, const WayPtr& wayToSnapTo)
+bool UnconnectedWaySnapper::_snapUnconnectedNodeToWay(const NodePtr& nodeToSnap,
+                                                      const WayPtr& wayToSnapTo)
 {
   //  Validate the parameters
   if (!nodeToSnap || !wayToSnapTo)
     return false;
 
+  LOG_TRACE(
+    "Attempting to snap node: " << nodeToSnap->getElementId() << " to way: " <<
+    wayToSnapTo->getElementId() << "...")
+
   const std::vector<long>& wayNodeIdsToSnapTo = wayToSnapTo->getNodeIds();
   // Make sure the way we're trying to snap to doesn't already contain the node we're trying to
   // snap to it.
@@ -631,6 +803,8 @@ bool UnconnectedWaySnapper::_snapUnconnectedNodeToWay(const NodePtr& nodeToSnap,
     // This check of the calculated distance being less than the allowed snap distance should not
     // be necessary, but it is for now.  For some reason, neighbors are occasionally being
     // returned at longer distances away than expected.
+    // TODO: This may have been fixed with the addition of distance sorting in
+    // IndexElementsVisitor...check.
     if (/*shortestDistance != DBL_MAX &&*/
         shortestDistanceFromNodeToSnapToWayCoord <= _maxSnapDistance)
     {
@@ -647,7 +821,7 @@ bool UnconnectedWaySnapper::_snapUnconnectedNodeToWay(const NodePtr& nodeToSnap,
             closestWayToSnapToCoord, _map->getProjection(),
             MapProjector::createWgs84Projection());
         LOG_TRACE(
-          "Snapping way node: " << nodeToSnap->getId() << " to coord: " <<
+          "Snapping way node: " << nodeToSnap->getElementId() << " to coord: " <<
           closestWayToSnapToCoord.toString() << " (wgs84: " << wgs84Coord.toString() <<
           ") and inserting at index: " << nodeToSnapInsertIndex);
       }
@@ -659,7 +833,15 @@ bool UnconnectedWaySnapper::_snapUnconnectedNodeToWay(const NodePtr& nodeToSnap,
       // Add the optional custom tag for tracking purposes.
       if (_markSnappedNodes)
       {
-        nodeToSnap->getTags().set(MetadataTags::HootSnappedWayNode(), "snapped_to_way");
+        nodeToSnap->getTags().set(MetadataTags::HootSnapped(), "snapped_to_way");
+      }
+      if (_markSnappedWays)
+      {
+        std::set<long> owningWayIds =
+          OsmUtils::getContainingWayIdsByNodeId(nodeToSnap->getId(), _map);
+        // assert(wayIds.size() == 1);
+        const long owningWayId = *owningWayIds.begin();
+        _map->getWay(owningWayId)->getTags().set(MetadataTags::HootSnapped(), "snapped_way");
       }
 
       // add the snapped node as a way node on the target way
@@ -668,9 +850,23 @@ bool UnconnectedWaySnapper::_snapUnconnectedNodeToWay(const NodePtr& nodeToSnap,
       wayNodeIdsToSnapToList.insert(nodeToSnapInsertIndex, nodeToSnap->getId());
       wayToSnapTo->setNodes(wayNodeIdsToSnapToList.toVector().toStdVector());
       LOG_VART(wayToSnapTo->getNodeIds());
+      _snappedToWay = wayToSnapTo;
+      LOG_VART(_snappedToWay);
 
       return true;
     }
+    else
+    {
+      LOG_TRACE(
+        "Node to snap: " << nodeToSnap->getElementId() << " not within snap distance: " <<
+        _maxSnapDistance << " of " << wayToSnapTo->getElementId());
+    }
+  }
+  else
+  {
+    LOG_TRACE(
+      "Way to snap to: " << wayToSnapTo->getElementId() << " contains node to snap: " <<
+      nodeToSnap->getElementId())
   }
   return false;
 }
Clone this wiki locally