Skip to content

v0.2.47..v0.2.48 changeset MapCropper.cpp

Garret Voltz edited this page Sep 27, 2019 · 1 revision
diff --git a/hoot-core/src/main/cpp/hoot/core/ops/MapCropper.cpp b/hoot-core/src/main/cpp/hoot/core/ops/MapCropper.cpp
index 2f9ba8a..91803e6 100644
--- a/hoot-core/src/main/cpp/hoot/core/ops/MapCropper.cpp
+++ b/hoot-core/src/main/cpp/hoot/core/ops/MapCropper.cpp
@@ -54,6 +54,8 @@
 #include <hoot/core/util/MapProjector.h>
 #include <hoot/core/util/Validate.h>
 #include <hoot/core/ops/RemoveEmptyRelationsOp.h>
+#include <hoot/core/util/StringUtils.h>
+#include <hoot/core/elements/OsmUtils.h>
 
 // Standard
 #include <limits>
@@ -68,6 +70,8 @@ using namespace Tgs;
 namespace hoot
 {
 
+int MapCropper::logWarnCount = 0;
+
 HOOT_FACTORY_REGISTER(OsmMapOperation, MapCropper)
 
 MapCropper::MapCropper() :
@@ -79,9 +83,9 @@ _numWaysInBounds(0),
 _numWaysOutOfBounds(0),
 _numWaysCrossingThreshold(0),
 _numCrossingWaysKept(0),
-_numCrossingWaysRemoved(0)
+_numCrossingWaysRemoved(0),
+_numNodesRemoved(0)
 {
-  setConfiguration(conf());
 }
 
 MapCropper::MapCropper(const Envelope& envelope) :
@@ -95,13 +99,14 @@ _numWaysInBounds(0),
 _numWaysOutOfBounds(0),
 _numWaysCrossingThreshold(0),
 _numCrossingWaysKept(0),
-_numCrossingWaysRemoved(0)
+_numCrossingWaysRemoved(0),
+_numNodesRemoved(0)
 {
 }
 
-MapCropper::MapCropper(const std::shared_ptr<const Geometry>& g, bool invert) :
+MapCropper::MapCropper(const std::shared_ptr<const Geometry>& g) :
 _envelopeG(g),
-_invert(invert),
+_invert(false),
 _keepEntireFeaturesCrossingBounds(false),
 _keepOnlyFeaturesInsideBounds(false),
 _statusUpdateInterval(1000),
@@ -109,15 +114,28 @@ _numWaysInBounds(0),
 _numWaysOutOfBounds(0),
 _numWaysCrossingThreshold(0),
 _numCrossingWaysKept(0),
-_numCrossingWaysRemoved(0)
+_numCrossingWaysRemoved(0),
+_numNodesRemoved(0)
+{
+}
+
+void MapCropper::setBounds(const geos::geom::Envelope& bounds)
+{
+  _nodeBounds = bounds;
+  _envelope = bounds,
+  _envelopeG.reset(GeometryFactory::getDefaultInstance()->toGeometry(&_envelope));
+}
+
+void MapCropper::setBounds(const std::shared_ptr<const geos::geom::Geometry>& g)
 {
+  _envelopeG = g;
 }
 
 void MapCropper::setInvert(bool invert)
 {
   _invert = invert;
-  // I don't think these options make sense when we're doing inverted cropping (maybe they do?),
-  // so let's leave them turned off if inverting is selected.
+  // Haven't seen evidence that these options make sense when we're doing inverted cropping so
+  // let's leave them turned off if inverting is selected.
   if (!_invert)
   {
     _keepOnlyFeaturesInsideBounds = false;
@@ -127,10 +145,18 @@ void MapCropper::setInvert(bool invert)
 
 void MapCropper::setKeepEntireFeaturesCrossingBounds(bool keep)
 {
-  if (_invert || _keepOnlyFeaturesInsideBounds)
+  // this option is ignored when set to invert
+  if (_invert)
   {
     _keepEntireFeaturesCrossingBounds = false;
   }
+  // this option is incomptible with the option to keep only features inside the bounds
+  else if (keep && _keepOnlyFeaturesInsideBounds)
+  {
+    throw IllegalArgumentException(
+      QString("Incompatible crop options: _keepOnlyFeaturesInsideBounds and ") +
+      QString("_keepEntireFeaturesCrossingBounds cannot both be enabled."));
+  }
   else
   {
     _keepEntireFeaturesCrossingBounds = keep;
@@ -139,17 +165,21 @@ void MapCropper::setKeepEntireFeaturesCrossingBounds(bool keep)
 
 void MapCropper::setKeepOnlyFeaturesInsideBounds(bool keep)
 {
+  // this option is ignored when set to invert
   if (_invert)
   {
     _keepOnlyFeaturesInsideBounds = false;
   }
+  // this option is incomptible with the option to keep features crossing the bounds
+  else if (keep && _keepEntireFeaturesCrossingBounds)
+  {
+    throw IllegalArgumentException(
+      QString("Incompatible crop options: _keepOnlyFeaturesInsideBounds and ") +
+      QString("_keepEntireFeaturesCrossingBounds cannot both be enabled."));
+  }
   else
   {
     _keepOnlyFeaturesInsideBounds = keep;
-    if (_keepOnlyFeaturesInsideBounds)
-    {
-      _keepEntireFeaturesCrossingBounds = false;
-    }
   }
 }
 
@@ -163,6 +193,7 @@ void MapCropper::setConfiguration(const Settings& conf)
     LOG_VARD(_envelope);
     _envelopeG.reset(GeometryFactory::getDefaultInstance()->toGeometry(&_envelope));
   }
+  // invert must be set before the two options following it
   setInvert(confOpts.getCropInvert());
   setKeepEntireFeaturesCrossingBounds(confOpts.getCropKeepEntireFeaturesCrossingBounds());
   setKeepOnlyFeaturesInsideBounds(confOpts.getCropKeepOnlyFeaturesInsideBounds());
@@ -172,144 +203,240 @@ void MapCropper::setConfiguration(const Settings& conf)
 
 void MapCropper::apply(OsmMapPtr& map)
 {
-  _numAffected = 0;
-  OsmMapPtr result = map;
+  if (MapProjector::isGeographic(map) == false && _nodeBounds.isNull() == false)
+  {
+    throw HootException("If the node bounds is set the projection must be geographic.");
+  }
 
+  LOG_DEBUG("Cropping ways...");
+
+  _numProcessed = 0;
+  _numAffected = 0;
+  _numWaysInBounds = 0;
+  _numWaysOutOfBounds = 0;
+  _numWaysCrossingThreshold = 0;
+  _numCrossingWaysKept = 0;
+  _numCrossingWaysRemoved = 0;
+  _numNodesRemoved = 0;
+  _explicitlyIncludedWayIds.clear();
+
+  LOG_VARD(_invert);
   LOG_VARD(_keepEntireFeaturesCrossingBounds);
   LOG_VARD(_keepOnlyFeaturesInsideBounds);
   LOG_VARD(_envelope);
-
-  if (MapProjector::isGeographic(map) == false && _nodeBounds.isNull() == false)
+  if (_envelopeG)
   {
-    throw HootException("If the node bounds is set the projection must be geographic.");
+    LOG_VARD(_envelopeG->toString());
   }
-
-  // Try visiting the elements from the most senior (e.g. relation that has no parents) to the
-  // most junior (nodes)?
+  LOG_VARD(_inclusionCrit.get());
 
   // go through all the ways
   long wayCtr = 0;
-  const WayMap ways = result->getWays();
+  const WayMap ways = map->getWays();
   for (WayMap::const_iterator it = ways.begin(); it != ways.end(); ++it)
   {
     const std::shared_ptr<Way>& w = it->second;
-    LOG_VART(w);
+    LOG_TRACE("Checking " << w->getElementId() << " for cropping...");
+    LOG_VART(w->getNodeIds());
+
     std::shared_ptr<LineString> ls = ElementConverter(map).convertToLineString(w);
-    const Envelope& e = *(ls->getEnvelopeInternal());
-    LOG_VART(e);
+    if (!ls.get())
+    {
+      if (logWarnCount < Log::getWarnMessageLimit())
+      {
+        LOG_WARN("Couldn't convert " << w->getElementId() << " to line string. Keeping way...");
+        LOG_VARW(w);
+      }
+      else if (logWarnCount == Log::getWarnMessageLimit())
+      {
+        LOG_WARN(className() << ": " << Log::LOG_WARN_LIMIT_REACHED_MESSAGE);
+      }
+      logWarnCount++;
 
-    // if the way is completely outside the region we're keeping
-    if (_isWhollyOutside(e))
+      _numProcessed++;
+      wayCtr++;
+      continue;
+    }
+    const Envelope& wayEnv = *(ls->getEnvelopeInternal());
+    LOG_VART(wayEnv);
+
+    if (_inclusionCrit)
+    {
+      LOG_VART(_inclusionCrit->isSatisfied(w));
+    }
+    if (_inclusionCrit && _inclusionCrit->isSatisfied(w))
+    {
+      // keep the way
+      LOG_TRACE("Keeping explicitly included way: " << w->getElementId() << "...");
+      _explicitlyIncludedWayIds.insert(w->getId());
+      _numWaysInBounds++;
+    }
+    // Have found that if we have _envelopeG, using it for inside/outside way comparison is more
+    // accurate than using the envelope. Its possible that we could eventually use that method
+    // exclusively and get rid of storing _envelope.
+    else if ((_envelopeG && _isWhollyOutside(*ls)) || _isWhollyOutside(wayEnv))
     {
       // remove the way
-      LOG_TRACE("Dropping: " << w << "...");
-      RemoveWayByEid::removeWayFully(result, w->getId());
+      LOG_TRACE("Dropping wholly outside way: " << w->getElementId() << "...");
+      RemoveWayByEid::removeWayFully(map, w->getId());
       _numWaysOutOfBounds++;
+      _numAffected++;
     }
-    else if (_isWhollyInside(e))
+    else if ((_envelopeG && _isWhollyInside(*ls)) || _isWhollyInside(wayEnv))
     {
       // keep the way
-      LOG_TRACE("Keeping: " << w << "...");
+      LOG_TRACE("Keeping wholly inside way: " << w->getElementId() << "...");
       _numWaysInBounds++;
     }
     else if (_keepOnlyFeaturesInsideBounds)
     {
-      // remove the way
-      LOG_TRACE("Dropping: " << w << "...");
-      RemoveWayByEid::removeWayFully(result, w->getId());
+      // Way isn't wholly inside and the configuration requires it to be, so remove the way.
+      LOG_TRACE(
+        "Dropping due to _keepOnlyFeaturesInsideBounds=true: " << w->getElementId() << "...");
+      RemoveWayByEid::removeWayFully(map, w->getId());
       _numWaysOutOfBounds++;
+      _numAffected++;
     }
     else if (!_keepEntireFeaturesCrossingBounds)
     {
+      // Way crosses the boundary and we're not configured to keep ways that cross the bounds, so
       // do an expensive operation to decide how much to keep, if any.
-      _cropWay(result, w->getId());
+      LOG_TRACE(
+        "Cropping due to _keepEntireFeaturesCrossingBounds=false: " << w->getElementId() << "...");
+      _cropWay(map, w->getId());
       _numWaysCrossingThreshold++;
     }
     else
     {
-      LOG_TRACE("Skipping: " << w);
+      // keep the way
+      LOG_TRACE("Keeping way: " << w->getElementId() << "...");
+      _numWaysInBounds++;
     }
 
     wayCtr++;
+    _numProcessed++;
     if (wayCtr % _statusUpdateInterval == 0)
     {
-      PROGRESS_INFO("Cropped " << wayCtr << " / " << ways.size() << " ways.");
+      PROGRESS_INFO(
+        "Cropped " << StringUtils::formatLargeNumber(wayCtr) << " / " <<
+        StringUtils::formatLargeNumber(ways.size()) << " ways.");
     }
   }
 
-  std::shared_ptr<NodeToWayMap> n2wp = result->getIndex().getNodeToWayMap();
-  NodeToWayMap& n2w = *n2wp;
+  std::shared_ptr<NodeToWayMap> n2w = map->getIndex().getNodeToWayMap();
 
-  LOG_INFO("Removing nodes...");
+  LOG_DEBUG("Removing nodes...");
 
   // go through all the nodes
   long nodeCtr = 0;
   long nodesRemoved = 0;
-  const NodeMap nodes = result->getNodes();
+  const NodeMap nodes = map->getNodes();
   for (NodeMap::const_iterator it = nodes.begin(); it != nodes.end(); ++it)
   {
-    const Coordinate& c = it->second->toCoordinate();
+    NodePtr node = it->second;
+    LOG_VART(node->getElementId());
 
     bool nodeInside = false;
 
-    if (_envelope.isNull() == false)
+    LOG_VART(_explicitlyIncludedWayIds.size());
+    if (_explicitlyIncludedWayIds.size() > 0)
     {
-      if (_invert == false)
-      {
-        nodeInside = _envelope.covers(c);
-      }
-      else
-      {
-        nodeInside = !_envelope.covers(c);
-      }
+      LOG_VART(OsmUtils::nodeContainedByAnyWay(node->getId(), _explicitlyIncludedWayIds, map));
+    }
+    if (_inclusionCrit && _explicitlyIncludedWayIds.size() > 0 &&
+        OsmUtils::nodeContainedByAnyWay(node->getId(), _explicitlyIncludedWayIds, map))
+    {
+      LOG_TRACE(
+        "Skipping delete for: " << node->getElementId() <<
+        " belonging to explicitly included way(s)...");
+      nodeInside = true;
     }
     else
     {
-      std::shared_ptr<Point> p(GeometryFactory::getDefaultInstance()->createPoint(c));
-
-      if (_invert == false)
+      const Coordinate& c = it->second->toCoordinate();
+      LOG_VART(c.toString());
+      if (_envelope.isNull() == false)
       {
-        nodeInside = _envelopeG->intersects(p.get());
+        if (_invert == false)
+        {
+          nodeInside = _envelope.covers(c);
+          LOG_TRACE(
+            "Node inside check: non-inverted crop and the envelope covers the element=" <<
+            nodeInside);
+        }
+        else
+        {
+          nodeInside = !_envelope.covers(c);
+          LOG_TRACE(
+            "Node inside check: inverted crop and the envelope covers the element=" << !nodeInside);
+        }
       }
       else
       {
-        nodeInside = !_envelopeG->intersects(p.get());
+        std::shared_ptr<Point> p(GeometryFactory::getDefaultInstance()->createPoint(c));
+        if (_invert == false)
+        {
+          nodeInside = _envelopeG->intersects(p.get());
+          LOG_TRACE(
+            "Node inside check: non-inverted crop and the envelope intersects the element=" <<
+            nodeInside);
+        }
+        else
+        {
+          nodeInside = !_envelopeG->intersects(p.get());
+          LOG_TRACE(
+            "Node inside check: inverted crop and the envelope intersects the element=" <<
+            !nodeInside);
+        }
       }
-    }
+      LOG_VART(nodeInside);
 
-    // if the node is outside
-    if (!nodeInside)
-    {
-      // if the node is within the limiting bounds.
-      if (_nodeBounds.isNull() == true || _nodeBounds.contains(c))
+      // if the node is outside
+      if (!nodeInside)
       {
-        // if the node is not part of a way
-        if (n2w.find(it->first) == n2w.end())
+        // if the node is within the limiting bounds.
+        // TODO: This may have been left over from support for four pass conflation using Hadoop.
+        // Could we just use _envelope or _envelopeG here instead?
+        LOG_VART(_nodeBounds.isNull());
+        if (!_nodeBounds.isNull())
         {
-          // remove the node
-          LOG_TRACE(
-            "Removing node with coords: " << it->second->getX() << " : " << it->second->getY());
-          RemoveNodeByEid::removeNodeNoCheck(result, it->second->getId());
-          nodesRemoved++;
+          LOG_VART(_nodeBounds.contains(c));
+        }
+        if (_nodeBounds.isNull() == true || _nodeBounds.contains(c))
+        {
+          // if the node is not part of a way
+          if (n2w->find(it->first) == n2w->end())
+          {
+            // remove the node
+            LOG_TRACE(
+              "Removing node with coords: " << it->second->getX() << " : " << it->second->getY());
+            RemoveNodeByEid::removeNodeNoCheck(map, it->second->getId());
+            nodesRemoved++;
+            _numAffected++;
+          }
         }
       }
     }
 
     nodeCtr++;
+    _numProcessed++;
     if (nodeCtr % _statusUpdateInterval == 0)
     {
       PROGRESS_INFO("Cropped " << nodeCtr << " / " << nodes.size() << " nodes.");
     }
   }
-  LOG_DEBUG("Nodes removed: " + QString::number(nodesRemoved));
-
-  RemoveEmptyRelationsOp().apply(map);
 
-  LOG_VARD(_numWaysInBounds);
-  LOG_VARD(_numWaysOutOfBounds);
-  LOG_VARD(_numWaysCrossingThreshold);
-  LOG_VARD(_numCrossingWaysKept);
-  LOG_VARD(_numCrossingWaysRemoved);
+  RemoveEmptyRelationsOp emptyRelationRemover;
+  LOG_INFO(emptyRelationRemover.getInitStatusMessage());
+  emptyRelationRemover.apply(map);
+  LOG_DEBUG(emptyRelationRemover.getCompletedStatusMessage());
+
+  LOG_VARD(StringUtils::formatLargeNumber(_numWaysInBounds));
+  LOG_VARD(StringUtils::formatLargeNumber(_numWaysOutOfBounds));
+  LOG_VARD(StringUtils::formatLargeNumber(_numWaysCrossingThreshold));
+  LOG_VARD(StringUtils::formatLargeNumber(_numCrossingWaysKept));
+  LOG_VARD(StringUtils::formatLargeNumber(_numCrossingWaysRemoved));
+  LOG_VARD(StringUtils::formatLargeNumber(_numNodesRemoved));
 }
 
 void MapCropper::_cropWay(const OsmMapPtr& map, long wid)
@@ -317,7 +444,6 @@ void MapCropper::_cropWay(const OsmMapPtr& map, long wid)
   LOG_TRACE("Cropping way crossing bounds: " << wid << "...");
 
   std::shared_ptr<Way> way = map->getWay(wid);
-
   std::shared_ptr<Geometry> fg = ElementConverter(map).convertToGeometry(way);
 
   // perform the intersection with the geometry
@@ -348,57 +474,64 @@ void MapCropper::_cropWay(const OsmMapPtr& map, long wid)
 
   if (e == 0)
   {
-    LOG_TRACE("Removing way: " << way->getId() << "...");
+    LOG_TRACE("Removing way during crop check: " << way->getElementId() << "...");
     RemoveWayByEid::removeWayFully(map, way->getId());
     _numCrossingWaysRemoved++;
+    _numAffected++;
   }
   else
   {
-    LOG_TRACE("Replacing way: " << way->getId() << " with element: " << e->getElementId() << "...");
-    e->setTags(way->getTags());
-    map->replace(way, e);
-    _numCrossingWaysKept++;
-  }
-}
+    LOG_TRACE(
+      "Replacing way during crop check: " << way->getElementId() << " with element: " <<
+      e->getElementId() << "...");
 
-long MapCropper::_findNodeId(const std::shared_ptr<const OsmMap>& map,
-                             const std::shared_ptr<const Way>& w, const Coordinate& c)
-{
-  long result = std::numeric_limits<long>::max();
-  const std::vector<long>& nodeIds = w->getNodeIds();
+    e->setTags(way->getTags());
 
-  for (size_t i = 0; i < nodeIds.size(); i++)
-  {
-    ConstNodePtr n = map->getNode(nodeIds[i]);
-    if (n->toCoordinate() == c)
+    // retain the parent ID
+    if (e->getElementType() == ElementType::Way)
+    {
+      WayPtr newWay = std::dynamic_pointer_cast<Way>(e);
+      newWay->setPid(way->getId());
+    }
+    else if (e->getElementType() == ElementType::Relation)
     {
-      // if there are multiple corresponding nodes, throw an exception.
-      if (result != std::numeric_limits<long>::max() && result != nodeIds[i])
+      RelationPtr newRelation = std::dynamic_pointer_cast<Relation>(e);
+      const vector<RelationData::Entry>& members = newRelation->getMembers();
+      for (size_t i = 0; i < members.size(); i++)
       {
-//        throw InternalErrorException(QString("Internal Error: Two nodes were found with the same "
-//                                     "coordinate. way: %1").arg(w->getId()));
-        LOG_ERROR("" << "Internal Error: Two nodes were found with the same coordinate. way: " <<
-            w->getId());
+        RelationData::Entry element = members[i];
+        if (element.getElementId().getType() == ElementType::Way)
+        {
+          WayPtr memberWay = map->getWay(element.getElementId().getId());
+          memberWay->setPid(way->getId());
+        }
       }
-      result = nodeIds[i];
     }
-  }
 
-  return result;
+    map->replace(way, e);
+
+    _numCrossingWaysKept++;
+  }
 }
 
 bool MapCropper::_isWhollyInside(const Envelope& e)
 {
   bool result = false;
+  LOG_VART(_envelope.isNull());
   if (_envelope.isNull() == false)
   {
     if (_invert)
     {
       result = !_envelope.intersects(e);
+      LOG_TRACE(
+        "Wholly inside check: inverted crop and the envelope intersects with the element=" <<
+        !result);
     }
     else
     {
       result = _envelope.covers(e);
+      LOG_TRACE(
+        "Wholly inside check: non-inverted crop and the envelope covers the element=" << result);
     }
   }
   else
@@ -406,27 +539,63 @@ bool MapCropper::_isWhollyInside(const Envelope& e)
     if (_invert)
     {
       result = !_envelopeG->getEnvelopeInternal()->intersects(e);
+      LOG_TRACE(
+        "Wholly inside way check: inverted crop and the envelope intersects with the element=" <<
+        !result);
     }
     else
     {
-      // if it isn't inverted we'd need to do a complex and expensive check so just return false.
+      // If it isn't inverted, we need to do an expensive check.
+      result = _envelopeG->getEnvelopeInternal()->covers(e);
+      LOG_TRACE(
+        "Wholly inside way check: non-inverted crop and the envelope covers the element=" << result);
     }
   }
+  LOG_TRACE("Wholly inside way check result: " << result);
+  return result;
+}
+
+bool MapCropper::_isWhollyInside(const Geometry& e)
+{
+  bool result = false;
+  LOG_VART(_envelope.isNull());
+
+  if (_invert)
+  {
+    result = !_envelopeG->intersects(&e);
+    LOG_TRACE(
+      "Wholly inside way check: inverted crop and the envelope intersects with the element=" <<
+      !result);
+  }
+  else
+  {
+    // If it isn't inverted, we need to do an expensive check.
+    result = _envelopeG->covers(&e);
+    LOG_TRACE(
+      "Wholly inside way check: non-inverted crop and the envelope covers the element=" << result);
+  }
+  LOG_TRACE("Wholly inside way check result: " << result);
   return result;
 }
 
 bool MapCropper::_isWhollyOutside(const Envelope& e)
 {
   bool result = false;
+  LOG_VART(_envelope.isNull());
   if (_envelope.isNull() == false)
   {
     if (_invert)
     {
       result = _envelope.covers(e);
+      LOG_TRACE(
+        "Wholly outside check: inverted crop and the envelope covers the element=" << result);
     }
     else
     {
       result = !_envelope.intersects(e);
+      LOG_TRACE(
+        "Wholly outside way check: non-inverted crop and the envelope intersects with the element=" <<
+        !result);
     }
   }
   else
@@ -434,8 +603,40 @@ bool MapCropper::_isWhollyOutside(const Envelope& e)
     if (_invert == false)
     {
       result = !_envelopeG->getEnvelopeInternal()->intersects(e);
+      //result = !_envelopeG->intersects(GeometryFactory::getDefaultInstance()->toGeometry(&e));
+      LOG_TRACE(
+        "Wholly outside way check: non-inverted crop and the envelope intersects with the element=" <<
+        !result);
+    }
+    else
+    {
+      result = _envelopeG->getEnvelopeInternal()->covers(e);
+      //result = _envelopeG->covers(GeometryFactory::getDefaultInstance()->toGeometry(&e));
+      LOG_TRACE(
+        "Wholly outside way check: inverted crop and the envelope covers the element=" << result);
     }
   }
+  LOG_TRACE("Wholly outside way check result: " << result);
+  return result;
+}
+
+bool MapCropper::_isWhollyOutside(const Geometry& e)
+{
+  bool result = false;
+  if (_invert == false)
+  {
+    result = !_envelopeG->intersects(&e);
+    LOG_TRACE(
+      "Wholly outside way check: non-inverted crop and the envelope intersects with the element=" <<
+      !result);
+  }
+  else
+  {
+    result = _envelopeG->covers(&e);
+    LOG_TRACE(
+      "Wholly outside way check: inverted crop and the envelope covers the element=" << result);
+  }
+  LOG_TRACE("Wholly outside way check result: " << result);
   return result;
 }
 
@@ -466,46 +667,6 @@ void MapCropper::readObject(QDataStream& is)
   }
 }
 
-std::shared_ptr<Way> MapCropper::_reintroduceWay(OsmMapPtr map, std::shared_ptr<const Way> w,
-  const LineString* ls)
-{
-  // create a new way
-  std::shared_ptr<Way> newWay(new Way(w->getStatus(), map->createNextWayId(),
-    w->getRawCircularError()));
-  newWay->setPid(w->getPid());
-  newWay->setTags(w->getTags());
-
-  // for each point on the linestring
-  for (size_t i = 0; i < ls->getNumPoints(); i++)
-  {
-    const Coordinate& c = ls->getCoordinateN(i);
-
-    // find the corresponding node in the way
-    long nid = _findNodeId(map, w, c);
-
-    // if there isn't a corresponding node in the way
-    if (nid == std::numeric_limits<long>::max())
-    {
-      // if this isn't the first or last point
-      if (i != 0 && i != ls->getNumPoints() - 1)
-      {
-        // this shouldn't be possible, something went wrong.
-        throw InternalErrorException("Internal Error: An unexpected coordinate was found.");
-      }
-      // create a new node
-      NodePtr node(new Node(w->getStatus(), map->createNextNodeId(), c,
-        w->getCircularError()));
-      map->addNode(node);
-      nid = node->getId();
-    }
-
-    // add the node to the way
-    newWay->addNode(nid);
-  }
-
-  return newWay;
-}
-
 void MapCropper::writeObject(QDataStream& os) const
 {
   os << _invert;
Clone this wiki locally