Skip to content

Commit

Permalink
Merge pull request #12 from tudelft3d/improve/import-fallbacks
Browse files Browse the repository at this point in the history
Improve/Import-fallbacks
  • Loading branch information
ipadjen authored Nov 8, 2023
2 parents 5266ac8 + adbc046 commit 3afff34
Show file tree
Hide file tree
Showing 11 changed files with 144 additions and 132 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
## [Unreleased]
### Added
- Precompiled Windows executable
### Changed
- Improved fallbacks in case building import fails in late stages
### Fixed
- Minor bugfixes

## [0.4.3] - 2023-08-25
### Fixed
Expand Down
18 changes: 4 additions & 14 deletions src/Building.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,30 +42,16 @@ Building::Building()
: PolyFeature(1), _elevation(-global::largnum), _height(-global::largnum),
_ptsPtr(std::make_shared<Point_set_3>()), _hasFailed(false) {}

Building::Building(const int internalID)
: PolyFeature(1, internalID), _elevation(-global::largnum), _height(-global::largnum),
_ptsPtr(std::make_shared<Point_set_3>()), _hasFailed(false) {}

Building::Building(const nlohmann::json& poly)
: PolyFeature(poly, true, 1), _elevation(-global::largnum), _height(-global::largnum),
_ptsPtr(std::make_shared<Point_set_3>()), _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<Point_set_3>()), _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<Point_set_3>()), _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<Point_set_3>()), _hasFailed(false) {}
// 'true' here to check for polygon simplicity

Building::~Building() = default;

void Building::insert_point(const Point_3& pt) {
Expand Down Expand Up @@ -275,6 +261,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"];
Expand Down
4 changes: 1 addition & 3 deletions src/Building.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -59,6 +56,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;
Expand Down
75 changes: 45 additions & 30 deletions src/ImportedBuilding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@

int ImportedBuilding::noBottom = 0;

ImportedBuilding::ImportedBuilding(std::unique_ptr<nlohmann::json>& 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<nlohmann::json>& 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<std::string, int> lodGeomLst;
Expand Down Expand Up @@ -116,23 +116,33 @@ ImportedBuilding::ImportedBuilding(std::unique_ptr<nlohmann::json>& 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<EPECK> 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_parent_building_id()
+ " Reason: Footprint polygon is not simple.");
this->deactivate();
return;
CGAL::Polygon_with_holes_2<EPECK> facePolyWH;
bool first = true;
for (auto& coordBnd : geometry["boundaries"].front()[footprintIdx]) {
CGAL::Polygon_2<EPECK> 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);
Expand All @@ -141,12 +151,13 @@ ImportedBuilding::ImportedBuilding(std::unique_ptr<nlohmann::json>& buildingJson
this->set_footprint_mesh_connectivity(pointConnectivity);
}

ImportedBuilding::ImportedBuilding(Mesh& mesh, const int internalID)
: Building(internalID), _buildingJson(std::make_unique<nlohmann::json>()),
_footprintIdxList(), _parentBuildingID(), _appendToBuilding(false), _ptMap(),
ImportedBuilding::ImportedBuilding(Mesh& mesh)
: Building(), _buildingJson(std::make_unique<nlohmann::json>()),
_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<EPICK> downVector(0, 0, -1);
Expand Down Expand Up @@ -339,6 +350,14 @@ void ImportedBuilding::reconstruct() {
PMP::polygon_soup_to_polygon_mesh(points, polygons, _mesh);
PMP::triangulate_faces(_mesh);

if (this->get_height() < Config::get().minHeight) {
// 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"
"\n lower than minimum, or the mesh connectivity is broken."
"\n Trying to reconstruct LoD1.2 from building points");
}

/*
Mesh wrap;
const double relative_alpha = 300.;
Expand Down Expand Up @@ -409,10 +428,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;
}
Expand All @@ -423,7 +438,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;
Expand Down
6 changes: 2 additions & 4 deletions src/ImportedBuilding.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ class ImportedBuilding : public Building {

ImportedBuilding() = delete;
ImportedBuilding(std::unique_ptr<nlohmann::json>& buildingJson,
PointSet3Ptr& importedBuildingPts, const int internalID);
ImportedBuilding(Mesh& mesh, const int internalID);
PointSet3Ptr& importedBuildingPts);
ImportedBuilding(Mesh& mesh);
~ImportedBuilding();

virtual double get_elevation() override;
Expand All @@ -49,7 +49,6 @@ class ImportedBuilding : public Building {
void append_nonground_part(const std::shared_ptr<ImportedBuilding>& 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;

Expand All @@ -61,7 +60,6 @@ class ImportedBuilding : public Building {
std::unique_ptr<nlohmann::json> _buildingJson;
std::vector<int> _footprintIdxList;
std::vector<std::vector<int>> _footprintPtsIdxList;
std::string _parentBuildingID;
bool _appendToBuilding;
bool _trueHeight;
int _lodIdx;
Expand Down
78 changes: 47 additions & 31 deletions src/Map3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ReconstructedBuilding>(*poly, internalID++);
auto building = std::make_shared<ReconstructedBuilding>(*poly);
_reconstructedBuildingsPtr.push_back(building);
_buildingsPtr.push_back(building);
_allFeaturesPtr.push_back(building);
Expand All @@ -119,10 +118,8 @@ void Map3d::set_features() {
std::cout << "Importing CityJSON geometries" << std::endl;

std::vector<std::shared_ptr<ImportedBuilding>> appendingBuildings;
internalID = 0;
for (auto& importedBuilding: _importedBuildingsJSON) {
auto explicitCityJSONGeom = std::make_shared<ImportedBuilding>(importedBuilding, _importedBuildingsPts,
internalID++);
auto explicitCityJSONGeom = std::make_shared<ImportedBuilding>(importedBuilding, _importedBuildingsPts);
if (!explicitCityJSONGeom->is_appending()) {
_importedBuildingsPtr.push_back(explicitCityJSONGeom);
_buildingsPtr.push_back(explicitCityJSONGeom);
Expand All @@ -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;
}
Expand All @@ -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<ImportedBuilding>(mesh, internalID++);
auto explicitOBJGeom = std::make_shared<ImportedBuilding>(mesh);
_importedBuildingsPtr.push_back(explicitOBJGeom);
_buildingsPtr.push_back(explicitOBJGeom);
_allFeaturesPtr.push_back(explicitOBJGeom);
Expand Down Expand Up @@ -333,36 +330,55 @@ void Map3d::reconstruct_buildings() {
<< ". If I cannot find a geometry with that LoD, I will reconstruct in the highest LoD available"
<< std::endl;
}
int failed = 0;
#pragma omp parallel for
# pragma omp parallel for
for (int i = 0; i < _buildingsPtr.size(); ++i) {
//for (auto& f : _buildingsPtr) { // MSVC doesn't like range loops with OMP
auto& f = _buildingsPtr[i];
if (!f->is_active()) continue;
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) {
#pragma omp atomic
++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();
}
auto& b = _buildingsPtr[i];
if (b->is_active()) this->reconstruct_one_building(b);
}
this->clear_inactives();
std::cout << " Number of successfully reconstructed buildings: " << _buildingsPtr.size() << std::endl;
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;
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_one_building(std::shared_ptr<Building>& 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<ReconstructedBuilding>(std::static_pointer_cast<ImportedBuilding>(building));
#pragma omp critical
{
_reconstructedBuildingsPtr.push_back(importToReconstructBuild);
_allFeaturesPtr.push_back(importToReconstructBuild);
_buildingsPtr.push_back(importToReconstructBuild);
}
std::shared_ptr<Building> buildToReconstruct = importToReconstructBuild;
this->reconstruct_one_building(buildToReconstruct);
} else {
// mark for geojson output
building->mark_as_failed();
}
}
}

void Map3d::reconstruct_boundaries() {
std::cout << "\nReconstructing boundaries" << std::endl;
if (_boundariesPtr.size() > 2) { // Means more than one side
Expand Down Expand Up @@ -550,14 +566,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();
Expand Down
1 change: 1 addition & 0 deletions src/Map3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class Map3d {
void remove_extra_terrain_pts();
void reconstruct_terrain();
void reconstruct_buildings();
void reconstruct_one_building(std::shared_ptr<Building>& building);
void reconstruct_boundaries();
void reconstruct_with_flat_terrain();
void solve_building_conflicts();
Expand Down
Loading

0 comments on commit 3afff34

Please sign in to comment.