From d33ceb92f0f432b725150521c8efd5f135f91a02 Mon Sep 17 00:00:00 2001 From: Ivan Paden Date: Fri, 27 Oct 2023 05:15:04 +0200 Subject: [PATCH 1/7] Add fallback for failing reconstruction of imported buildings --- src/Building.cpp | 4 +++ src/Building.h | 1 + src/ImportedBuilding.cpp | 8 ++++++ src/Map3d.cpp | 46 ++++++++++++++++++++++++++--------- src/Map3d.h | 1 + src/PolyFeature.cpp | 6 +++++ src/PolyFeature.h | 1 + src/ReconstructedBuilding.cpp | 9 +++++++ src/ReconstructedBuilding.h | 1 + 9 files changed, 66 insertions(+), 11 deletions(-) diff --git a/src/Building.cpp b/src/Building.cpp index f2dd75fa..b5060cec 100644 --- a/src/Building.cpp +++ b/src/Building.cpp @@ -275,6 +275,10 @@ double Building::sq_max_dim() { return *(std::max_element(dims.begin(), dims.end())); } +PointSet3Ptr Building::get_points() const { + return _ptsPtr; +} + void Building::get_cityjson_info(nlohmann::json& b) const { b["type"] = "Building"; // b["attributes"]; diff --git a/src/Building.h b/src/Building.h index d0a1b1c9..70db1502 100644 --- a/src/Building.h +++ b/src/Building.h @@ -59,6 +59,7 @@ class Building : public PolyFeature { bool has_self_intersections() const; void set_to_zero_terrain(); double sq_max_dim(); + PointSet3Ptr get_points() const; virtual void get_cityjson_info(nlohmann::json& b) const override; virtual void get_cityjson_semantics(nlohmann::json& g) const override; diff --git a/src/ImportedBuilding.cpp b/src/ImportedBuilding.cpp index 6a4190e9..75e9d6b9 100644 --- a/src/ImportedBuilding.cpp +++ b/src/ImportedBuilding.cpp @@ -339,6 +339,14 @@ void ImportedBuilding::reconstruct() { PMP::polygon_soup_to_polygon_mesh(points, polygons, _mesh); PMP::triangulate_faces(_mesh); + if (this->get_height() < Config::get().minHeight) { + this->deactivate(); + // Store points to ptsPtr so that it might be used for LoD1 reconstruction + for (auto& pt : _ptMap) _ptsPtr->insert(pt.second); + throw std::runtime_error("Importing failed. It could be that the height is lower than minimum, or the " + "mesh connectivity is broken. Trying to reconstruct LoD1 from building points"); + } + /* Mesh wrap; const double relative_alpha = 300.; diff --git a/src/Map3d.cpp b/src/Map3d.cpp index 20267adf..b2d28da0 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -333,10 +333,23 @@ void Map3d::reconstruct_buildings() { << ". If I cannot find a geometry with that LoD, I will reconstruct in the highest LoD available" << std::endl; } + std::cout << "Total number of _buildingsPtr: " << _buildingsPtr.size() << std::endl; + this->reconstruct_buildings(_buildingsPtr); + std::cout << "Total number of _buildingsPtr: " << _buildingsPtr.size() << std::endl; + // Gather failed reconstructions int failed = 0; + for (auto& b : _buildingsPtr) if (b->has_failed_to_reconstruct()) ++failed; + std::cout << " Number of successfully reconstructed buildings: " << _buildingsPtr.size() - failed << std::endl; + Config::get().logSummary << "Building reconstruction summary: successfully reconstructed buildings: " + << _buildingsPtr.size() - failed << std::endl; + Config::get().logSummary << " num of failed reconstructions: " + << failed << std::endl; +} + +void Map3d::reconstruct_buildings(BuildingsPtr& buildings) { + BuildingsPtr newBuildingsToReconstruct; #pragma omp parallel for - for (auto& f : _buildingsPtr) { - if (!f->is_active()) continue; + for (auto& f : buildings) { try { f->reconstruct(); //-- In case of hybrid boolean/constraining reconstruction @@ -345,20 +358,31 @@ void Map3d::reconstruct_buildings() { f->reconstruct(); } } catch (std::exception& e) { - #pragma omp atomic - ++failed; + if (f->is_imported()) { + // try to recover by reconstructing LoD1 from geometry pts + auto importToReconstructBuild = + std::make_shared(std::static_pointer_cast(f)); + _reconstructedBuildingsPtr.push_back(importToReconstructBuild); + _allFeaturesPtr.push_back(importToReconstructBuild); + _buildingsPtr.push_back(importToReconstructBuild); + newBuildingsToReconstruct.push_back(importToReconstructBuild); + } else { + // mark for geojson output + f->mark_as_failed(); + } // add information to log file Config::write_to_log("Building ID: " + f->get_id() + " Failed to reconstruct. Reason: " + e.what()); - // mark for geojson output - f->mark_as_failed(); } } this->clear_inactives(); - std::cout << " Number of successfully reconstructed buildings: " << _buildingsPtr.size() << std::endl; - Config::get().logSummary << "Building reconstruction summary: successfully reconstructed buildings: " - << _buildingsPtr.size() - failed << std::endl; - Config::get().logSummary << " num of failed reconstructions: " - << failed << std::endl; + /* + for (auto& b : newBuildingsToReconstruct) { + _reconstructedBuildingsPtr.push_back(std::static_pointer_cast(b)); + _allFeaturesPtr.push_back(b); + _buildingsPtr.push_back(b); + } + */ + if (!newBuildingsToReconstruct.empty()) this->reconstruct_buildings(newBuildingsToReconstruct); } void Map3d::reconstruct_boundaries() { diff --git a/src/Map3d.h b/src/Map3d.h index 17c4828d..8309d719 100644 --- a/src/Map3d.h +++ b/src/Map3d.h @@ -80,6 +80,7 @@ class Map3d { void remove_extra_terrain_pts(); void reconstruct_terrain(); void reconstruct_buildings(); + void reconstruct_buildings(BuildingsPtr& buildings); void reconstruct_boundaries(); void reconstruct_with_flat_terrain(); void solve_building_conflicts(); diff --git a/src/PolyFeature.cpp b/src/PolyFeature.cpp index ba3c09f2..fa341e62 100644 --- a/src/PolyFeature.cpp +++ b/src/PolyFeature.cpp @@ -309,6 +309,12 @@ const Polygon_with_holes_2& PolyFeature::get_poly() const { return _poly; } +Polygon_with_attr PolyFeature::get_poly_w_attr() const { + Polygon_with_attr poly; + poly.polygon = _poly; + return poly; +} + const std::vector>& PolyFeature::get_ground_elevations() const { return _groundElevations; } diff --git a/src/PolyFeature.h b/src/PolyFeature.h index e6ec815f..5b85abdb 100644 --- a/src/PolyFeature.h +++ b/src/PolyFeature.h @@ -68,6 +68,7 @@ class PolyFeature : public TopoFeature { Polygon_with_holes_2& get_poly(); const Polygon_with_holes_2& get_poly() const; + Polygon_with_attr get_poly_w_attr() const; const std::vector>& get_ground_elevations() const; const int get_internal_id() const; MinBbox& get_min_bbox(); diff --git a/src/ReconstructedBuilding.cpp b/src/ReconstructedBuilding.cpp index 3b920478..5f3260e8 100644 --- a/src/ReconstructedBuilding.cpp +++ b/src/ReconstructedBuilding.cpp @@ -29,6 +29,7 @@ #include "geomutils.h" #include "LoD12.h" +#include "ImportedBuilding.h" ReconstructedBuilding::ReconstructedBuilding() : Building(), _attributeHeight(-global::largnum), @@ -109,6 +110,14 @@ ReconstructedBuilding::ReconstructedBuilding(const Polygon_with_attr& poly, cons } } +ReconstructedBuilding::ReconstructedBuilding(const std::shared_ptr& importedBuilding) + : Building(importedBuilding->get_poly_w_attr(), importedBuilding->get_internal_id()), + _attributeHeight(-global::largnum), _attributeHeightAdvantage(Config::get().buildingHeightAttrAdv) { + _ptsPtr = importedBuilding->get_points(); + _groundElevations = importedBuilding->get_ground_elevations(); + _id = importedBuilding->get_id(); +} + ReconstructedBuilding::~ReconstructedBuilding() = default; /* diff --git a/src/ReconstructedBuilding.h b/src/ReconstructedBuilding.h index 3536186b..47079890 100644 --- a/src/ReconstructedBuilding.h +++ b/src/ReconstructedBuilding.h @@ -38,6 +38,7 @@ class ReconstructedBuilding : public Building { // ReconstructedBuilding(const nlohmann::json& poly); ReconstructedBuilding(const nlohmann::json& poly, const int internalID); ReconstructedBuilding(const Polygon_with_attr& poly, const int internalID); + ReconstructedBuilding(const std::shared_ptr& importedBuilding); ~ReconstructedBuilding(); virtual double get_elevation() override; From c44015cc87d3ebf439a3ca5608cf570b3d2aa202 Mon Sep 17 00:00:00 2001 From: Ivan Paden Date: Fri, 27 Oct 2023 05:26:16 +0200 Subject: [PATCH 2/7] Remove extra --- src/Map3d.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/Map3d.cpp b/src/Map3d.cpp index b2d28da0..9fd8a07a 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -333,9 +333,7 @@ void Map3d::reconstruct_buildings() { << ". If I cannot find a geometry with that LoD, I will reconstruct in the highest LoD available" << std::endl; } - std::cout << "Total number of _buildingsPtr: " << _buildingsPtr.size() << std::endl; this->reconstruct_buildings(_buildingsPtr); - std::cout << "Total number of _buildingsPtr: " << _buildingsPtr.size() << std::endl; // Gather failed reconstructions int failed = 0; for (auto& b : _buildingsPtr) if (b->has_failed_to_reconstruct()) ++failed; @@ -375,13 +373,6 @@ void Map3d::reconstruct_buildings(BuildingsPtr& buildings) { } } this->clear_inactives(); - /* - for (auto& b : newBuildingsToReconstruct) { - _reconstructedBuildingsPtr.push_back(std::static_pointer_cast(b)); - _allFeaturesPtr.push_back(b); - _buildingsPtr.push_back(b); - } - */ if (!newBuildingsToReconstruct.empty()) this->reconstruct_buildings(newBuildingsToReconstruct); } From b6712203379e9a8d509f48c4f83d1c5c160d98a4 Mon Sep 17 00:00:00 2001 From: Ivan Paden Date: Fri, 27 Oct 2023 06:46:10 +0200 Subject: [PATCH 3/7] Sort out PolyFeature ID situation --- src/Building.cpp | 14 ------------ src/Building.h | 3 --- src/ImportedBuilding.cpp | 32 +++++++++++++--------------- src/ImportedBuilding.h | 6 ++---- src/Map3d.cpp | 17 ++++++--------- src/PolyFeature.cpp | 40 +++++++++-------------------------- src/PolyFeature.h | 8 +++---- src/ReconstructedBuilding.cpp | 18 ++++++---------- src/ReconstructedBuilding.h | 5 ++--- 9 files changed, 46 insertions(+), 97 deletions(-) diff --git a/src/Building.cpp b/src/Building.cpp index b5060cec..5f1e21ba 100644 --- a/src/Building.cpp +++ b/src/Building.cpp @@ -42,30 +42,16 @@ Building::Building() : PolyFeature(1), _elevation(-global::largnum), _height(-global::largnum), _ptsPtr(std::make_shared()), _hasFailed(false) {} -Building::Building(const int internalID) - : PolyFeature(1, internalID), _elevation(-global::largnum), _height(-global::largnum), - _ptsPtr(std::make_shared()), _hasFailed(false) {} - Building::Building(const nlohmann::json& poly) : PolyFeature(poly, true, 1), _elevation(-global::largnum), _height(-global::largnum), _ptsPtr(std::make_shared()), _hasFailed(false) {} // 'true' here to check for polygon simplicity -Building::Building(const nlohmann::json& poly, const int internalID) - : PolyFeature(poly, true, 1, internalID), _elevation(-global::largnum), _height(-global::largnum), - _ptsPtr(std::make_shared()), _hasFailed(false) {} - // 'true' here to check for polygon simplicity - Building::Building(const Polygon_with_attr& poly) : PolyFeature(poly, true, 1), _elevation(-global::largnum), _height(-global::largnum), _ptsPtr(std::make_shared()), _hasFailed(false) {} // 'true' here to check for polygon simplicity -Building::Building(const Polygon_with_attr& poly, const int internalID) - : PolyFeature(poly, true, 1, internalID), _elevation(-global::largnum), _height(-global::largnum), - _ptsPtr(std::make_shared()), _hasFailed(false) {} - // 'true' here to check for polygon simplicity - Building::~Building() = default; void Building::insert_point(const Point_3& pt) { diff --git a/src/Building.h b/src/Building.h index 70db1502..58fcb1bf 100644 --- a/src/Building.h +++ b/src/Building.h @@ -34,11 +34,8 @@ class Building : public PolyFeature { public: Building(); - Building(const int internalID); Building(const nlohmann::json& poly); - Building(const nlohmann::json& poly, const int internalID); Building(const Polygon_with_attr& poly); - Building(const Polygon_with_attr& poly, const int internalID); ~Building(); static void alpha_wrap(const BuildingsPtr& buildings, Mesh& newMesh); diff --git a/src/ImportedBuilding.cpp b/src/ImportedBuilding.cpp index 75e9d6b9..4e7a8ce0 100644 --- a/src/ImportedBuilding.cpp +++ b/src/ImportedBuilding.cpp @@ -40,14 +40,14 @@ int ImportedBuilding::noBottom = 0; -ImportedBuilding::ImportedBuilding(std::unique_ptr& buildingJson, PointSet3Ptr& importedBuildingPts, const int internalID) - : Building(internalID), _buildingJson(std::move(buildingJson)), - _footprintIdxList(), _parentBuildingID(), _ptMap(), - _appendToBuilding(false), _lodIdx(-1), _footprintPtsIdxList(), _trueHeight(Config::get().importTrueHeight) { +ImportedBuilding::ImportedBuilding(std::unique_ptr& buildingJson, PointSet3Ptr& importedBuildingPts) + : Building(), _buildingJson(std::move(buildingJson)), + _footprintIdxList(), _ptMap(), _appendToBuilding(false), + _lodIdx(-1), _footprintPtsIdxList(), _trueHeight(Config::get().importTrueHeight) { _f_imported = true; // the flag is here to avoid shorten polygons later. todo to fix - //-- Get parent building ID - _parentBuildingID = (*_buildingJson)["parents"].front(); + //-- ID is the partent building ID + _id = (*_buildingJson)["parents"].front(); //-- Define LoD std::map lodGeomLst; @@ -124,7 +124,7 @@ ImportedBuilding::ImportedBuilding(std::unique_ptr& buildingJson pointConnectivity[IO::gen_key_bucket(Point_2(_ptMap.at(ptIdx).x(), _ptMap.at(ptIdx).y()))] = ptIdx; } if (!facePoly.is_simple()) { - Config::write_to_log("Failed to import building: " + this->get_parent_building_id() + Config::write_to_log("Failed to import building: " + this->get_id() + " Reason: Footprint polygon is not simple."); this->deactivate(); return; @@ -141,12 +141,13 @@ ImportedBuilding::ImportedBuilding(std::unique_ptr& buildingJson this->set_footprint_mesh_connectivity(pointConnectivity); } -ImportedBuilding::ImportedBuilding(Mesh& mesh, const int internalID) - : Building(internalID), _buildingJson(std::make_unique()), - _footprintIdxList(), _parentBuildingID(), _appendToBuilding(false), _ptMap(), +ImportedBuilding::ImportedBuilding(Mesh& mesh) + : Building(), _buildingJson(std::make_unique()), + _footprintIdxList(), _appendToBuilding(false), _ptMap(), _lodIdx(-1), _footprintPtsIdxList(), _trueHeight(Config::get().importTrueHeight) { _f_imported = true; // the flag is here to avoid shorten polygons later. todo to fix + _id = std::to_string(_polyInternalID); //-- Get the polygon from the building bottom // group faces pointing down CGAL::Vector_3 downVector(0, 0, -1); @@ -343,8 +344,9 @@ void ImportedBuilding::reconstruct() { this->deactivate(); // Store points to ptsPtr so that it might be used for LoD1 reconstruction for (auto& pt : _ptMap) _ptsPtr->insert(pt.second); - throw std::runtime_error("Importing failed. It could be that the height is lower than minimum, or the " - "mesh connectivity is broken. Trying to reconstruct LoD1 from building points"); + throw std::runtime_error("Importing failed. It could be that the height is" + "\n lower than minimum, or the mesh connectivity is broken." + "\n Trying to reconstruct LoD1.2 from building points"); } /* @@ -417,10 +419,6 @@ const nlohmann::json& ImportedBuilding::get_building_json() const { return *_buildingJson; } -const std::string& ImportedBuilding::get_parent_building_id() const { - return _parentBuildingID; -} - const int ImportedBuilding::get_lod_idx() const { return _lodIdx; } @@ -431,7 +429,7 @@ const bool ImportedBuilding::is_appending() const { void ImportedBuilding::check_simplicity(Polygon_2& ring) { if (!ring.is_simple()) { - Config::write_to_log("Failed to import building: " + this->get_parent_building_id() + Config::write_to_log("Failed to import building: " + this->get_id() + " Reason: Footprint polygon is not simple."); this->deactivate(); return; diff --git a/src/ImportedBuilding.h b/src/ImportedBuilding.h index c30ee3b2..bfbce2e5 100644 --- a/src/ImportedBuilding.h +++ b/src/ImportedBuilding.h @@ -38,8 +38,8 @@ class ImportedBuilding : public Building { ImportedBuilding() = delete; ImportedBuilding(std::unique_ptr& buildingJson, - PointSet3Ptr& importedBuildingPts, const int internalID); - ImportedBuilding(Mesh& mesh, const int internalID); + PointSet3Ptr& importedBuildingPts); + ImportedBuilding(Mesh& mesh); ~ImportedBuilding(); virtual double get_elevation() override; @@ -49,7 +49,6 @@ class ImportedBuilding : public Building { void append_nonground_part(const std::shared_ptr& other); const nlohmann::json& get_building_json() const; - const std::string& get_parent_building_id() const; const int get_lod_idx() const; const bool is_appending() const; @@ -61,7 +60,6 @@ class ImportedBuilding : public Building { std::unique_ptr _buildingJson; std::vector _footprintIdxList; std::vector> _footprintPtsIdxList; - std::string _parentBuildingID; bool _appendToBuilding; bool _trueHeight; int _lodIdx; diff --git a/src/Map3d.cpp b/src/Map3d.cpp index 9fd8a07a..17ee023e 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -106,9 +106,8 @@ void Map3d::set_features() { //-- Add features - order in _allFeaturesPtr defines the advantage in marking terrain polygons //- Buildings - int internalID = 0; for (auto& poly : _polygonsBuildings) { - auto building = std::make_shared(*poly, internalID++); + auto building = std::make_shared(*poly); _reconstructedBuildingsPtr.push_back(building); _buildingsPtr.push_back(building); _allFeaturesPtr.push_back(building); @@ -119,10 +118,8 @@ void Map3d::set_features() { std::cout << "Importing CityJSON geometries" << std::endl; std::vector> appendingBuildings; - internalID = 0; for (auto& importedBuilding: _importedBuildingsJSON) { - auto explicitCityJSONGeom = std::make_shared(importedBuilding, _importedBuildingsPts, - internalID++); + auto explicitCityJSONGeom = std::make_shared(importedBuilding, _importedBuildingsPts); if (!explicitCityJSONGeom->is_appending()) { _importedBuildingsPtr.push_back(explicitCityJSONGeom); _buildingsPtr.push_back(explicitCityJSONGeom); @@ -134,7 +131,7 @@ void Map3d::set_features() { //- Check for building parts that do not have footprint and append to another instance of the same building for (auto& b: appendingBuildings) { for (auto& importedBuilding: _importedBuildingsPtr) { - if (b->get_parent_building_id() == importedBuilding->get_parent_building_id()) { + if (b->get_id() == importedBuilding->get_id()) { importedBuilding->append_nonground_part(b); break; } @@ -145,7 +142,7 @@ void Map3d::set_features() { } else if (!_importedBuildingsOther.empty()) { std::cout << "Importing geometries" << std::endl; for (auto& mesh : _importedBuildingsOther) { - auto explicitOBJGeom = std::make_shared(mesh, internalID++); + auto explicitOBJGeom = std::make_shared(mesh); _importedBuildingsPtr.push_back(explicitOBJGeom); _buildingsPtr.push_back(explicitOBJGeom); _allFeaturesPtr.push_back(explicitOBJGeom); @@ -563,14 +560,14 @@ void Map3d::clear_inactives() { for (auto& importedBuilding: _importedBuildingsPtr) { if (!importedBuilding->is_active()) { auto it = std::find(inactiveBuildingIdxs.begin(), inactiveBuildingIdxs.end(), - importedBuilding->get_parent_building_id()); + importedBuilding->get_id()); if (it == inactiveBuildingIdxs.end()) - inactiveBuildingIdxs.push_back(importedBuilding->get_parent_building_id()); + inactiveBuildingIdxs.push_back(importedBuilding->get_id()); } } for (unsigned long i = 0; i < _importedBuildingsPtr.size();) { auto it = std::find(inactiveBuildingIdxs.begin(), inactiveBuildingIdxs.end(), - _importedBuildingsPtr[i]->get_parent_building_id()); + _importedBuildingsPtr[i]->get_id()); if (it == inactiveBuildingIdxs.end()) ++i; else { _importedBuildingsPtr[i]->deactivate(); diff --git a/src/PolyFeature.cpp b/src/PolyFeature.cpp index fa341e62..bc556234 100644 --- a/src/PolyFeature.cpp +++ b/src/PolyFeature.cpp @@ -37,19 +37,15 @@ #endif PolyFeature::PolyFeature() - : TopoFeature(), _poly(), _groundElevations(), _polyInternalID(), + : TopoFeature(), _poly(), _groundElevations(), _polyInternalID(new_internal_id()), _groundElevation(-global::largnum), _minBbox() {} PolyFeature::PolyFeature(const int outputLayerID) - : TopoFeature(outputLayerID), _poly(), _groundElevations(), _polyInternalID(), + : TopoFeature(outputLayerID), _poly(), _groundElevations(), _polyInternalID(new_internal_id()), _groundElevation(-global::largnum), _minBbox() {} -PolyFeature::PolyFeature(const int outputLayerID, const int internalID) - : TopoFeature(outputLayerID), _groundElevations(), _polyInternalID(internalID), - _groundElevation(-global::largnum), _minBbox() {} - PolyFeature::PolyFeature(const nlohmann::json& poly, const bool checkSimplicity) - : TopoFeature(), _groundElevations(), _polyInternalID(), + : TopoFeature(), _groundElevations(), _polyInternalID(new_internal_id()), _groundElevation(-global::largnum), _minBbox() { this->parse_json_poly(poly, checkSimplicity); } @@ -63,19 +59,8 @@ PolyFeature::PolyFeature(const nlohmann::json& poly, const bool checkSimplicity, PolyFeature::PolyFeature(const nlohmann::json& poly, const int outputLayerID) : PolyFeature(poly, false, outputLayerID) {} -PolyFeature::PolyFeature(const nlohmann::json& poly, const bool checkSimplicity, - const int outputLayerID, const int internalID) - : PolyFeature(poly, checkSimplicity) { - _polyInternalID = internalID; - _outputLayerID = outputLayerID; - if (_outputLayerID >= _numOfOutputLayers) _numOfOutputLayers = _outputLayerID + 1; -} - -PolyFeature::PolyFeature(const nlohmann::json& poly, const int outputLayerID, const int internalID) - : PolyFeature(poly, false, outputLayerID, internalID) {} - PolyFeature::PolyFeature(const Polygon_with_attr& poly, const bool checkSimplicity) - : TopoFeature(), _groundElevations(), _polyInternalID(), + : TopoFeature(), _groundElevations(), _polyInternalID(new_internal_id()), _groundElevation(-global::largnum), _minBbox() { bool isOuterRing = true; for (auto& ring : poly.polygon.rings()) { @@ -122,19 +107,10 @@ PolyFeature::PolyFeature(const Polygon_with_attr& poly, const bool checkSimplici PolyFeature::PolyFeature(const Polygon_with_attr& poly, const int outputLayerID) : PolyFeature(poly, false, outputLayerID) {} -PolyFeature::PolyFeature(const Polygon_with_attr& poly, const bool checkSimplicity, - const int outputLayerID, const int internalID) - : PolyFeature(poly, checkSimplicity) { - _polyInternalID = internalID; - _outputLayerID = outputLayerID; - if (_outputLayerID >= _numOfOutputLayers) _numOfOutputLayers = _outputLayerID + 1; -} - -PolyFeature::PolyFeature(const Polygon_with_attr& poly, const int outputLayerID, const int internalID) - : PolyFeature(poly, false, outputLayerID, internalID) {} - PolyFeature::~PolyFeature() = default; +int PolyFeature::_numOfPolyFeatures = 0; + void PolyFeature::calc_footprint_elevation_nni(const DT& dt) { typedef std::vector> Point_coordinate_vector; DT::Face_handle fh = nullptr; @@ -301,6 +277,10 @@ void PolyFeature::clear_feature() { _mesh.clear(); } +int PolyFeature::new_internal_id() { + return ++_numOfPolyFeatures; +} + Polygon_with_holes_2& PolyFeature::get_poly() { return _poly; } diff --git a/src/PolyFeature.h b/src/PolyFeature.h index 5b85abdb..fa2ecc31 100644 --- a/src/PolyFeature.h +++ b/src/PolyFeature.h @@ -34,17 +34,12 @@ class PolyFeature : public TopoFeature { public: PolyFeature(); PolyFeature(const int outputLayerID); - PolyFeature(const int outputLayerID, const int internalID); PolyFeature(const nlohmann::json& poly, const bool checkSimplicity = false); PolyFeature(const nlohmann::json& poly, const bool checkSimplicity, const int outputLayerID); - PolyFeature(const nlohmann::json& poly, const bool checkSimplicity, const int outputLayerID, const int internalID); PolyFeature(const nlohmann::json& poly, const int outputLayerID); - PolyFeature(const nlohmann::json& poly, const int outputLayerID, const int internalID); PolyFeature(const Polygon_with_attr& poly, const bool checkSimplicity = false); PolyFeature(const Polygon_with_attr& poly, const bool checkSimplicity, const int outputLayerID); - PolyFeature(const Polygon_with_attr& poly, const bool checkSimplicity, const int outputLayerID, const int internalID); PolyFeature(const Polygon_with_attr& poly, const int outputLayerID); - PolyFeature(const Polygon_with_attr& poly, const int outputLayerID, const int internalID); virtual ~PolyFeature(); void calc_footprint_elevation_nni(const DT& dt); @@ -74,12 +69,15 @@ class PolyFeature : public TopoFeature { MinBbox& get_min_bbox(); protected: + static int _numOfPolyFeatures; + int _polyInternalID; Polygon_with_holes_2 _poly; std::vector> _groundElevations; double _groundElevation; MinBbox _minBbox; + int new_internal_id(); void parse_json_poly(const nlohmann::json& poly, const bool checkSimplicity); }; diff --git a/src/ReconstructedBuilding.cpp b/src/ReconstructedBuilding.cpp index 5f3260e8..9fa71cc5 100644 --- a/src/ReconstructedBuilding.cpp +++ b/src/ReconstructedBuilding.cpp @@ -35,10 +35,6 @@ ReconstructedBuilding::ReconstructedBuilding() : Building(), _attributeHeight(-global::largnum), _attributeHeightAdvantage(Config::get().buildingHeightAttrAdv) {} -ReconstructedBuilding::ReconstructedBuilding(const int internalID) - : Building(internalID), _attributeHeight(-global::largnum), - _attributeHeightAdvantage(Config::get().buildingHeightAttrAdv) {} - ReconstructedBuilding::ReconstructedBuilding(const Mesh& mesh) : ReconstructedBuilding() { _mesh = mesh; @@ -61,13 +57,13 @@ ReconstructedBuilding::ReconstructedBuilding(const nlohmann::json& poly) } */ -ReconstructedBuilding::ReconstructedBuilding(const nlohmann::json& poly, const int internalID) - : Building(poly, internalID), _attributeHeight(-global::largnum), +ReconstructedBuilding::ReconstructedBuilding(const nlohmann::json& poly) + : Building(poly), _attributeHeight(-global::largnum), _attributeHeightAdvantage(Config::get().buildingHeightAttrAdv) { if (!Config::get().buildingUniqueId.empty() && poly["properties"].contains(Config::get().buildingUniqueId)) { _id = poly["properties"][Config::get().buildingUniqueId].dump(); } else { - _id = std::to_string(internalID); + _id = std::to_string(_polyInternalID); } if (poly["properties"].contains(Config::get().buildingHeightAttribute)) { if (poly["properties"][Config::get().buildingHeightAttribute].is_number()) { @@ -85,15 +81,15 @@ ReconstructedBuilding::ReconstructedBuilding(const nlohmann::json& poly, const i } } -ReconstructedBuilding::ReconstructedBuilding(const Polygon_with_attr& poly, const int internalID) - : Building(poly, internalID), _attributeHeight(-global::largnum), +ReconstructedBuilding::ReconstructedBuilding(const Polygon_with_attr& poly) + : Building(poly), _attributeHeight(-global::largnum), _attributeHeightAdvantage(Config::get().buildingHeightAttrAdv) { // Check for the polygon ID attribute auto idIt = poly.attributes.find(Config::get().buildingUniqueId); if (idIt != poly.attributes.end()) { _id = idIt->second; } else { - _id = std::to_string(internalID); + _id = std::to_string(_polyInternalID); } // Check for the building height attribute auto buildingHeightAttrIt = poly.attributes.find(Config::get().buildingHeightAttribute); @@ -111,7 +107,7 @@ ReconstructedBuilding::ReconstructedBuilding(const Polygon_with_attr& poly, cons } ReconstructedBuilding::ReconstructedBuilding(const std::shared_ptr& importedBuilding) - : Building(importedBuilding->get_poly_w_attr(), importedBuilding->get_internal_id()), + : Building(importedBuilding->get_poly_w_attr()), _attributeHeight(-global::largnum), _attributeHeightAdvantage(Config::get().buildingHeightAttrAdv) { _ptsPtr = importedBuilding->get_points(); _groundElevations = importedBuilding->get_ground_elevations(); diff --git a/src/ReconstructedBuilding.h b/src/ReconstructedBuilding.h index 47079890..a7db2364 100644 --- a/src/ReconstructedBuilding.h +++ b/src/ReconstructedBuilding.h @@ -33,11 +33,10 @@ class ReconstructedBuilding : public Building { public: ReconstructedBuilding(); - ReconstructedBuilding(const int internalID); ReconstructedBuilding(const Mesh& mesh); // ReconstructedBuilding(const nlohmann::json& poly); - ReconstructedBuilding(const nlohmann::json& poly, const int internalID); - ReconstructedBuilding(const Polygon_with_attr& poly, const int internalID); + ReconstructedBuilding(const nlohmann::json& poly); + ReconstructedBuilding(const Polygon_with_attr& poly); ReconstructedBuilding(const std::shared_ptr& importedBuilding); ~ReconstructedBuilding(); From 40f7e94f3769aa31804dad5a7596eb634b8e44ac Mon Sep 17 00:00:00 2001 From: Ivan Paden Date: Fri, 27 Oct 2023 17:50:37 +0200 Subject: [PATCH 4/7] Refactor fallback --- src/ImportedBuilding.cpp | 1 - src/Map3d.cpp | 57 ++++++++++++++++++++-------------------- src/Map3d.h | 2 +- 3 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/ImportedBuilding.cpp b/src/ImportedBuilding.cpp index 4e7a8ce0..e6d5342a 100644 --- a/src/ImportedBuilding.cpp +++ b/src/ImportedBuilding.cpp @@ -341,7 +341,6 @@ void ImportedBuilding::reconstruct() { PMP::triangulate_faces(_mesh); if (this->get_height() < Config::get().minHeight) { - this->deactivate(); // Store points to ptsPtr so that it might be used for LoD1 reconstruction for (auto& pt : _ptMap) _ptsPtr->insert(pt.second); throw std::runtime_error("Importing failed. It could be that the height is" diff --git a/src/Map3d.cpp b/src/Map3d.cpp index 17ee023e..d5681127 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -330,7 +330,9 @@ void Map3d::reconstruct_buildings() { << ". If I cannot find a geometry with that LoD, I will reconstruct in the highest LoD available" << std::endl; } - this->reconstruct_buildings(_buildingsPtr); + # pragma omp parallel for + for (auto& b : _buildingsPtr) this->reconstruct_one_building(b); + this->clear_inactives(); // Gather failed reconstructions int failed = 0; for (auto& b : _buildingsPtr) if (b->has_failed_to_reconstruct()) ++failed; @@ -341,36 +343,33 @@ void Map3d::reconstruct_buildings() { << failed << std::endl; } -void Map3d::reconstruct_buildings(BuildingsPtr& buildings) { - BuildingsPtr newBuildingsToReconstruct; - #pragma omp parallel for - for (auto& f : buildings) { - try { - f->reconstruct(); - //-- In case of hybrid boolean/constraining reconstruction - if (Config::get().clip && !Config::get().handleSelfIntersect && f->has_self_intersections()) { - f->set_clip_flag(false); - f->reconstruct(); - } - } catch (std::exception& e) { - if (f->is_imported()) { - // try to recover by reconstructing LoD1 from geometry pts - auto importToReconstructBuild = - std::make_shared(std::static_pointer_cast(f)); - _reconstructedBuildingsPtr.push_back(importToReconstructBuild); - _allFeaturesPtr.push_back(importToReconstructBuild); - _buildingsPtr.push_back(importToReconstructBuild); - newBuildingsToReconstruct.push_back(importToReconstructBuild); - } else { - // mark for geojson output - f->mark_as_failed(); - } - // add information to log file - Config::write_to_log("Building ID: " + f->get_id() + " Failed to reconstruct. Reason: " + e.what()); +void Map3d::reconstruct_one_building(std::shared_ptr& building) { + try { + building->reconstruct(); + //-- In case of hybrid boolean/constraining reconstruction + if (Config::get().clip && !Config::get().handleSelfIntersect && building->has_self_intersections()) { + building->set_clip_flag(false); + building->reconstruct(); + } + } catch (std::exception& e) { + // add information to log file + Config::write_to_log("Building ID: " + building->get_id() + " Failed to reconstruct. Reason: " + e.what()); + // fallback for failed reconstruction of imported buildings + if (building->is_imported()) { + building->deactivate(); // deactivate this and use reconstructed instead + // try to recover by reconstructing LoD1.2 from geometry pts + auto importToReconstructBuild = + std::make_shared(std::static_pointer_cast(building)); + _reconstructedBuildingsPtr.push_back(importToReconstructBuild); + _allFeaturesPtr.push_back(importToReconstructBuild); + _buildingsPtr.push_back(importToReconstructBuild); + std::shared_ptr buildToReconstruct = importToReconstructBuild; + this->reconstruct_one_building(buildToReconstruct); + } else { + // mark for geojson output + building->mark_as_failed(); } } - this->clear_inactives(); - if (!newBuildingsToReconstruct.empty()) this->reconstruct_buildings(newBuildingsToReconstruct); } void Map3d::reconstruct_boundaries() { diff --git a/src/Map3d.h b/src/Map3d.h index 8309d719..71dd4d81 100644 --- a/src/Map3d.h +++ b/src/Map3d.h @@ -80,7 +80,7 @@ class Map3d { void remove_extra_terrain_pts(); void reconstruct_terrain(); void reconstruct_buildings(); - void reconstruct_buildings(BuildingsPtr& buildings); + void reconstruct_one_building(std::shared_ptr& building); void reconstruct_boundaries(); void reconstruct_with_flat_terrain(); void solve_building_conflicts(); From 6e79408da86a17315826f6a26b4f5c30af89528d Mon Sep 17 00:00:00 2001 From: Ivan Paden Date: Fri, 27 Oct 2023 23:09:51 +0200 Subject: [PATCH 5/7] Make sure inactive buildings are not reconstructed --- src/Map3d.cpp | 6 ++++-- src/PolyFeature.cpp | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Map3d.cpp b/src/Map3d.cpp index d5681127..bc14f8ad 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -331,8 +331,10 @@ void Map3d::reconstruct_buildings() { << std::endl; } # pragma omp parallel for - for (auto& b : _buildingsPtr) this->reconstruct_one_building(b); - this->clear_inactives(); + for (auto& b : _buildingsPtr) { + if (b->is_active()) this->reconstruct_one_building(b); + } + this->clear_inactives(); // in case of imported-reconstructed fallback // Gather failed reconstructions int failed = 0; for (auto& b : _buildingsPtr) if (b->has_failed_to_reconstruct()) ++failed; diff --git a/src/PolyFeature.cpp b/src/PolyFeature.cpp index bc556234..4872e35b 100644 --- a/src/PolyFeature.cpp +++ b/src/PolyFeature.cpp @@ -177,8 +177,8 @@ void PolyFeature::calc_footprint_elevation_linear(const DT& dt) { double PolyFeature::ground_elevation() { if (_groundElevation < -global::largnum + global::smallnum) { - if (_groundElevations.empty())throw std::runtime_error("Polygon elevations missing!" - " Cannot calculate average"); + if (_groundElevations.empty()) throw std::runtime_error("Polygon elevations missing!" + " Cannot calculate average"); // calculating base elevation as 95 percentile of outer ring _groundElevation = geomutils::percentile(_groundElevations.front(), 0.95); } From 2f2252886f6e39b2f1a209c95d91a36ff4d6a320 Mon Sep 17 00:00:00 2001 From: Ivan Paden Date: Fri, 27 Oct 2023 23:17:09 +0200 Subject: [PATCH 6/7] Update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cc57692..f2611831 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [Unreleased] +### Changed +- Improved fallbacks in case building import fails in late stages +### Fixed +- Minor bugfixes + ## [0.4.3] - 2023-08-25 ### Fixed - Issue with GDAL on Ubuntu 20.04 From ee0595337e9a71697cab56bb9a6f933cb1cb84d1 Mon Sep 17 00:00:00 2001 From: Ivan Paden Date: Thu, 2 Nov 2023 17:57:01 +0100 Subject: [PATCH 7/7] Fix import-reconstruct issues --- src/ImportedBuilding.cpp | 42 +++++++++++++++++++++++++--------------- src/Map3d.cpp | 13 ++++++++----- src/PolyFeature.cpp | 2 +- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/ImportedBuilding.cpp b/src/ImportedBuilding.cpp index e6d5342a..b6d65d24 100644 --- a/src/ImportedBuilding.cpp +++ b/src/ImportedBuilding.cpp @@ -116,23 +116,33 @@ ImportedBuilding::ImportedBuilding(std::unique_ptr& buildingJson // int polyNo = 0; for (int& footprintIdx : _footprintIdxList) { //-- Construct footprint polygon from ground surface - nlohmann::json coordBnd = geometry["boundaries"].front()[footprintIdx].front(); - CGAL::Polygon_2 facePoly; - for (const int& ptIdx: coordBnd) { - facePoly.push_back(ePoint_2(_ptMap.at(ptIdx).x(), _ptMap.at(ptIdx).y())); - footprintElevations.push_back(_ptMap.at(ptIdx).z()); - pointConnectivity[IO::gen_key_bucket(Point_2(_ptMap.at(ptIdx).x(), _ptMap.at(ptIdx).y()))] = ptIdx; - } - if (!facePoly.is_simple()) { - Config::write_to_log("Failed to import building: " + this->get_id() - + " Reason: Footprint polygon is not simple."); - this->deactivate(); - return; + CGAL::Polygon_with_holes_2 facePolyWH; + bool first = true; + for (auto& coordBnd : geometry["boundaries"].front()[footprintIdx]) { + CGAL::Polygon_2 facePoly; + for (const int& ptIdx: coordBnd) { + facePoly.push_back(ePoint_2(_ptMap.at(ptIdx).x(), _ptMap.at(ptIdx).y())); + footprintElevations.push_back(_ptMap.at(ptIdx).z()); + pointConnectivity[IO::gen_key_bucket(Point_2(_ptMap.at(ptIdx).x(), _ptMap.at(ptIdx).y()))] = ptIdx; + } + if (!facePoly.is_simple()) { + Config::write_to_log("Failed to import building: " + this->get_id() + + " Reason: Footprint polygon is not simple."); + this->deactivate(); + return; + } + geomutils::pop_back_if_equal_to_front(facePoly); + + if (first) { + if (facePoly.is_clockwise_oriented()) facePoly.reverse_orientation(); + first = false; + facePolyWH.outer_boundary() = facePoly; + } else { + if (facePoly.is_counterclockwise_oriented()) facePoly.reverse_orientation(); + facePolyWH.add_hole(facePoly); + } } - geomutils::pop_back_if_equal_to_front(facePoly); - if (facePoly.is_clockwise_oriented()) facePoly.reverse_orientation(); - - polySet.join(facePoly); + polySet.join(facePolyWH); } //-- Polyset to polygon data structure this->polyset_to_polygon(polySet); diff --git a/src/Map3d.cpp b/src/Map3d.cpp index bc14f8ad..a3a1f825 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -360,11 +360,14 @@ void Map3d::reconstruct_one_building(std::shared_ptr& building) { if (building->is_imported()) { building->deactivate(); // deactivate this and use reconstructed instead // try to recover by reconstructing LoD1.2 from geometry pts - auto importToReconstructBuild = - std::make_shared(std::static_pointer_cast(building)); - _reconstructedBuildingsPtr.push_back(importToReconstructBuild); - _allFeaturesPtr.push_back(importToReconstructBuild); - _buildingsPtr.push_back(importToReconstructBuild); + auto importToReconstructBuild = + std::make_shared(std::static_pointer_cast(building)); + #pragma omp critical + { + _reconstructedBuildingsPtr.push_back(importToReconstructBuild); + _allFeaturesPtr.push_back(importToReconstructBuild); + _buildingsPtr.push_back(importToReconstructBuild); + } std::shared_ptr buildToReconstruct = importToReconstructBuild; this->reconstruct_one_building(buildToReconstruct); } else { diff --git a/src/PolyFeature.cpp b/src/PolyFeature.cpp index 4872e35b..813211aa 100644 --- a/src/PolyFeature.cpp +++ b/src/PolyFeature.cpp @@ -41,7 +41,7 @@ PolyFeature::PolyFeature() _groundElevation(-global::largnum), _minBbox() {} PolyFeature::PolyFeature(const int outputLayerID) - : TopoFeature(outputLayerID), _poly(), _groundElevations(), _polyInternalID(new_internal_id()), + : TopoFeature(outputLayerID), _poly(), _groundElevations(), _polyInternalID(new_internal_id()), _groundElevation(-global::largnum), _minBbox() {} PolyFeature::PolyFeature(const nlohmann::json& poly, const bool checkSimplicity)