Skip to content

Commit 7fe97ae

Browse files
authored
Optimize MapCropper (#5701)
* Ignore read cropping on JSON based outputs * Bulk remove ways in OsmMap * Member function argument description comment * Copyright
1 parent 89a4399 commit 7fe97ae

File tree

10 files changed

+88
-9
lines changed

10 files changed

+88
-9
lines changed

hoot-core/src/main/cpp/hoot/core/elements/OsmMap.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,38 @@ void OsmMap::replaceNodes(const std::map<long, long>& replacements)
651651
}
652652
}
653653

654+
void OsmMap::bulkRemoveWays(const std::vector<long>& way_ids, bool removeFully)
655+
{
656+
// Get a pointer to the element to relation map and then clear the index before working with the index again,
657+
// this will keep the index from being rebuilt with each removal of a way. Rebuild it once at the end of the
658+
// function
659+
std::shared_ptr<ElementToRelationMap> relationMap = _index->getElementToRelationMap();
660+
if (removeFully)
661+
_index->clearRelationIndex();
662+
663+
// Remove all of the ways
664+
for (auto& id : way_ids)
665+
{
666+
ElementId eid = ElementId::way(id);
667+
if (removeFully)
668+
{
669+
// Update all relations to remove references to this way
670+
const set<long>& relations = relationMap->getRelationByElement(eid);
671+
for (const auto& r : _relations)
672+
{
673+
if (relations.find(r.first) != relations.end())
674+
r.second->removeElement(eid);
675+
}
676+
}
677+
// Actually remove the way from the map and the index
678+
_index->removeWay(getWay(id));
679+
_ways.erase(id);
680+
}
681+
// Rebuild the relation map only one time
682+
if (removeFully)
683+
_index->getElementToRelationMap();
684+
}
685+
654686
void OsmMap::setProjection(const std::shared_ptr<OGRSpatialReference>& srs)
655687
{
656688
_srs = srs;

hoot-core/src/main/cpp/hoot/core/elements/OsmMap.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,15 @@ class OsmMap : public std::enable_shared_from_this<OsmMap>, public ElementProvid
213213
int numWaysAppended() const { return _numWaysAppended; }
214214
int numWaysSkippedForAppending() const { return _numWaysSkippedForAppending; }
215215

216+
/** Removing multiple ways using other methods will trigger the indices in this object
217+
* to be rebuilt between each delete operation which is expensive, this method will delete
218+
* all of the ways in one shot and rebuild the indices afterwards.
219+
* @param way_ids Vector of way IDs that are to be removed
220+
* @param removeFully When set to true, way IDs are removed from relations in the map too
221+
* when false, the way is removed from the map only, relations still reference the way ID
222+
*/
223+
void bulkRemoveWays(const std::vector<long>& way_ids, bool removeFully);
224+
216225
////////////////////////////////////////RELATION/////////////////////////////////////////////////
217226

218227
ConstRelationPtr getRelation(long id) const override;

hoot-core/src/main/cpp/hoot/core/io/DataConverter.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,11 @@ void DataConverter::convert(const QStringList& inputs, const QString& output)
109109
{
110110
_validateInput(inputs, output);
111111

112-
if (!IoUtils::areSupportedOgrFormats(inputs, true) && IoUtils::isSupportedOgrFormat(output))
112+
bool ogrInputs = IoUtils::areSupportedOgrFormats(inputs, true);
113+
bool ogrOutput = IoUtils::isSupportedOgrFormat(output);
114+
bool jsonOutput = IoUtils::isSupportedJsonFormat(output);
115+
116+
if (!ogrInputs && (ogrOutput || jsonOutput))
113117
_cropReadIfBounded = false;
114118

115119
_progress.setJobId(ConfigOptions().getJobId());

hoot-core/src/main/cpp/hoot/core/io/IoUtils.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ bool IoUtils::isSupportedOgrFormat(const QString& input, const bool allowDir)
106106
}
107107
}
108108

109+
bool IoUtils::isSupportedJsonFormat(const QString& input)
110+
{
111+
const QString inputLower = input.toLower();
112+
return inputLower.endsWith(".json") || inputLower.endsWith(".geojson");
113+
}
114+
109115
bool IoUtils::anyAreSupportedOgrFormats(const QStringList& inputs, const bool allowDir)
110116
{
111117
if (inputs.empty())

hoot-core/src/main/cpp/hoot/core/io/IoUtils.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ class IoUtils
7878
* @return true if the input is supported by OGR; false otherwise
7979
*/
8080
static bool isSupportedOgrFormat(const QString& input, const bool allowDir = false);
81+
/**
82+
* Returns true if the input format is a Hootenanny supported JSON format
83+
*
84+
* @param input input path
85+
* @return true if the input is a type of JSON; false otherwise
86+
*/
87+
static bool isSupportedJsonFormat(const QString& input);
8188
/**
8289
* Determines if a set of inputs paths are all OGR supported formats
8390
*

hoot-core/src/main/cpp/hoot/core/io/OsmGeoJsonWriter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ void OsmGeoJsonWriter::_writePartial(const ElementProviderPtr& provider, const C
314314
QString OsmGeoJsonWriter::_getBbox() const
315315
{
316316
Envelope bounds = CalculateMapBoundsVisitor::getGeosBounds(_map);
317+
if (_bounds)
318+
bounds = *_bounds->getEnvelopeInternal();
317319
return QString("[%1, %2, %3, %4]")
318320
.arg(QString::number(bounds.getMinX(), 'g', 5), QString::number(bounds.getMinY(), 'g', 5),
319321
QString::number(bounds.getMaxX(), 'g', 5), QString::number(bounds.getMaxY(), 'g', 5));

hoot-core/src/main/cpp/hoot/core/ops/MapCropper.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* This will properly maintain the copyright information. Maxar
2323
* copyrights will be updated automatically.
2424
*
25-
* @copyright Copyright (C) 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Maxar (http://www.maxar.com/)
25+
* @copyright Copyright (C) 2015-2023 Maxar (http://www.maxar.com/)
2626
*/
2727

2828
#include "MapCropper.h"
@@ -190,6 +190,10 @@ void MapCropper::apply(OsmMapPtr& map)
190190
LOG_VARD(_bounds->toString());
191191
LOG_VARD(_inclusionCrit.get());
192192

193+
// First iteration finds the elements to delete and crop
194+
vector<long> waysToRemove;
195+
vector<long> waysToRemoveFully;
196+
vector<long> waysToCrop;
193197
// go through all the ways
194198
long wayCtr = 0;
195199
// Make a copy because the map is modified below
@@ -253,9 +257,9 @@ void MapCropper::apply(OsmMapPtr& map)
253257
LOG_TRACE("Dropping wholly outside way: " << w->getElementId() << "...");
254258
// Removal is based on the parent setting, either remove it fully or leave it in the relation
255259
if (_removeFromParentRelation)
256-
RemoveWayByEid::removeWayFully(map, w->getId());
260+
waysToRemoveFully.emplace_back(w->getId());
257261
else
258-
RemoveWayByEid::removeWay(map, w->getId());
262+
waysToRemove.emplace_back(w->getId());
259263
_numWaysOutOfBounds++;
260264
_numAffected++;
261265
}
@@ -271,7 +275,7 @@ void MapCropper::apply(OsmMapPtr& map)
271275
{
272276
// Way isn't wholly inside and the configuration requires it to be, so remove the way.
273277
LOG_TRACE("Dropping due to _keepOnlyFeaturesInsideBounds=true: " << w->getElementId() << "...");
274-
RemoveWayByEid::removeWayFully(map, w->getId());
278+
waysToRemoveFully.emplace_back(w->getId());
275279
_numWaysOutOfBounds++;
276280
_numAffected++;
277281
}
@@ -280,7 +284,7 @@ void MapCropper::apply(OsmMapPtr& map)
280284
// Way crosses the boundary and we're not configured to keep ways that cross the bounds, so
281285
// do an expensive operation to decide how much to keep, if any.
282286
LOG_TRACE("Cropping due to _keepEntireFeaturesCrossingBounds=false: " << w->getElementId() << "...");
283-
_cropWay(map, w->getId());
287+
waysToCrop.emplace_back(w->getId());
284288
_numWaysCrossingThreshold++;
285289
}
286290
else
@@ -299,6 +303,17 @@ void MapCropper::apply(OsmMapPtr& map)
299303
StringUtils::formatLargeNumber(ways.size()) << " ways.");
300304
}
301305
}
306+
307+
// Bulk remove ways from map and relations too
308+
map->bulkRemoveWays(waysToRemoveFully, true);
309+
310+
// Bulk remove ways from map only
311+
map->bulkRemoveWays(waysToRemove, false);
312+
313+
// Iterate the ways that cross the bounds and crop
314+
for (auto id : waysToCrop)
315+
_cropWay(map, id);
316+
302317
LOG_VARD(map->size());
303318
OsmMapWriterFactory::writeDebugMap(map, className(), "after-way-removal");
304319

hoot-core/src/main/cpp/hoot/core/visitors/SchemaTranslationVisitor.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* This will properly maintain the copyright information. Maxar
2323
* copyrights will be updated automatically.
2424
*
25-
* @copyright Copyright (C) 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Maxar (http://www.maxar.com/)
25+
* @copyright Copyright (C) 2015-2023 Maxar (http://www.maxar.com/)
2626
*/
2727

2828
#include "SchemaTranslationVisitor.h"
@@ -95,6 +95,10 @@ void SchemaTranslationVisitor::setTranslationScript(QString path)
9595

9696
void SchemaTranslationVisitor::visit(const ElementPtr& e)
9797
{
98+
// Don't attempt translation without a translator
99+
if (!_translator)
100+
return;
101+
// Filter the elements
98102
if (e.get() && e->getTags().getNonDebugCount() > 0 &&
99103
(_elementStatusFilter == Status::Invalid || e->getStatus() == _elementStatusFilter))
100104
{
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{"generator":"Hootenanny","type":"FeatureCollection","bbox":[-76.621, 39.284, -76.614, 39.287],"features": [
1+
{"generator":"Hootenanny","type":"FeatureCollection","bbox":[-76.62, 39.286, -76.615, 39.287],"features": [
22
{"type":"Feature","properties":{"ARA":"41183.0","FCSUBTYPE":"100083","F_CODE":"AL013","ZI005_FNA":"Building Test","ZI026_CTUC":"5"},"geometry": {"type":"MultiPolygon","coordinates": [[[[-76.6190305379, 39.2860467208],[-76.61874622374999, 39.28605710088],[-76.61875695258, 39.28620449776],[-76.61806762486999, 39.28623148588],[-76.61805421382, 39.28609446913],[-76.61762506038001, 39.28611107723],[-76.61760888352043, 39.28582831499],[-76.61901334434501, 39.28582831499],[-76.6190305379, 39.2860467208]]],[[[-76.61730051309, 39.28602388464],[-76.61600232393, 39.28606955696],[-76.61602914602, 39.28634151429],[-76.61542296678, 39.28636435036],[-76.61538129093391, 39.28582831499],[-76.61728816009655, 39.28582831499],[-76.61730051309, 39.28602388464]]]]}}
33
]
44
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{"generator":"Hootenanny","type":"FeatureCollection","bbox":[-76.621, 39.284, -76.614, 39.287],"features": [
1+
{"generator":"Hootenanny","type":"FeatureCollection","bbox":[-76.62, 39.286, -76.615, 39.287],"features": [
22
{"type":"Feature","properties":{"FCSUBTYPE":"100152","F_CODE":"AP030","LZN":"1118.6","OSMTAGS":{"highway":"primary"},"RIN_ROI":"3","RMWC":"5","RTY":"3","SGCC":"5","ZI005_FNA":"Highway Test","ZI016_WTC":"1","ZI026_CTUC":"5"},"geometry": {"type":"MultiLineString","coordinates": [[[-76.61972935029, 39.28622613963066],[-76.61925316929999, 39.28625017619],[-76.61922038510578, 39.28582831499]],[[-76.6148605218813, 39.28582831499],[-76.61492944837001, 39.28646608078],[-76.61460649762, 39.28648516172767]]]}}
33
]
44
}

0 commit comments

Comments
 (0)