diff --git a/hoot-core/src/main/cpp/hoot/core/io/ApiDbReader.cpp b/hoot-core/src/main/cpp/hoot/core/io/ApiDbReader.cpp
index e9d1cb7..036615c 100644
--- a/hoot-core/src/main/cpp/hoot/core/io/ApiDbReader.cpp
+++ b/hoot-core/src/main/cpp/hoot/core/io/ApiDbReader.cpp
@@ -33,7 +33,7 @@
#include <hoot/core/io/ApiDb.h>
#include <hoot/core/util/Log.h>
#include <hoot/core/util/StringUtils.h>
-#include <hoot/core/ops/MapCropper.h>
+#include <hoot/core/util/IoUtils.h>
// tgs
#include <tgs/System/Time.h>
@@ -62,7 +62,9 @@ _totalNumMapWays(0),
_totalNumMapRelations(0),
_numNodesRead(0),
_numWaysRead(0),
-_numRelationsRead(0)
+_numRelationsRead(0),
+_keepImmediatelyConnectedWaysOutsideBounds(
+ ConfigOptions().getConvertBoundingBoxKeepImmediatelyConnectedWaysOutsideBounds())
{
}
@@ -269,8 +271,6 @@ void ApiDbReader::_updateMetadataOnElement(ElementPtr element)
logWarnCount++;
}
}
- //I don't think OSM non-hoot metadata tags should be removed here...
- //tags.remove(MetadataTags::Accuracy());
}
}
@@ -285,8 +285,9 @@ bool ApiDbReader::_isValidBounds(const Envelope& bounds)
return true;
}
-void ApiDbReader::_readWaysByNodeIds(OsmMapPtr map, const QSet<QString>& nodeIds, QSet<QString>& wayIds,
- QSet<QString>& additionalNodeIds, long& nodeCount, long& wayCount)
+void ApiDbReader::_readWaysByNodeIds(OsmMapPtr map, const QSet<QString>& nodeIds,
+ QSet<QString>& wayIds, QSet<QString>& additionalNodeIds,
+ long& nodeCount, long& wayCount)
{
LOG_DEBUG("Retrieving way IDs referenced by the selected nodes...");
std::shared_ptr<QSqlQuery> wayIdItr = _getDatabase()->selectWayIdsByWayNodeIds(nodeIds);
@@ -297,14 +298,40 @@ void ApiDbReader::_readWaysByNodeIds(OsmMapPtr map, const QSet<QString>& nodeIds
wayIds.insert(QString::number(wayId));
}
LOG_VARD(wayIds.size());
+
if (wayIds.size() > 0)
{
+ // If the appropriate option is enabled, here we'll add the IDs of all ways that fall outside
+ // of the requested bounds but are directly connected to a way that is being returned by the
+ // query.
+ QSet<QString> connectedWayIds;
+ LOG_VARD(_keepImmediatelyConnectedWaysOutsideBounds);
+ if (_keepImmediatelyConnectedWaysOutsideBounds)
+ {
+ LOG_DEBUG("Retrieving way IDs immediately connected to the current ways...");
+ connectedWayIds = _getDatabase()->selectConnectedWayIds(wayIds);
+ LOG_VARD(connectedWayIds);
+ LOG_VARD(connectedWayIds.size());
+ wayIds.unite(connectedWayIds);
+ LOG_VARD(wayIds);
+ LOG_VARD(wayIds.size());
+ }
+
LOG_DEBUG("Retrieving ways by way ID...");
std::shared_ptr<QSqlQuery> wayItr =
_getDatabase()->selectElementsByElementIdList(wayIds, TableType::Way);
+ const bool tagConnectedWays =
+ ConfigOptions().getConvertBoundingBoxTagImmediatelyConnectedOutOfBoundsWays();
+ LOG_VARD(tagConnectedWays);
while (wayItr->next())
{
WayPtr way = _resultToWay(*wayItr, *map);
+ // If the appropriate option is enabled, we'll tag the connected out of bounds ways that we
+ // just identified. This may be helpful later when creating replacement changesets.
+ if (tagConnectedWays && connectedWayIds.contains(QString::number(way->getId())))
+ {
+ way->getTags().set(MetadataTags::HootConnectedWayOutsideBounds(), "yes");
+ }
map->addElement(way);
LOG_VART(way);
wayCount++;
@@ -320,7 +347,7 @@ void ApiDbReader::_readWaysByNodeIds(OsmMapPtr map, const QSet<QString>& nodeIds
additionalNodeIds.insert(QString::number(nodeId));
}
- //subtract nodeIds from additionalWayNodeIds so no dupes get added
+ //subtract nodeIds from additionalNodeIds, so no dupes get added
LOG_VARD(nodeIds.size());
LOG_VARD(additionalNodeIds.size());
additionalNodeIds = additionalNodeIds.subtract(nodeIds);
@@ -349,8 +376,10 @@ void ApiDbReader::_readByBounds(OsmMapPtr map, const Envelope& bounds)
long boundedWayCount = 0;
long boundedRelationCount = 0;
- LOG_DEBUG("Retrieving node records within the query bounds...");
+ LOG_DEBUG("Retrieving nodes within the query bounds...");
std::shared_ptr<QSqlQuery> nodeItr = _getDatabase()->selectNodesByBounds(bounds);
+ // Element IDs are stored as strings to allow inserting them directly into the select query
+ // strings.
QSet<QString> nodeIds;
while (nodeItr->next())
{
@@ -359,11 +388,11 @@ void ApiDbReader::_readByBounds(OsmMapPtr map, const Envelope& bounds)
LOG_VART(node);
map->addElement(node);
boundedNodeCount++;
- //Don't use the mapped id from the node object here, b/c we want don't want to use mapped ids
- //with any queries. Mapped ids may not exist yet.
+ // Don't use the mapped id from the node object here, b/c we want don't want to use mapped ids
+ // with any queries, because mapped ids may not exist yet.
const long nodeId = resultIterator.value(0).toLongLong();
LOG_VART(ElementId(ElementType::Node, nodeId));
- nodeIds.insert( QString::number(nodeId));
+ nodeIds.insert(QString::number(nodeId));
}
LOG_VARD(nodeIds.size());
@@ -376,6 +405,10 @@ void ApiDbReader::_readByBounds(OsmMapPtr map, const Envelope& bounds)
_readWaysByNodeIds(
map, nodeIds, wayIds, additionalWayNodeIds, boundedNodeCount, boundedWayCount);
nodeIds.unite(additionalWayNodeIds);
+ LOG_VARD(nodeIds.size());
+ LOG_VARD(wayIds);
+ LOG_VARD(wayIds.size());
+
LOG_DEBUG("Retrieving relation IDs referenced by the selected ways and nodes...");
QSet<QString> relationIds;
assert(nodeIds.size() > 0);
@@ -398,6 +431,7 @@ void ApiDbReader::_readByBounds(OsmMapPtr map, const Envelope& bounds)
}
}
LOG_VARD(relationIds.size());
+
// Iterate all relations (and sub-relations) that are "within" the bounds
QSet<QString> completedRelationIds;
while (relationIds.size() > 0)
@@ -405,6 +439,7 @@ void ApiDbReader::_readByBounds(OsmMapPtr map, const Envelope& bounds)
QSet<QString> newNodes;
QSet<QString> newWays;
QSet<QString> newRelations;
+
LOG_DEBUG("Retrieving relations by relation ID...");
std::shared_ptr<QSqlQuery> relationItr =
_getDatabase()->selectElementsByElementIdList(relationIds, TableType::Relation);
@@ -435,8 +470,10 @@ void ApiDbReader::_readByBounds(OsmMapPtr map, const Envelope& bounds)
// Keep track of all relations that have been added to the map so we don't try them again
completedRelationIds.insert(QString::number(relation->getId()));
}
+
// Clear the relations that we are iterating on, it is then filled with new relations later
relationIds.clear();
+
// Iterate any new nodes that are members of relations that need to be queried
newNodes = newNodes.subtract(nodeIds);
if (newNodes.size() > 0)
@@ -455,11 +492,13 @@ void ApiDbReader::_readByBounds(OsmMapPtr map, const Envelope& bounds)
nodeIds.insert(QString::number(nodeId));
}
}
+
// Iterate any new ways that are members of relations that need to be queried
newWays = newWays.subtract(wayIds);
if (newWays.size() > 0)
{
QSet<QString> additionalNodeIds;
+
LOG_DEBUG("Retrieving ways by way ID...");
std::shared_ptr<QSqlQuery> wayItr =
_getDatabase()->selectElementsByElementIdList(newWays, TableType::Way);
@@ -481,7 +520,7 @@ void ApiDbReader::_readByBounds(OsmMapPtr map, const Envelope& bounds)
additionalNodeIds.insert(QString::number(nodeId));
}
- //subtract nodeIds from additionalWayNodeIds so no dupes get added
+ //subtract nodeIds from additionalWayNodeIds, so no dupes get added
LOG_VARD(nodeIds.size());
LOG_VARD(additionalNodeIds.size());
additionalNodeIds = additionalNodeIds.subtract(nodeIds);
@@ -490,7 +529,8 @@ void ApiDbReader::_readByBounds(OsmMapPtr map, const Envelope& bounds)
if (additionalNodeIds.size() > 0)
{
LOG_DEBUG(
- "Retrieving nodes falling outside of the query bounds but belonging to a selected way...");
+ "Retrieving nodes falling outside of the query bounds but belonging to a selected " <<
+ "way...");
std::shared_ptr<QSqlQuery> additionalWayNodeItr =
_getDatabase()->selectElementsByElementIdList(additionalNodeIds, TableType::Node);
while (additionalWayNodeItr->next())
@@ -502,6 +542,7 @@ void ApiDbReader::_readByBounds(OsmMapPtr map, const Envelope& bounds)
}
}
}
+
// Get the set of new relations found minus anything that has already been completed
relationIds = newRelations.subtract(completedRelationIds);
}
@@ -517,25 +558,17 @@ void ApiDbReader::_readByBounds(OsmMapPtr map, const Envelope& bounds)
// The default behavior of the db bounded read is to return the entirety of features found
// within the bounds, even if sections of those features exist outside the bounds. Only run the
- // crop operation if the crop related option are different than the default behavior. Clearly, it
- // would be more efficient to run a different query to pull the features back the way we want
- // them from the start and skip this step completely. That's possibly something to look into
- // doing in the future.
+ // crop operation if the crop related options are different than the default behavior. Clearly, it
+ // would be more efficient to run a different set of initial queries to pull the features back
+ // already cropped the way we want them from the start and skip this step completely. That's
+ // possibly an optimization to look into doing in the future.
ConfigOptions conf;
if (!conf.getConvertBoundingBoxKeepEntireFeaturesCrossingBounds() ||
conf.getConvertBoundingBoxKeepOnlyFeaturesInsideBounds())
- {
- LOG_INFO("Applying bounds filtering to ingested data: " << _bounds << "...");
- MapCropper cropper(_bounds);
- LOG_INFO(cropper.getInitStatusMessage());
- // We don't reuse MapCropper's version of these options, since we want the freedom to have
- // different default values than what MapCropper uses.
- cropper.setKeepEntireFeaturesCrossingBounds(
- ConfigOptions().getConvertBoundingBoxKeepEntireFeaturesCrossingBounds());
- cropper.setKeepOnlyFeaturesInsideBounds(
- ConfigOptions().getConvertBoundingBoxKeepOnlyFeaturesInsideBounds());
- cropper.apply(map);
- LOG_INFO(cropper.getCompletedStatusMessage());
+ {
+ // We've already handled keeping immediately connected oob ways during the query, so don't need
+ // to worry about it here.
+ IoUtils::cropToBounds(map, _bounds);
}
LOG_DEBUG("Current map:");