From 89b5398ec1edd141498afce13f94720a7541a89b Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Thu, 17 May 2018 11:14:45 +0200 Subject: [PATCH 01/45] WIP --- src/Building.cpp | 2 +- src/Building.h | 2 +- src/Map3d.cpp | 8 +++++++- src/Map3d.h | 4 ++-- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Building.cpp b/src/Building.cpp index e41db438..842c591f 100644 --- a/src/Building.cpp +++ b/src/Building.cpp @@ -193,7 +193,7 @@ void Building::get_cityjson(nlohmann::json& j, std::unordered_mapget_id()] = b; } -void Building::get_citygml(std::wostream& of) { +void Building::get_citygml(std::wostream& of, bool building_triangulate, bool building_include_floor) { float h = z_to_float(this->get_height()); float hbase = z_to_float(this->get_height_base()); of << ""; diff --git a/src/Building.h b/src/Building.h index 01f89a2c..d17b4ab9 100644 --- a/src/Building.h +++ b/src/Building.h @@ -37,7 +37,7 @@ class Building: public Flat { bool lift(); bool add_elevation_point(Point2 &p, double z, float radius, int lasclass); void get_obj(std::unordered_map< std::string, unsigned long > &dPts, int lod, std::string mtl, std::string &fs); - void get_citygml(std::wostream& of); + void get_citygml(std::wostream& of, bool building_triangulate, bool building_include_floor); void get_citygml_imgeo(std::wostream& of); void get_imgeo_nummeraanduiding(std::wostream& of); void get_csv(std::wostream& of); diff --git a/src/Map3d.cpp b/src/Map3d.cpp index f37f9319..011a0604 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -194,7 +194,13 @@ bool Map3d::get_cityjson(std::string filename) { void Map3d::get_citygml(std::wostream& of) { create_citygml_header(of); for (auto& f : _lsFeatures) { - f->get_citygml(of); + if (f->get_class() == BUILDING) { + Building* f = dynamic_cast(f); + f->get_citygml(of, _building_triangulate, _building_include_floor); + } + else { + f->get_citygml(of); + } of << "\n"; } of << "\n"; diff --git a/src/Map3d.h b/src/Map3d.h index b8706887..1f746546 100644 --- a/src/Map3d.h +++ b/src/Map3d.h @@ -108,9 +108,9 @@ class Map3d { private: float _building_heightref_roof; float _building_heightref_ground; - bool _building_triangulate; // TODO: Not used anymore, remove? + bool _building_triangulate; // TODO: Not used anymore, remove? int _building_lod; - bool _building_include_floor; // TODO: Not used anymore, remove? + bool _building_include_floor; // TODO: Not used anymore, remove? int _terrain_simplification; int _forest_simplification; double _terrain_simplification_tinsimp; From 9de96d50cdba645b8937df2e86896405830b500f Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Thu, 17 May 2018 15:05:55 +0200 Subject: [PATCH 02/45] add triangulate building and building floor code --- src/Building.cpp | 79 +++++++++++++++++++++++++++------------------ src/Building.h | 8 +++-- src/Map3d.cpp | 10 ++---- src/Map3d.h | 4 +-- src/TopoFeature.cpp | 52 ++++++++++++++++++++++++----- src/TopoFeature.h | 1 + src/io.cpp | 19 ++++++++--- src/io.h | 2 +- src/main.cpp | 30 ++++++++--------- 9 files changed, 130 insertions(+), 75 deletions(-) diff --git a/src/Building.cpp b/src/Building.cpp index 842c591f..7d361d14 100644 --- a/src/Building.cpp +++ b/src/Building.cpp @@ -33,14 +33,17 @@ //-- static variable float Building::_heightref_top; float Building::_heightref_base; +bool Building::_building_triangulate; +bool Building::_building_include_floor; std::set Building::_las_classes_roof; std::set Building::_las_classes_ground; - -Building::Building(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref_top, float heightref_base) +Building::Building(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref_top, float heightref_base, bool building_triangulate, bool building_include_floor) : Flat(wkt, layername, attributes, pid) { _heightref_top = heightref_top; _heightref_base = heightref_base; + _building_triangulate = building_triangulate; + _building_include_floor = building_include_floor; } void Building::set_las_classes_roof(std::set theset) @@ -193,7 +196,7 @@ void Building::get_cityjson(nlohmann::json& j, std::unordered_mapget_id()] = b; } -void Building::get_citygml(std::wostream& of, bool building_triangulate, bool building_include_floor) { +void Building::get_citygml(std::wostream& of) { float h = z_to_float(this->get_height()); float hbase = z_to_float(this->get_height_base()); of << ""; @@ -220,22 +223,19 @@ void Building::get_citygml(std::wostream& of, bool building_triangulate, bool bu of << ""; of << ""; of << ""; - //-- get floor - get_polygon_lifted_gml(of, this->_p2, hbase, false); - //-- get roof - get_polygon_lifted_gml(of, this->_p2, h, true); - //-- get the walls - auto r = _p2->outer(); - int i; - for (i = 0; i < (r.size() - 1); i++) - get_extruded_line_gml(of, &r[i], &r[i + 1], h, hbase, false); - get_extruded_line_gml(of, &r[i], &r[0], h, hbase, false); - //-- irings - auto irings = _p2->inners(); - for (Ring2& r : irings) { - for (i = 0; i < (r.size() - 1); i++) - get_extruded_line_gml(of, &r[i], &r[i + 1], h, hbase, false); - get_extruded_line_gml(of, &r[i], &r[0], h, hbase, false); + if (_building_triangulate) { + for (auto& t : _triangles) + get_triangle_as_gml_surfacemember(of, t); + for (auto& t : _triangles_vw) + get_triangle_as_gml_surfacemember(of, t, true); + if (_building_include_floor) { + for (auto& t : _triangles) { + get_floor_triangle_as_gml_surfacemember(of, t, _height_base); + } + } + } + else { + get_extruded_lod1_block_gml(of, this->_p2, h, hbase, _building_include_floor); } of << ""; of << ""; @@ -259,22 +259,37 @@ void Building::get_citygml_imgeo(std::wostream& of) { of << ""; of << ""; of << ""; - //-- get floor - get_polygon_lifted_gml(of, this->_p2, hbase, false); - //-- get roof - get_polygon_lifted_gml(of, this->_p2, h, true); - //-- get the walls - auto r = _p2->outer(); - int i; - for (i = 0; i < (r.size() - 1); i++) - get_extruded_line_gml(of, &r[i], &r[i + 1], h, hbase, false); - get_extruded_line_gml(of, &r[i], &r[0], h, hbase, false); - //-- irings - auto irings = _p2->inners(); - for (Ring2& r : irings) { + if (_building_triangulate) { + for (auto& t : _triangles) + get_triangle_as_gml_surfacemember(of, t); + for (auto& t : _triangles_vw) + get_triangle_as_gml_surfacemember(of, t, true); + if (_building_include_floor) { + for (auto& t : _triangles) { + get_floor_triangle_as_gml_surfacemember(of, t, _height_base); + } + } + } + else { + if (_building_include_floor) { + //-- get floor + get_polygon_lifted_gml(of, this->_p2, hbase, false); + } + //-- get roof + get_polygon_lifted_gml(of, this->_p2, h, true); + //-- get the walls + auto r = _p2->outer(); + int i; for (i = 0; i < (r.size() - 1); i++) get_extruded_line_gml(of, &r[i], &r[i + 1], h, hbase, false); get_extruded_line_gml(of, &r[i], &r[0], h, hbase, false); + //-- irings + auto irings = _p2->inners(); + for (Ring2& r : irings) { + for (i = 0; i < (r.size() - 1); i++) + get_extruded_line_gml(of, &r[i], &r[i + 1], h, hbase, false); + get_extruded_line_gml(of, &r[i], &r[0], h, hbase, false); + } } of << ""; of << ""; diff --git a/src/Building.h b/src/Building.h index d17b4ab9..5ef13414 100644 --- a/src/Building.h +++ b/src/Building.h @@ -33,11 +33,11 @@ class Building: public Flat { public: - Building(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref_top, float heightref_base); + Building(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref_top, float heightref_base, bool building_triangulate, bool building_include_floor); bool lift(); bool add_elevation_point(Point2 &p, double z, float radius, int lasclass); void get_obj(std::unordered_map< std::string, unsigned long > &dPts, int lod, std::string mtl, std::string &fs); - void get_citygml(std::wostream& of, bool building_triangulate, bool building_include_floor); + void get_citygml(std::wostream& of); void get_citygml_imgeo(std::wostream& of); void get_imgeo_nummeraanduiding(std::wostream& of); void get_csv(std::wostream& of); @@ -57,7 +57,9 @@ class Building: public Flat { std::vector _zvaluesground; int _height_base; static float _heightref_top; - static float _heightref_base; + static float _heightref_base; + static bool _building_triangulate; + static bool _building_include_floor; static std::set _las_classes_roof; static std::set _las_classes_ground; }; diff --git a/src/Map3d.cpp b/src/Map3d.cpp index a28da4a5..a0d0a913 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -194,13 +194,7 @@ bool Map3d::get_cityjson(std::string filename) { void Map3d::get_citygml(std::wostream& of) { create_citygml_header(of); for (auto& f : _lsFeatures) { - if (f->get_class() == BUILDING) { - Building* f = dynamic_cast(f); - f->get_citygml(of, _building_triangulate, _building_include_floor); - } - else { - f->get_citygml(of); - } + f->get_citygml(of); of << "\n"; } of << "\n"; @@ -922,7 +916,7 @@ void Map3d::extract_feature(OGRFeature *f, std::string layername, const char *id attributes[boost::locale::to_lower(f->GetFieldDefnRef(i)->GetNameRef())] = std::make_pair(f->GetFieldDefnRef(i)->GetType(), f->GetFieldAsString(i)); } if (layertype == "Building") { - Building* p3 = new Building(wkt, layername, attributes, id, _building_heightref_roof, _building_heightref_ground); + Building* p3 = new Building(wkt, layername, attributes, id, _building_heightref_roof, _building_heightref_ground, _building_triangulate, _building_include_floor); _lsFeatures.push_back(p3); } else if (layertype == "Terrain") { diff --git a/src/Map3d.h b/src/Map3d.h index e62b66ca..ae72c36a 100644 --- a/src/Map3d.h +++ b/src/Map3d.h @@ -108,9 +108,9 @@ class Map3d { private: float _building_heightref_roof; float _building_heightref_ground; - bool _building_triangulate; // TODO: Not used anymore, remove? + bool _building_triangulate; int _building_lod; - bool _building_include_floor; // TODO: Not used anymore, remove? + bool _building_include_floor; int _terrain_simplification; int _forest_simplification; double _terrain_simplification_tinsimp; diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index 3a8f72c8..71abda8e 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -892,16 +892,26 @@ void TopoFeature::get_triangle_as_gml_surfacemember(std::wostream& of, Triangle& of << ""; of << ""; if (verticalwall == false) { - of << "" << _vertices[t.v0].second << ""; - of << "" << _vertices[t.v1].second << ""; - of << "" << _vertices[t.v2].second << ""; - of << "" << _vertices[t.v0].second << ""; + of << "" + << _vertices[t.v0].second << " " + << _vertices[t.v1].second << " " + << _vertices[t.v2].second << " " + << _vertices[t.v0].second << ""; + //of << "" << _vertices[t.v0].second << ""; + //of << "" << _vertices[t.v1].second << ""; + //of << "" << _vertices[t.v2].second << ""; + //of << "" << _vertices[t.v0].second << ""; } else { - of << "" << _vertices_vw[t.v0].second << ""; - of << "" << _vertices_vw[t.v1].second << ""; - of << "" << _vertices_vw[t.v2].second << ""; - of << "" << _vertices_vw[t.v0].second << ""; + of << "" + << _vertices_vw[t.v0].second << " " + << _vertices_vw[t.v1].second << " " + << _vertices_vw[t.v2].second << " " + << _vertices_vw[t.v0].second << ""; + //of << "" << _vertices_vw[t.v0].second << ""; + //of << "" << _vertices_vw[t.v1].second << ""; + //of << "" << _vertices_vw[t.v2].second << ""; + //of << "" << _vertices_vw[t.v0].second << ""; } of << ""; of << ""; @@ -909,6 +919,32 @@ void TopoFeature::get_triangle_as_gml_surfacemember(std::wostream& of, Triangle& of << ""; } +void TopoFeature::get_floor_triangle_as_gml_surfacemember(std::wostream& of, Triangle& t, int baseheight) { + of << ""; + of << ""; + of << ""; + of << ""; + + std::stringstream ss; + ss << std::fixed << std::setprecision(3); + // replace z of the vertices with baseheight + ss << "" + << _vertices[t.v0].second.substr(0, _vertices[t.v0].second.find_last_of(" ") + 1) << z_to_float(baseheight) << " " + << _vertices[t.v2].second.substr(0, _vertices[t.v2].second.find_last_of(" ") + 1) << z_to_float(baseheight) << " " + << _vertices[t.v1].second.substr(0, _vertices[t.v1].second.find_last_of(" ") + 1) << z_to_float(baseheight) << " " + << _vertices[t.v0].second.substr(0, _vertices[t.v0].second.find_last_of(" ") + 1) << z_to_float(baseheight) << ""; + //ss << "" << _vertices[t.v0].second.substr(0, _vertices[t.v0].second.find_last_of(" ") + 1) << z_to_float(baseheight) << ""; + //ss << "" << _vertices[t.v2].second.substr(0, _vertices[t.v2].second.find_last_of(" ") + 1) << z_to_float(baseheight) << ""; + //ss << "" << _vertices[t.v1].second.substr(0, _vertices[t.v1].second.find_last_of(" ") + 1) << z_to_float(baseheight) << ""; + //ss << "" << _vertices[t.v0].second.substr(0, _vertices[t.v0].second.find_last_of(" ") + 1) << z_to_float(baseheight) << ""; + + of << ss.str(); + of << ""; + of << ""; + of << ""; + of << ""; +} + void TopoFeature::get_triangle_as_gml_triangle(std::wostream& of, Triangle& t, bool verticalwall) { of << ""; of << ""; diff --git a/src/TopoFeature.h b/src/TopoFeature.h index 3fa3dc3d..03f9f75d 100644 --- a/src/TopoFeature.h +++ b/src/TopoFeature.h @@ -102,6 +102,7 @@ class TopoFeature { void get_cityjson_geom(nlohmann::json& g, std::unordered_map &dPts, std::string primitive = "MultiSurface"); void get_triangle_as_gml_surfacemember(std::wostream& of, Triangle& t, bool verticalwall = false); + void get_floor_triangle_as_gml_surfacemember(std::wostream& of, Triangle& t, int baseheight); void get_triangle_as_gml_triangle(std::wostream& of, Triangle& t, bool verticalwall = false); bool get_attribute(std::string attributeName, std::string &attribute, std::string defaultValue = ""); }; diff --git a/src/io.cpp b/src/io.cpp index ca3a1b37..dbfa9281 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -138,15 +138,26 @@ void get_extruded_line_gml(std::wostream& of, Point2* a, Point2* b, double high, of << ""; } -void get_extruded_lod1_block_gml(std::wostream& of, Polygon2* p2, double high, double low) { - //-- get floor - get_polygon_lifted_gml(of, p2, low, false); +void get_extruded_lod1_block_gml(std::wostream& of, Polygon2* p2, double high, double low, bool building_include_floor) { + if (building_include_floor) { + //-- get floor + get_polygon_lifted_gml(of, p2, low, false); + } //-- get roof get_polygon_lifted_gml(of, p2, high, true); //-- get the walls auto r = p2->outer(); - for (int i = 0; i < (r.size() - 1); i++) + int i; + for (i = 0; i < (r.size() - 1); i++) get_extruded_line_gml(of, &r[i], &r[i + 1], high, low, false); + get_extruded_line_gml(of, &r[i], &r[0], high, low, false); + //-- irings + auto irings = p2->inners(); + for (Ring2& r : irings) { + for (i = 0; i < (r.size() - 1); i++) + get_extruded_line_gml(of, &r[i], &r[i + 1], high, low, false); + get_extruded_line_gml(of, &r[i], &r[0], high, low, false); + } } bool is_string_integer(std::string s, int min, int max) { diff --git a/src/io.h b/src/io.h index af6bdf2d..ed932c2d 100644 --- a/src/io.h +++ b/src/io.h @@ -39,7 +39,7 @@ void get_citygml_imgeo_namespaces(std::wostream& of); void get_polygon_lifted_gml(std::wostream& of, Polygon2* p2, double height, bool reverse = false); void get_extruded_line_gml(std::wostream& of, Point2* a, Point2* b, double high, double low, bool reverse = false); -void get_extruded_lod1_block_gml(std::wostream& of, Polygon2* p2, double high, double low = 0.0); +void get_extruded_lod1_block_gml(std::wostream& of, Polygon2* p2, double high, double low = 0.0, bool building_include_floor = false); bool is_string_integer(std::string s, int min = 0, int max = 1e6); float z_to_float(int z); diff --git a/src/main.cpp b/src/main.cpp index 8de690d8..b518f913 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -212,6 +212,12 @@ int main(int argc, const char * argv[]) { else map3d.set_building_triangulate(false); } + if (n["Building"]["floor"]) { + if (n["Building"]["floor"].as() == "true") + map3d.set_building_include_floor(true); + else + map3d.set_building_include_floor(false); + } } if (n["Terrain"]) { if (n["Terrain"]["simplification"]) @@ -505,16 +511,6 @@ int main(int argc, const char * argv[]) { } std::clog << "...3dfying done.\n"; - //-- output - if (nodes["output"]) { - YAML::Node n = nodes["output"]; - if (n["building_floor"]) { - if (n["building_floor"].as() == "true") { - map3d.set_building_include_floor(true); - } - } - } - //-- iterate over all output for (auto& output : outputs) { auto startFileWriting = boost::chrono::high_resolution_clock::now(); @@ -752,6 +748,13 @@ bool validate_yaml(const char* arg, std::set& allowedFeatures) { std::cerr << "\tOption 'Building.triangulate' invalid; must be 'true' or 'false'.\n"; } } + if (n["Building"]["floor"]) { + std::string s = n["Building"]["floor"].as(); + if ((s != "true") && (s != "false")) { + wentgood = false; + std::cerr << "\tOption 'Building.floor' invalid; must be 'true' or 'false'.\n"; + } + } } if (n["Terrain"]) { if (n["Terrain"]["simplification"]) { @@ -990,13 +993,6 @@ bool validate_yaml(const char* arg, std::set& allowedFeatures) { wentgood = false; std::cerr << "\tOutput format GDAL needs gdal_driver setting\n"; } - if (n["building_floor"]) { - std::string s = n["building_floor"].as(); - if ((s != "true") && (s != "false")) { - wentgood = false; - std::cerr << "\tOption 'output.building_floor' invalid; must be 'true' or 'false'.\n"; - } - } } return wentgood; From 52404bb1d4b84bd571e7f0c4b10c3f4fed9c00d9 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Thu, 17 May 2018 16:55:13 +0200 Subject: [PATCH 03/45] change gml surfacemember pos to poslist --- src/TopoFeature.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index 71abda8e..f244234d 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -897,10 +897,6 @@ void TopoFeature::get_triangle_as_gml_surfacemember(std::wostream& of, Triangle& << _vertices[t.v1].second << " " << _vertices[t.v2].second << " " << _vertices[t.v0].second << ""; - //of << "" << _vertices[t.v0].second << ""; - //of << "" << _vertices[t.v1].second << ""; - //of << "" << _vertices[t.v2].second << ""; - //of << "" << _vertices[t.v0].second << ""; } else { of << "" @@ -908,10 +904,6 @@ void TopoFeature::get_triangle_as_gml_surfacemember(std::wostream& of, Triangle& << _vertices_vw[t.v1].second << " " << _vertices_vw[t.v2].second << " " << _vertices_vw[t.v0].second << ""; - //of << "" << _vertices_vw[t.v0].second << ""; - //of << "" << _vertices_vw[t.v1].second << ""; - //of << "" << _vertices_vw[t.v2].second << ""; - //of << "" << _vertices_vw[t.v0].second << ""; } of << ""; of << ""; @@ -933,10 +925,6 @@ void TopoFeature::get_floor_triangle_as_gml_surfacemember(std::wostream& of, Tri << _vertices[t.v2].second.substr(0, _vertices[t.v2].second.find_last_of(" ") + 1) << z_to_float(baseheight) << " " << _vertices[t.v1].second.substr(0, _vertices[t.v1].second.find_last_of(" ") + 1) << z_to_float(baseheight) << " " << _vertices[t.v0].second.substr(0, _vertices[t.v0].second.find_last_of(" ") + 1) << z_to_float(baseheight) << ""; - //ss << "" << _vertices[t.v0].second.substr(0, _vertices[t.v0].second.find_last_of(" ") + 1) << z_to_float(baseheight) << ""; - //ss << "" << _vertices[t.v2].second.substr(0, _vertices[t.v2].second.find_last_of(" ") + 1) << z_to_float(baseheight) << ""; - //ss << "" << _vertices[t.v1].second.substr(0, _vertices[t.v1].second.find_last_of(" ") + 1) << z_to_float(baseheight) << ""; - //ss << "" << _vertices[t.v0].second.substr(0, _vertices[t.v0].second.find_last_of(" ") + 1) << z_to_float(baseheight) << ""; of << ss.str(); of << ""; From 22fa6bc86330502542a8b6cf3cde535a30ec816c Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Thu, 17 May 2018 17:35:45 +0200 Subject: [PATCH 04/45] make nc unique to overcome collapsed vw's --- src/TopoFeature.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index f244234d..c761840c 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -489,11 +489,17 @@ void TopoFeature::construct_vertical_walls(const NodeColumn& nc, int baseheight) } //-- check if there's a nc for either ncit = nc.find(gen_key_bucket(&a)); - if (ncit != nc.end()) + if (ncit != nc.end()) { anc = ncit->second; + // make values in anc unique + anc.erase(unique(anc.begin(), anc.end()), anc.end()); + } ncit = nc.find(gen_key_bucket(&b)); - if (ncit != nc.end()) + if (ncit != nc.end()) { bnc = ncit->second; + // make values in bnc unique + bnc.erase(unique(bnc.begin(), bnc.end()), bnc.end()); + } if ((anc.empty() == true) && (bnc.empty() == true)) continue; From af93f6cb9ec51d735fee59d2697a31214520a430 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Tue, 22 May 2018 17:26:51 +0200 Subject: [PATCH 05/45] add inner walls in buildings and make building floor flat --- src/Map3d.cpp | 8 +-- src/TopoFeature.cpp | 123 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 126 insertions(+), 5 deletions(-) diff --git a/src/Map3d.cpp b/src/Map3d.cpp index 7092b481..88c6b44b 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -1117,8 +1117,8 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st if (star.size() == 1){ if (std::get<0>(star[0])->get_class() != BRIDGE) { TopoFeature* fadj = std::get<0>(star[0]); - //-- if not building and same class or both soft, then average. - if (f->get_class() != BUILDING && (f->is_hard() == false && fadj->is_hard() == false)) { + //-- if not building or both soft, then average. + if (f->get_class() != BUILDING && fadj->get_class() != BUILDING && (f->is_hard() == false && fadj->is_hard() == false)) { stitch_average(f, ringi, pi, fadj, std::get<1>(star[0]), std::get<2>(star[0])); } else { @@ -1167,7 +1167,7 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st TopoClass topoClass = std::get<1>(zstar[i])->get_class(); if (topoClass == BUILDING) { //-- set building to the one with the lowest base - if (building == -1 || dynamic_cast(std::get<1>(zstar[building]))->get_height_base() > dynamic_cast(std::get<1>(zstar[i]))->get_height_base()) { + if (building == -1 || dynamic_cast(std::get<1>(zstar[i]))->get_height_base() > dynamic_cast(std::get<1>(zstar[building]))->get_height_base()) { building = i; } } @@ -1176,7 +1176,7 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st } } - //-- Deal with buildings. If there's a building and a soft class incident, then this soft class + //-- Deal with buildings. If there's a building and adjacent is not water, then this class //-- get allocated the height value of the floor of the building. Any building will do if >1. //-- Also ignore water so it doesn't get snapped to the floor of a building if (building != -1) { diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index c761840c..3cbec21d 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -532,7 +532,7 @@ void TopoFeature::construct_vertical_walls(const NodeColumn& nc, int baseheight) fadj_bz = fadj->get_vertex_elevation(adj_b_ringi, adj_b_pi); } - +#pragma region bridges //-- Make exeption for bridges, they have vw's from bottom up, swap . Also skip if adjacent feature is bridge, vw is then created by bridge if (this->get_class() == BRIDGE) { //-- find the height of the vertex in the node column @@ -591,6 +591,127 @@ void TopoFeature::construct_vertical_walls(const NodeColumn& nc, int baseheight) if (fadj != nullptr && fadj->get_class() == BRIDGE) { continue; } +#pragma endregion + +#pragma region building inner walls + //-- add extra vw for the inner walls of buildings + if (this->get_class() == BUILDING) { + //-- create walls from lowest roof to ground, height jumps are created later + int tmp_az = az; + int tmp_bz = bz; + int tmp_fadj_az = baseheight; + int tmp_fadj_bz = baseheight; + bool adifferent = false; + bool bdifferent = false; + + //-- solve for the case two buildings are adjacent but their baseheights are different + // in case they are different start at the lowest height (which is always the baseheight of the adjacent building) + if (std::find(anc.begin(), anc.end(), tmp_fadj_az) == anc.end()) { + tmp_fadj_az = anc.front(); + adifferent = true; + } + if (std::find(bnc.begin(), bnc.end(), tmp_fadj_bz) == bnc.end()) { + tmp_fadj_bz = bnc.front(); + bdifferent = true; + } + if (adifferent) { + Point3 p; + p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(baseheight)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_fadj_az)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(baseheight)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + Triangle t; + int size = int(_vertices_vw.size()); + t.v0 = size - 2; + t.v1 = size - 3; + t.v2 = size - 1; + _triangles_vw.push_back(t); + } + if (bdifferent) { + Point3 p; + p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(baseheight)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(tmp_fadj_bz)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_fadj_az)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + Triangle t; + int size = int(_vertices_vw.size()); + t.v0 = size - 2; + t.v1 = size - 3; + t.v2 = size - 1; + _triangles_vw.push_back(t); + } + if (fadj->get_class() == BUILDING) { + if (az > fadj_az && bz > fadj_bz) { + tmp_az = fadj_az; + tmp_bz = fadj_bz; + } + + if (_id == "b31bc4dcc-00ba-11e6-b420-2bdcc4ab5d7f") { + std::cout << "stop"; + } + + if ((tmp_az >= tmp_fadj_az || tmp_bz >= tmp_fadj_bz) && (tmp_az != tmp_fadj_az && tmp_bz != tmp_fadj_bz)) { + //-- find the height of the vertex in the node column + std::vector::const_iterator sait, eait, sbit, ebit; + sait = std::find(anc.begin(), anc.end(), tmp_fadj_az); + eait = std::find(anc.begin(), anc.end(), tmp_az); + sbit = std::find(bnc.begin(), bnc.end(), tmp_fadj_bz); + ebit = std::find(bnc.begin(), bnc.end(), tmp_bz); + + //-- iterate to triangulate + while ((sbit != ebit) && (sbit != bnc.end()) && ((sbit + 1) != bnc.end())) { + Point3 p; + if (anc.size() == 0 || sait == anc.end()) { + p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_az)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + } + else { + p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + } + p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + sbit++; + p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + Triangle t; + int size = int(_vertices_vw.size()); + t.v0 = size - 2; + t.v1 = size - 3; + t.v2 = size - 1; + _triangles_vw.push_back(t); + } + while (sait != eait && sait != anc.end() && (sait + 1) != anc.end()) { + Point3 p; + if (bnc.size() == 0 || ebit == bnc.end()) { + p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(tmp_bz)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + } + else { + p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*ebit)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + } + p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + sait++; + p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + Triangle t; + int size = int(_vertices_vw.size()); + t.v0 = size - 3; + t.v1 = size - 2; + t.v2 = size - 1; + _triangles_vw.push_back(t); + } + } + } + } +#pragma endregion + //-- check height differences: f > fadj for *both* Points a and b. if (az < fadj_az || bz < fadj_bz) { continue; From 313e7a180072d0bc4c1529153e4bc82bad23997c Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Thu, 24 May 2018 15:07:54 +0200 Subject: [PATCH 06/45] put licence warning on top again so it prints allways --- src/main.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b518f913..777a9e8a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -114,6 +114,8 @@ int main(int argc, const char * argv[]) { options(poall).positional(popos).run(), vm); po::notify(vm); + std::clog << licensewarning << std::endl; + if (vm.count("help")) { std::cout << "Usage: 3dfier config.yml --OBJ myoutput.obj" << std::endl; std::cout << " 3dfier config.yml --CityGML myoutput.gml" << std::endl; @@ -166,9 +168,6 @@ int main(int argc, const char * argv[]) { return EXIT_FAILURE; } - std::clog << licensewarning << std::endl; - - //-- allowed feature classes std::set allowedFeatures{ "Building", "Water", "Terrain", "Road", "Forest", "Separation", "Bridge/Overpass" }; From 174d134bbb0fd7d68f95f9586285d9aa39c1f0c1 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Thu, 24 May 2018 18:10:26 +0200 Subject: [PATCH 07/45] add function to create inner walls and handle _nc_inner_walls separately --- src/Building.cpp | 41 +++- src/Map3d.cpp | 319 +++++++++++++++------------ src/Map3d.h | 2 + src/TopoFeature.cpp | 518 ++++++++++++++++++++++++++++++++------------ src/TopoFeature.h | 1 + src/geomtools.cpp | 2 +- src/geomtools.h | 2 +- 7 files changed, 601 insertions(+), 284 deletions(-) diff --git a/src/Building.cpp b/src/Building.cpp index 7d361d14..2eac8a1d 100644 --- a/src/Building.cpp +++ b/src/Building.cpp @@ -143,11 +143,11 @@ void Building::get_obj(std::unordered_map< std::string, unsigned long > &dPts, i TopoFeature::get_obj(dPts, mtl, fs); } else if (lod == 0) { - fs += mtl; + fs += mtl; fs += "\n"; for (auto& t : _triangles) { unsigned long a, b, c; - int z = this->get_height_base(); + float z = z_to_float(this->get_height_base()); auto it = dPts.find(gen_key_bucket(&_vertices[t.v0].first, z)); if (it == dPts.end()) { a = dPts.size() + 1; @@ -175,8 +175,41 @@ void Building::get_obj(std::unordered_map< std::string, unsigned long > &dPts, i if ((a != b) && (a != c) && (b != c)) { fs += "f "; fs += std::to_string(a); fs += " "; fs += std::to_string(b); fs += " "; fs += std::to_string(c); fs += "\n"; } - // else - // std::clog << "COLLAPSED TRIANGLE REMOVED\n"; + } + } + if (_building_include_floor) { + fs += "usemtl BuildingFloor\n"; + for (auto& t : _triangles) { + unsigned long a, b, c; + float z = z_to_float(this->get_height_base()); + auto it = dPts.find(gen_key_bucket(&_vertices[t.v0].first, z)); + if (it == dPts.end()) { + a = dPts.size() + 1; + dPts[gen_key_bucket(&_vertices[t.v0].first, z)] = a; + } + else { + a = it->second; + } + it = dPts.find(gen_key_bucket(&_vertices[t.v1].first, z)); + if (it == dPts.end()) { + b = dPts.size() + 1; + dPts[gen_key_bucket(&_vertices[t.v1].first, z)] = b; + } + else { + b = it->second; + } + it = dPts.find(gen_key_bucket(&_vertices[t.v2].first, z)); + if (it == dPts.end()) { + c = dPts.size() + 1; + dPts[gen_key_bucket(&_vertices[t.v2].first, z)] = c; + } + else { + c = it->second; + } + //reverse orientation for floor polygon, a-c-b instead of a-b-c. + if ((a != b) && (a != c) && (b != c)) { + fs += "f "; fs += std::to_string(a); fs += " "; fs += std::to_string(c); fs += " "; fs += std::to_string(b); fs += "\n"; + } } } } diff --git a/src/Map3d.cpp b/src/Map3d.cpp index 88c6b44b..4f711daa 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -41,6 +41,7 @@ Map3d::Map3d() { _building_triangulate = true; _building_lod = 1; _building_include_floor = false; + _building_inner_walls = true; _terrain_simplification = 0; _forest_simplification = 0; _terrain_simplification_tinsimp = 0.0; @@ -694,6 +695,8 @@ bool Map3d::threeDfy(bool stitching) { //-- Sort all node column vectors for (auto& nc : _nc) { std::sort(nc.second.begin(), nc.second.end()); + // make values in nc unique + nc.second.erase(unique(nc.second.begin(), nc.second.end()), nc.second.end()); } std::clog << "===== /BOWTIES =====\n"; @@ -710,6 +713,9 @@ bool Map3d::threeDfy(bool stitching) { int baseheight = 0; if (f->get_class() == BUILDING) { baseheight = dynamic_cast(f)->get_height_base(); + if (_building_inner_walls) { + f->construct_inner_walls(_nc_inner_walls, baseheight); + } } f->construct_vertical_walls(_nc, baseheight); } @@ -1113,168 +1119,197 @@ void Map3d::stitch_lifted_features() { } void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< std::tuple >& star) { - //-- degree of vertex == 2 - if (star.size() == 1){ - if (std::get<0>(star[0])->get_class() != BRIDGE) { - TopoFeature* fadj = std::get<0>(star[0]); - //-- if not building or both soft, then average. - if (f->get_class() != BUILDING && fadj->get_class() != BUILDING && (f->is_hard() == false && fadj->is_hard() == false)) { - stitch_average(f, ringi, pi, fadj, std::get<1>(star[0]), std::get<2>(star[0])); + //-- get p and key_bucket once and check if nc location is empty + Point2 p = f->get_point2(ringi, pi); + std::string key_bucket = gen_key_bucket(&p); + if (_nc[key_bucket].empty()) { + //-- degree of vertex == 2 + if (star.size() == 1) { + if (std::get<0>(star[0])->get_class() != BRIDGE) { + TopoFeature* fadj = std::get<0>(star[0]); + //-- if not building or both soft, then average. + if (f->get_class() != BUILDING && fadj->get_class() != BUILDING && (f->is_hard() == false && fadj->is_hard() == false)) { + stitch_average(f, ringi, pi, fadj, std::get<1>(star[0]), std::get<2>(star[0])); + } + else { + stitch_jumpedge(f, ringi, pi, fadj, std::get<1>(star[0]), std::get<2>(star[0])); + } } else { - stitch_jumpedge(f, ringi, pi, fadj, std::get<1>(star[0]), std::get<2>(star[0])); + // for bridges we need to create VW and therefor add the height to the NC + _nc[key_bucket].push_back(f->get_vertex_elevation(ringi, pi)); } } - else { - // for bridges we need to create VW and therefor add the height to the NC - Point2 p = f->get_point2(ringi, pi); - _nc[gen_key_bucket(&p)].push_back(f->get_vertex_elevation(ringi, pi)); - } - } - //-- degree of vertex >= 3: more complex cases - else if (star.size() > 1) { - //-- collect all elevations - std::vector< std::tuple< int, TopoFeature*, int, int > > zstar; - zstar.push_back(std::make_tuple( - f->get_vertex_elevation(ringi, pi), - f, - ringi, - pi)); - for (auto& fadj : star) { - if (std::get<0>(fadj)->get_class() != BRIDGE) { - zstar.push_back(std::make_tuple( - std::get<0>(fadj)->get_vertex_elevation(std::get<1>(fadj), std::get<2>(fadj)), - std::get<0>(fadj), - std::get<1>(fadj), - std::get<2>(fadj))); + //-- degree of vertex >= 3: more complex cases + else if (star.size() > 1) { + //-- collect all elevations + std::vector< std::tuple< int, TopoFeature*, int, int > > zstar; + zstar.push_back(std::make_tuple( + f->get_vertex_elevation(ringi, pi), + f, + ringi, + pi)); + for (auto& fadj : star) { + if (std::get<0>(fadj)->get_class() != BRIDGE) { + zstar.push_back(std::make_tuple( + std::get<0>(fadj)->get_vertex_elevation(std::get<1>(fadj), std::get<2>(fadj)), + std::get<0>(fadj), + std::get<1>(fadj), + std::get<2>(fadj))); + } + // This it for adjacent objects at the corners of the bridge where the adjacent features need extra VW at the height jump. + else { + f->add_vertical_wall(); + } } - // This it for adjacent objects at the corners of the bridge where the adjacent features need extra VW at the height jump. - else { - f->add_vertical_wall(); + + //-- sort low-high based on heights (get<0>) + std::sort(zstar.begin(), zstar.end(), + [](std::tuple const &t1, std::tuple const &t2) { + return std::get<0>(t1) < std::get<0>(t2); + }); + + if (f->get_id() == "b9f71cae4-00c9-11e6-b420-2bdcc4ab5d7f" && pi==1) { + std::cout << "stop"; } - } - //-- sort low-high based on heights (get<0>) - std::sort(zstar.begin(), zstar.end(), - [](std::tuple const &t1, std::tuple const &t2) { - return std::get<0>(t1) < std::get<0>(t2); - }); - - //-- Identify buildings and water - int building = -1; - int water = -1; - for (int i = 0; i < zstar.size(); i++) { - TopoClass topoClass = std::get<1>(zstar[i])->get_class(); - if (topoClass == BUILDING) { - //-- set building to the one with the lowest base - if (building == -1 || dynamic_cast(std::get<1>(zstar[i]))->get_height_base() > dynamic_cast(std::get<1>(zstar[building]))->get_height_base()) { - building = i; + //-- Identify buildings and water + int building = -1; + int water = -1; + int lowestbuilding = -1; + std::vector buildings; + for (int i = 0; i < zstar.size(); i++) { + TopoClass topoClass = std::get<1>(zstar[i])->get_class(); + if (topoClass == BUILDING) { + //-- store building indexes + buildings.push_back(i); + //-- set building to the one with the highest base + if (building == -1 || dynamic_cast(std::get<1>(zstar[i]))->get_height_base() > dynamic_cast(std::get<1>(zstar[building]))->get_height_base()) { + building = i; + } + //-- store height of building with lowest roof + if (lowestbuilding == -1 || std::get<0>(zstar[i]) < std::get<0>(zstar[lowestbuilding])) { + lowestbuilding = i; + } + } + else if (topoClass == WATER) { + water = i; } } - else if (topoClass == WATER) { - water = i; - } - } - //-- Deal with buildings. If there's a building and adjacent is not water, then this class - //-- get allocated the height value of the floor of the building. Any building will do if >1. - //-- Also ignore water so it doesn't get snapped to the floor of a building - if (building != -1) { - int baseheight = dynamic_cast(std::get<1>(zstar[building]))->get_height_base(); - for (auto& each : zstar) { - if (std::get<1>(each)->get_class() != BUILDING && std::get<1>(each)->get_class() != WATER) { - std::get<0>(each) = baseheight; - if (water != -1) { - //- add a vertical wall between the feature and the water - std::get<1>(each)->add_vertical_wall(); + //-- Deal with buildings. If there's a building and adjacent is not water, then this class + //-- get allocated the height value of the floor of the building. Any building will do if >1. + //-- Also ignore water so it doesn't get snapped to the floor of a building + if (building != -1) { + //-- push building heights if inner walls are needed and only once for the lowest building to overcome duplicates in the node column + if (_building_inner_walls && buildings.size() > 1 && _nc_inner_walls[key_bucket].empty()) { + int tmph = -99999; + for (auto i : buildings) { + int h = dynamic_cast(std::get<1>(zstar[i]))->get_height_base(); + if (h != tmph) { //-- not to repeat the same height + _nc_inner_walls[key_bucket].push_back(h); + tmph = h; + } } + // push lowest building roof height to seperate node column + _nc_inner_walls[key_bucket].push_back(std::get<0>(zstar[lowestbuilding])); } - else if (std::get<1>(each)->get_class() == BUILDING) { - std::get<1>(each)->add_vertical_wall(); + int baseheight = dynamic_cast(std::get<1>(zstar[building]))->get_height_base(); + for (auto& each : zstar) { + if (std::get<1>(each)->get_class() != BUILDING && std::get<1>(each)->get_class() != WATER) { + std::get<0>(each) = baseheight; + if (water != -1) { + //- add a vertical wall between the feature and the water + std::get<1>(each)->add_vertical_wall(); + } + } + else if (std::get<1>(each)->get_class() == BUILDING) { + std::get<1>(each)->add_vertical_wall(); + } } } - } - else { - for (std::vector< std::tuple< int, TopoFeature*, int, int > >::iterator it = zstar.begin(); it != zstar.end(); ++it) { - std::vector< std::tuple< int, TopoFeature*, int, int > >::iterator fnext = it; - for (std::vector< std::tuple< int, TopoFeature*, int, int > >::iterator it2 = it + 1; it2 != zstar.end(); ++it2) { - int deltaz = std::abs(std::get<0>(*it) - std::get<0>(*it2)); - // features are within threshold jump edge, handle various cases - if (deltaz < this->_threshold_jump_edges) { - fnext = it2; - // it and it2 are same class, set height to first since averaging doesn't work if >2 objects of same class within threshold - // this mainly applies for bridges and outlier detection of roads, otherwise it shouldn't be possible - if (std::get<1>(*it)->get_class() == std::get<1>(*it2)->get_class()) { - std::get<0>(*it2) = std::get<0>(*it); - } - // it is hard, it2 is hard - // keep height when both are hard surfaces, add vw - else if (std::get<1>(*it)->is_hard()) { - if (std::get<1>(*it2)->is_hard()) { - //-- add a wall to the heighest feature, it2 is allways highest since zstart is sorted by height - std::get<1>(*it2)->add_vertical_wall(); - } - // it is hard, it2 is soft - // set height of it2 to it - else { + else { + for (std::vector< std::tuple< int, TopoFeature*, int, int > >::iterator it = zstar.begin(); it != zstar.end(); ++it) { + std::vector< std::tuple< int, TopoFeature*, int, int > >::iterator fnext = it; + for (std::vector< std::tuple< int, TopoFeature*, int, int > >::iterator it2 = it + 1; it2 != zstar.end(); ++it2) { + int deltaz = std::abs(std::get<0>(*it) - std::get<0>(*it2)); + // features are within threshold jump edge, handle various cases + if (deltaz < this->_threshold_jump_edges) { + fnext = it2; + // it and it2 are same class, set height to first since averaging doesn't work if >2 objects of same class within threshold + // this mainly applies for bridges and outlier detection of roads, otherwise it shouldn't be possible + if (std::get<1>(*it)->get_class() == std::get<1>(*it2)->get_class()) { std::get<0>(*it2) = std::get<0>(*it); } + // it is hard, it2 is hard + // keep height when both are hard surfaces, add vw + else if (std::get<1>(*it)->is_hard()) { + if (std::get<1>(*it2)->is_hard()) { + //-- add a wall to the heighest feature, it2 is allways highest since zstart is sorted by height + std::get<1>(*it2)->add_vertical_wall(); + } + // it is hard, it2 is soft + // set height of it2 to it + else { + std::get<0>(*it2) = std::get<0>(*it); + } + } + // it is soft, it2 is hard + // set height of it to it2 + else if (std::get<1>(*it2)->is_hard()) { + std::get<0>(*it) = std::get<0>(*it2); + } } - // it is soft, it2 is hard - // set height of it to it2 - else if (std::get<1>(*it2)->is_hard()) { - std::get<0>(*it) = std::get<0>(*it2); - } - } - // features are outside threshold jump edges, add vw - else { - // stitch object withouth height to adjacent object which does have a height - if (std::get<0>(*it) == -9999 && std::get<0>(*it2) != -9999) { - std::get<0>(*it) = std::get<0>(*it2); - } - else if (std::get<0>(*it2) == -9999 && std::get<0>(*it) != -9999) { - std::get<0>(*it2) = std::get<0>(*it); - } + // features are outside threshold jump edges, add vw else { - //-- add a wall to the heighest feature, it2 is allways highest since zstart is sorted by height - std::get<1>(*it2)->add_vertical_wall(); + // stitch object withouth height to adjacent object which does have a height + if (std::get<0>(*it) == -9999 && std::get<0>(*it2) != -9999) { + std::get<0>(*it) = std::get<0>(*it2); + } + else if (std::get<0>(*it2) == -9999 && std::get<0>(*it) != -9999) { + std::get<0>(*it2) = std::get<0>(*it); + } + else { + //-- add a wall to the heighest feature, it2 is allways highest since zstart is sorted by height + std::get<1>(*it2)->add_vertical_wall(); + } } } - } - //-- Average heights of soft features within the jumpedge threshold counted from the lowest feature or skip to the next hard feature - // fnext is the last feature within threshold jump edge, average all soft in between - if (it != fnext) { - if (std::get<1>(*it)->is_hard() == false && std::get<1>(*fnext)->is_hard() == false) { - int totalz = 0; - int count = 0; - for (std::vector< std::tuple< int, TopoFeature*, int, int > >::iterator it2 = it; it2 != fnext + 1; ++it2) { - totalz += std::get<0>(*it2); - count++; - } - totalz = totalz / count; - for (std::vector< std::tuple< int, TopoFeature*, int, int > >::iterator it2 = it; it2 != fnext + 1; ++it2) { - std::get<0>(*it2) = totalz; + //-- Average heights of soft features within the jumpedge threshold counted from the lowest feature or skip to the next hard feature + // fnext is the last feature within threshold jump edge, average all soft in between + if (it != fnext) { + if (std::get<1>(*it)->is_hard() == false && std::get<1>(*fnext)->is_hard() == false) { + int totalz = 0; + int count = 0; + for (std::vector< std::tuple< int, TopoFeature*, int, int > >::iterator it2 = it; it2 != fnext + 1; ++it2) { + totalz += std::get<0>(*it2); + count++; + } + totalz = totalz / count; + for (std::vector< std::tuple< int, TopoFeature*, int, int > >::iterator it2 = it; it2 != fnext + 1; ++it2) { + std::get<0>(*it2) = totalz; + } } - } - else if (std::get<1>(*it)->is_hard() == false) { - // Adjust all intermediate soft features - for (std::vector< std::tuple< int, TopoFeature*, int, int > >::iterator it2 = it; it2 != fnext; ++it2) { - std::get<0>(*it2) = std::get<0>(*fnext); + else if (std::get<1>(*it)->is_hard() == false) { + // Adjust all intermediate soft features + for (std::vector< std::tuple< int, TopoFeature*, int, int > >::iterator it2 = it; it2 != fnext; ++it2) { + std::get<0>(*it2) = std::get<0>(*fnext); + } } + it = fnext; } - it = fnext; } } - } - //-- assign the adjusted heights and build the nc - int tmph = -99999; - for (auto& each : zstar) { - std::get<1>(each)->set_vertex_elevation(std::get<2>(each), std::get<3>(each), std::get<0>(each)); - if (std::get<0>(each) != tmph) { //-- not to repeat the same height - Point2 p = std::get<1>(each)->get_point2(std::get<2>(each), std::get<3>(each)); - _nc[gen_key_bucket(&p)].push_back(std::get<0>(each)); - tmph = std::get<0>(each); + //-- assign the adjusted heights and build the nc + int tmph = -99999; + for (auto& each : zstar) { + int h = std::get<0>(each); + std::get<1>(each)->set_vertex_elevation(std::get<2>(each), std::get<3>(each), h); + if (h != tmph) { //-- not to repeat the same height + _nc[key_bucket].push_back(h); + tmph = h; + } } } } @@ -1293,13 +1328,23 @@ void Map3d::stitch_jumpedge(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f f1->add_vertical_wall(); f2->add_vertical_wall(); _nc[key_bucket].push_back(f1z); - _nc[key_bucket].push_back(f2z); + if (f1z != f2z) { + _nc[key_bucket].push_back(f2z); + } int f1base = dynamic_cast(f1)->get_height_base(); int f2base = dynamic_cast(f2)->get_height_base(); _nc[key_bucket].push_back(f1base); if (f1base != f2base) { _nc[key_bucket].push_back(f2base); } + if (_building_inner_walls && _nc_inner_walls[key_bucket].empty()) { + _nc_inner_walls[key_bucket].push_back(f1base); + if (f1base != f2base) { + _nc_inner_walls[key_bucket].push_back(f2base); + } + // push lowest building roof height to seperate node column + _nc_inner_walls[key_bucket].push_back(std::min(f1z, f2z)); + } } else if (f1->get_class() == BUILDING) { if (f2->get_class() != WATER) { diff --git a/src/Map3d.h b/src/Map3d.h index ae72c36a..7a1b17f8 100644 --- a/src/Map3d.h +++ b/src/Map3d.h @@ -111,6 +111,7 @@ class Map3d { bool _building_triangulate; int _building_lod; bool _building_include_floor; + bool _building_inner_walls; int _terrain_simplification; int _forest_simplification; double _terrain_simplification_tinsimp; @@ -132,6 +133,7 @@ class Map3d { std::array,NUM_ALLOWEDLASTOPO> _las_classes_allowed; NodeColumn _nc; + NodeColumn _nc_inner_walls; std::vector _lsFeatures; std::vector _allowed_layers; bgi::rtree< PairIndexed, bgi::rstar<16> > _rtree; diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index 3cbec21d..ebdf18f6 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -487,22 +487,6 @@ void TopoFeature::construct_vertical_walls(const NodeColumn& nc, int baseheight) b = ring[ai + 1]; bi = ai + 1; } - //-- check if there's a nc for either - ncit = nc.find(gen_key_bucket(&a)); - if (ncit != nc.end()) { - anc = ncit->second; - // make values in anc unique - anc.erase(unique(anc.begin(), anc.end()), anc.end()); - } - ncit = nc.find(gen_key_bucket(&b)); - if (ncit != nc.end()) { - bnc = ncit->second; - // make values in bnc unique - bnc.erase(unique(bnc.begin(), bnc.end()), bnc.end()); - } - - if ((anc.empty() == true) && (bnc.empty() == true)) - continue; //-- find the adjacent polygon to segment ab (fadj) fadj = nullptr; @@ -511,7 +495,7 @@ void TopoFeature::construct_vertical_walls(const NodeColumn& nc, int baseheight) int adj_b_ringi = 0; int adj_b_pi = 0; for (auto& adj : *(_adjFeatures)) { - if (adj->has_segment(b, a, adj_b_ringi, adj_b_pi, adj_a_ringi, adj_a_pi) == true) { + if (adj->has_segment(b, a, adj_b_ringi, adj_b_pi, adj_a_ringi, adj_a_pi)) { fadj = adj; break; } @@ -532,8 +516,19 @@ void TopoFeature::construct_vertical_walls(const NodeColumn& nc, int baseheight) fadj_bz = fadj->get_vertex_elevation(adj_b_ringi, adj_b_pi); } -#pragma region bridges - //-- Make exeption for bridges, they have vw's from bottom up, swap . Also skip if adjacent feature is bridge, vw is then created by bridge + //-- check if there's a nc for either + ncit = nc.find(gen_key_bucket(&a)); + if (ncit != nc.end()) { + anc = ncit->second; + } + ncit = nc.find(gen_key_bucket(&b)); + if (ncit != nc.end()) { + bnc = ncit->second; + } + if ((anc.empty() == true) && (bnc.empty() == true)) + continue; + + //-- Make exeption for bridges, they have vw's from bottom up, swap. Also skip if adjacent feature is bridge, vw is then created by bridge if (this->get_class() == BRIDGE) { //-- find the height of the vertex in the node column std::vector::const_iterator sait, eait, sbit, ebit; @@ -588,129 +583,9 @@ void TopoFeature::construct_vertical_walls(const NodeColumn& nc, int baseheight) _triangles_vw.push_back(t); } } - if (fadj != nullptr && fadj->get_class() == BRIDGE) { + if (fadj->get_class() == BRIDGE) { continue; } -#pragma endregion - -#pragma region building inner walls - //-- add extra vw for the inner walls of buildings - if (this->get_class() == BUILDING) { - //-- create walls from lowest roof to ground, height jumps are created later - int tmp_az = az; - int tmp_bz = bz; - int tmp_fadj_az = baseheight; - int tmp_fadj_bz = baseheight; - bool adifferent = false; - bool bdifferent = false; - - //-- solve for the case two buildings are adjacent but their baseheights are different - // in case they are different start at the lowest height (which is always the baseheight of the adjacent building) - if (std::find(anc.begin(), anc.end(), tmp_fadj_az) == anc.end()) { - tmp_fadj_az = anc.front(); - adifferent = true; - } - if (std::find(bnc.begin(), bnc.end(), tmp_fadj_bz) == bnc.end()) { - tmp_fadj_bz = bnc.front(); - bdifferent = true; - } - if (adifferent) { - Point3 p; - p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(baseheight)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_fadj_az)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(baseheight)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - Triangle t; - int size = int(_vertices_vw.size()); - t.v0 = size - 2; - t.v1 = size - 3; - t.v2 = size - 1; - _triangles_vw.push_back(t); - } - if (bdifferent) { - Point3 p; - p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(baseheight)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(tmp_fadj_bz)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_fadj_az)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - Triangle t; - int size = int(_vertices_vw.size()); - t.v0 = size - 2; - t.v1 = size - 3; - t.v2 = size - 1; - _triangles_vw.push_back(t); - } - if (fadj->get_class() == BUILDING) { - if (az > fadj_az && bz > fadj_bz) { - tmp_az = fadj_az; - tmp_bz = fadj_bz; - } - - if (_id == "b31bc4dcc-00ba-11e6-b420-2bdcc4ab5d7f") { - std::cout << "stop"; - } - - if ((tmp_az >= tmp_fadj_az || tmp_bz >= tmp_fadj_bz) && (tmp_az != tmp_fadj_az && tmp_bz != tmp_fadj_bz)) { - //-- find the height of the vertex in the node column - std::vector::const_iterator sait, eait, sbit, ebit; - sait = std::find(anc.begin(), anc.end(), tmp_fadj_az); - eait = std::find(anc.begin(), anc.end(), tmp_az); - sbit = std::find(bnc.begin(), bnc.end(), tmp_fadj_bz); - ebit = std::find(bnc.begin(), bnc.end(), tmp_bz); - - //-- iterate to triangulate - while ((sbit != ebit) && (sbit != bnc.end()) && ((sbit + 1) != bnc.end())) { - Point3 p; - if (anc.size() == 0 || sait == anc.end()) { - p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_az)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - } - else { - p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - } - p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - sbit++; - p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - Triangle t; - int size = int(_vertices_vw.size()); - t.v0 = size - 2; - t.v1 = size - 3; - t.v2 = size - 1; - _triangles_vw.push_back(t); - } - while (sait != eait && sait != anc.end() && (sait + 1) != anc.end()) { - Point3 p; - if (bnc.size() == 0 || ebit == bnc.end()) { - p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(tmp_bz)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - } - else { - p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*ebit)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - } - p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - sait++; - p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - Triangle t; - int size = int(_vertices_vw.size()); - t.v0 = size - 3; - t.v1 = size - 2; - t.v2 = size - 1; - _triangles_vw.push_back(t); - } - } - } - } -#pragma endregion //-- check height differences: f > fadj for *both* Points a and b. if (az < fadj_az || bz < fadj_bz) { @@ -776,6 +651,368 @@ void TopoFeature::construct_vertical_walls(const NodeColumn& nc, int baseheight) } } +void TopoFeature::construct_inner_walls(const NodeColumn& nc, int baseheight) { + //-- gather all rings + std::vector therings; + therings.push_back(_p2->outer()); + for (Ring2& iring : _p2->inners()) + therings.push_back(iring); + + //-- process each vertex of the polygon separately + std::vector anc, bnc; + std::unordered_map>::const_iterator ncit; + Point2 a, b; + TopoFeature* fadj; + int ringi = -1; + for (Ring2& ring : therings) { + ringi++; + for (int ai = 0; ai < ring.size(); ai++) { + //-- Point a + a = ring[ai]; + //-- find Point b + if (ai == (ring.size() - 1)) { + b = ring.front(); + } + else { + b = ring[ai + 1]; + } + + //-- find the adjacent polygon to segment ab (fadj) + fadj = nullptr; + int adj_a_ringi = 0; + int adj_a_pi = 0; + int adj_b_ringi = 0; + int adj_b_pi = 0; + for (auto& adj : *(_adjFeatures)) { + if (adj->has_segment(b, a, adj_b_ringi, adj_b_pi, adj_a_ringi, adj_a_pi)) { + fadj = adj; + break; + } + } + if (fadj == nullptr) { + continue; + } + + if (fadj->get_class() == BUILDING) { + //-- check if there's a nc for either + ncit = nc.find(gen_key_bucket(&a)); + if (ncit != nc.end()) { + anc = ncit->second; + } + ncit = nc.find(gen_key_bucket(&b)); + if (ncit != nc.end()) { + bnc = ncit->second; + } + + if (anc.empty() && bnc.empty()) + continue; + //// if one of the nc is empty it a vertex within an inner wall, use other nc + //if (anc.empty()) + // anc = bnc; + //if (bnc.empty()) + // bnc = anc; + + int az = anc.back(); + int bz = bnc.back(); + + int fadj_az = baseheight; + int fadj_bz = baseheight; + + if (_id == "b31bd5f7b-00ba-11e6-b420-2bdcc4ab5d7f" && ringi == 0 && ai == 1) { + std::cout << 'stop'; + } + + + ////////////TODO + // make two triangles between the first and second height, second and third, etc, not the 'waaier' type + + //-- find the height of the vertex in the node column + std::vector::const_iterator sait, eait, sbit, ebit; + sait = std::find(anc.begin(), anc.end(), fadj_az); + eait = std::find(anc.begin(), anc.end(), az); + sbit = std::find(bnc.begin(), bnc.end(), fadj_bz); + ebit = std::find(bnc.begin(), bnc.end(), bz); + + //-- iterate to triangulate + while ((sbit != ebit) && (sbit != bnc.end()) && ((sbit + 1) != bnc.end())) { + Point3 p; + if (anc.size() == 0 || sait == anc.end()) { + p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(az)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + } + else { + p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + } + p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + sbit++; + p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + Triangle t; + int size = int(_vertices_vw.size()); + t.v0 = size - 2; + t.v1 = size - 3; + t.v2 = size - 1; + _triangles_vw.push_back(t); + } + while (sait != eait && sait != anc.end() && (sait + 1) != anc.end()) { + Point3 p; + if (bnc.size() == 0 || ebit == bnc.end()) { + p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(bz)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + } + else { + p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*ebit)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + } + p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + sait++; + p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + Triangle t; + int size = int(_vertices_vw.size()); + t.v0 = size - 3; + t.v1 = size - 2; + t.v2 = size - 1; + _triangles_vw.push_back(t); + } + } + } + } +} + +//void TopoFeature::construct_inner_walls(const NodeColumn& nc, const NodeColumn& ncbuilding, int baseheight) { +// //-- gather all rings +// std::vector therings; +// therings.push_back(_p2->outer()); +// for (Ring2& iring : _p2->inners()) +// therings.push_back(iring); +// +// //-- process each vertex of the polygon separately +// std::vector anc, bnc; +// std::unordered_map>::const_iterator ncit; +// Point2 a, b; +// TopoFeature* fadj; +// int ringi = -1; +// for (Ring2& ring : therings) { +// ringi++; +// for (int ai = 0; ai < ring.size(); ai++) { +// //-- Point a +// a = ring[ai]; +// //-- find Point b +// int bi; +// if (ai == (ring.size() - 1)) { +// b = ring.front(); +// bi = 0; +// } +// else { +// b = ring[ai + 1]; +// bi = ai + 1; +// } +// //-- check if there's a nc for either +// ncit = nc.find(gen_key_bucket(&a)); +// if (ncit != nc.end()) { +// anc = ncit->second; +// // make values in anc unique +// //anc.erase(unique(anc.begin(), anc.end()), anc.end()); +// } +// ncit = nc.find(gen_key_bucket(&b)); +// if (ncit != nc.end()) { +// bnc = ncit->second; +// // make values in bnc unique +// // bnc.erase(unique(bnc.begin(), bnc.end()), bnc.end()); +// } +// +// if ((anc.empty() == true) && (bnc.empty() == true)) +// continue; +// +// //-- find the adjacent polygon to segment ab (fadj) +// fadj = nullptr; +// int adj_a_ringi = 0; +// int adj_a_pi = 0; +// int adj_b_ringi = 0; +// int adj_b_pi = 0; +// for (auto& adj : *(_adjFeatures)) { +// if (adj->has_segment(b, a, adj_b_ringi, adj_b_pi, adj_a_ringi, adj_a_pi) == true) { +// fadj = adj; +// break; +// } +// } +// if (fadj == nullptr && this->get_class() != BUILDING) { +// continue; +// } +// +// int az = this->get_vertex_elevation(ringi, ai); +// int bz = this->get_vertex_elevation(ringi, bi); +// int fadj_az, fadj_bz; +// if (fadj == nullptr) { +// fadj_az = baseheight; +// fadj_bz = baseheight; +// } +// else { +// fadj_az = fadj->get_vertex_elevation(adj_a_ringi, adj_a_pi); +// fadj_bz = fadj->get_vertex_elevation(adj_b_ringi, adj_b_pi); +// } +// +// //-- add extra vw for the inner walls of buildings +// //-- create walls from lowest roof to ground, height jumps are created later +// int tmp_az = az; +// int tmp_bz = bz; +// int tmp_fadj_az = baseheight; +// int tmp_fadj_bz = baseheight; +// bool adifferent = false; +// bool bdifferent = false; +// +// if (_id == "b31bd5f7b-00ba-11e6-b420-2bdcc4ab5d7f" && ringi == 0 && ai == 1) { +// std::cout << 'stop'; +// } +// +// //-- solve for the case two buildings are adjacent but their baseheights are different +// // in case they are different start at the lowest height (which is always the baseheight of the adjacent building) +// if (std::find(anc.begin(), anc.end(), tmp_fadj_az) == anc.end()) { +// tmp_fadj_az = anc.front(); +// adifferent = true; +// } +// +// if (std::find(bnc.begin(), bnc.end(), tmp_fadj_bz) == bnc.end()) { +// tmp_fadj_bz = bnc.front(); +// bdifferent = true; +// } +// // Add extra triangle in case we have multiple buildings connected with different base heights +// if (adifferent && bdifferent) { +// if (tmp_fadj_bz > tmp_fadj_az) { +// Point3 p; +// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_fadj_az)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(tmp_fadj_az)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(tmp_fadj_bz)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// Triangle t; +// int size = int(_vertices_vw.size()); +// t.v0 = size - 2; +// t.v1 = size - 3; +// t.v2 = size - 1; +// _triangles_vw.push_back(t); +// +// //set tmp_fadj_bz to new height +// tmp_fadj_bz = tmp_fadj_az; +// } +// if (tmp_fadj_az > tmp_fadj_bz) { +// Point3 p; +// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_fadj_bz)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(tmp_fadj_bz)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_fadj_az)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// Triangle t; +// int size = int(_vertices_vw.size()); +// t.v0 = size - 2; +// t.v1 = size - 3; +// t.v2 = size - 1; +// _triangles_vw.push_back(t); +// //set tmp_fadj_az to new height +// tmp_fadj_az = tmp_fadj_bz; +// } +// } +// if (adifferent) { +// Point3 p; +// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(baseheight)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(baseheight)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_fadj_az)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// Triangle t; +// int size = int(_vertices_vw.size()); +// t.v0 = size - 2; +// t.v1 = size - 3; +// t.v2 = size - 1; +// _triangles_vw.push_back(t); +// } +// if (bdifferent) { +// Point3 p; +// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_fadj_az)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(baseheight)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(tmp_fadj_bz)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// Triangle t; +// int size = int(_vertices_vw.size()); +// t.v0 = size - 2; +// t.v1 = size - 3; +// t.v2 = size - 1; +// _triangles_vw.push_back(t); +// } +// +// if (fadj->get_class() == BUILDING) { +// if (az > fadj_az && bz > fadj_bz) { +// tmp_az = fadj_az; +// tmp_bz = fadj_bz; +// } +// if ((tmp_az >= tmp_fadj_az || tmp_bz >= tmp_fadj_bz) && (tmp_az != tmp_fadj_az && tmp_bz != tmp_fadj_bz)) { +// //-- find the height of the vertex in the node column +// std::vector::const_iterator sait, eait, sbit, ebit; +// sait = std::find(anc.begin(), anc.end(), tmp_fadj_az); +// eait = std::find(anc.begin(), anc.end(), tmp_az); +// sbit = std::find(bnc.begin(), bnc.end(), tmp_fadj_bz); +// ebit = std::find(bnc.begin(), bnc.end(), tmp_bz); +// +// //-- iterate to triangulate +// while ((sbit != ebit) && (sbit != bnc.end()) && ((sbit + 1) != bnc.end())) { +// Point3 p; +// if (anc.size() == 0 || sait == anc.end()) { +// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_az)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// } +// else { +// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// } +// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// sbit++; +// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// Triangle t; +// int size = int(_vertices_vw.size()); +// t.v0 = size - 2; +// t.v1 = size - 3; +// t.v2 = size - 1; +// _triangles_vw.push_back(t); +// } +// while (sait != eait && sait != anc.end() && (sait + 1) != anc.end()) { +// Point3 p; +// if (bnc.size() == 0 || ebit == bnc.end()) { +// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(tmp_bz)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// } +// else { +// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*ebit)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// } +// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// sait++; +// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); +// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); +// Triangle t; +// int size = int(_vertices_vw.size()); +// t.v0 = size - 3; +// t.v1 = size - 2; +// t.v2 = size - 1; +// _triangles_vw.push_back(t); +// } +// } +// } +// } +// } +//} + bool TopoFeature::has_segment(Point2& a, Point2& b, int& aringi, int& api, int& bringi, int& bpi) { double threshold = 0.001; double sqr_threshold = threshold * threshold; @@ -783,7 +1020,6 @@ bool TopoFeature::has_segment(Point2& a, Point2& b, int& aringi, int& api, int& Point2 tmp; if (this->has_point2(a, ringis, pis) == true) { for (int k = 0; k < ringis.size(); k++) { - // nextpi = pis[k]; int nextpi; tmp = this->get_next_point2_in_ring(ringis[k], pis[k], nextpi); if (sqr_distance(b, tmp) <= sqr_threshold) { diff --git a/src/TopoFeature.h b/src/TopoFeature.h index 03f9f75d..bef5526b 100644 --- a/src/TopoFeature.h +++ b/src/TopoFeature.h @@ -53,6 +53,7 @@ class TopoFeature { std::string get_id(); void construct_vertical_walls(const NodeColumn& nc, int baseheight); + void construct_inner_walls(const NodeColumn& nc, int baseheight); void fix_bowtie(); void add_adjacent_feature(TopoFeature* adjFeature); std::vector* get_adjacent_features(); diff --git a/src/geomtools.cpp b/src/geomtools.cpp index 226e8268..2fd42306 100644 --- a/src/geomtools.cpp +++ b/src/geomtools.cpp @@ -220,7 +220,7 @@ std::string gen_key_bucket(const Point3* p) { return ss.str(); } -std::string gen_key_bucket(const Point3* p, int z) { +std::string gen_key_bucket(const Point3* p, float z) { std::stringstream ss; ss << std::fixed << std::setprecision(3) << p->get<0>() << " " << p->get<1>() << " " << z; return ss.str(); diff --git a/src/geomtools.h b/src/geomtools.h index 85905710..7dee0da0 100644 --- a/src/geomtools.h +++ b/src/geomtools.h @@ -34,7 +34,7 @@ std::string gen_key_bucket(const Point2* p); std::string gen_key_bucket(const Point3* p); -std::string gen_key_bucket(const Point3* p, int z); +std::string gen_key_bucket(const Point3* p, float z); double distance(const Point2 &p1, const Point2 &p2); double sqr_distance(const Point2 &p1, const Point2 &p2); From 0ecccc49253bcdf47a460e9d69e2d6861173a997 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Mon, 28 May 2018 10:06:04 +0200 Subject: [PATCH 08/45] Split building walls (including NC) from other VW's --- src/Map3d.cpp | 174 +++++++++++++++++++++++++++++++++----------- src/TopoFeature.cpp | 169 ++++++++++++++++++++++++------------------ 2 files changed, 230 insertions(+), 113 deletions(-) diff --git a/src/Map3d.cpp b/src/Map3d.cpp index 4f711daa..dfaaa26f 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -1122,7 +1122,7 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st //-- get p and key_bucket once and check if nc location is empty Point2 p = f->get_point2(ringi, pi); std::string key_bucket = gen_key_bucket(&p); - if (_nc[key_bucket].empty()) { + if (_nc[key_bucket].empty() && _nc_inner_walls[key_bucket].empty()) { //-- degree of vertex == 2 if (star.size() == 1) { if (std::get<0>(star[0])->get_class() != BRIDGE) { @@ -1169,10 +1169,6 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st return std::get<0>(t1) < std::get<0>(t2); }); - if (f->get_id() == "b9f71cae4-00c9-11e6-b420-2bdcc4ab5d7f" && pi==1) { - std::cout << "stop"; - } - //-- Identify buildings and water int building = -1; int water = -1; @@ -1181,6 +1177,8 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st for (int i = 0; i < zstar.size(); i++) { TopoClass topoClass = std::get<1>(zstar[i])->get_class(); if (topoClass == BUILDING) { + //add vw to all buildings + std::get<1>(zstar[i])->add_vertical_wall(); //-- store building indexes buildings.push_back(i); //-- set building to the one with the highest base @@ -1202,18 +1200,16 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st //-- Also ignore water so it doesn't get snapped to the floor of a building if (building != -1) { //-- push building heights if inner walls are needed and only once for the lowest building to overcome duplicates in the node column - if (_building_inner_walls && buildings.size() > 1 && _nc_inner_walls[key_bucket].empty()) { - int tmph = -99999; - for (auto i : buildings) { - int h = dynamic_cast(std::get<1>(zstar[i]))->get_height_base(); - if (h != tmph) { //-- not to repeat the same height - _nc_inner_walls[key_bucket].push_back(h); - tmph = h; - } + int tmph = -99999; + for (auto i : buildings) { + int h = dynamic_cast(std::get<1>(zstar[i]))->get_height_base(); + if (h != tmph) { //-- not to repeat the same height + _nc_inner_walls[key_bucket].push_back(h); + tmph = h; } - // push lowest building roof height to seperate node column - _nc_inner_walls[key_bucket].push_back(std::get<0>(zstar[lowestbuilding])); } + // push lowest building roof height to seperate node column + _nc_inner_walls[key_bucket].push_back(std::get<0>(zstar[lowestbuilding])); int baseheight = dynamic_cast(std::get<1>(zstar[building]))->get_height_base(); for (auto& each : zstar) { if (std::get<1>(each)->get_class() != BUILDING && std::get<1>(each)->get_class() != WATER) { @@ -1223,9 +1219,6 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st std::get<1>(each)->add_vertical_wall(); } } - else if (std::get<1>(each)->get_class() == BUILDING) { - std::get<1>(each)->add_vertical_wall(); - } } } else { @@ -1322,28 +1315,17 @@ void Map3d::stitch_jumpedge(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f int f2z = f2->get_vertex_elevation(ringi2, pi2); //-- Buildings involved - if ((f1->get_class() == BUILDING) || (f2->get_class() == BUILDING)) { - if ((f1->get_class() == BUILDING) && (f2->get_class() == BUILDING)) { - // add a wall to both buildings - f1->add_vertical_wall(); - f2->add_vertical_wall(); - _nc[key_bucket].push_back(f1z); - if (f1z != f2z) { - _nc[key_bucket].push_back(f2z); - } + if (f1->get_class() == BUILDING || f2->get_class() == BUILDING) { + if (f1->get_class() == BUILDING && f2->get_class() == BUILDING) { int f1base = dynamic_cast(f1)->get_height_base(); int f2base = dynamic_cast(f2)->get_height_base(); - _nc[key_bucket].push_back(f1base); + _nc_inner_walls[key_bucket].push_back(f1base); if (f1base != f2base) { - _nc[key_bucket].push_back(f2base); + _nc_inner_walls[key_bucket].push_back(f2base); } - if (_building_inner_walls && _nc_inner_walls[key_bucket].empty()) { - _nc_inner_walls[key_bucket].push_back(f1base); - if (f1base != f2base) { - _nc_inner_walls[key_bucket].push_back(f2base); - } - // push lowest building roof height to seperate node column - _nc_inner_walls[key_bucket].push_back(std::min(f1z, f2z)); + _nc_inner_walls[key_bucket].push_back(f1z); + if (f1z != f2z) { + _nc_inner_walls[key_bucket].push_back(f2z); } } else if (f1->get_class() == BUILDING) { @@ -1352,12 +1334,11 @@ void Map3d::stitch_jumpedge(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f } else { //- keep water flat, add the water height to the nc - _nc[key_bucket].push_back(f2z); + _nc_inner_walls[key_bucket].push_back(f2z); } //- expect a building to always be heighest adjacent feature - f1->add_vertical_wall(); - _nc[key_bucket].push_back(f1z); - _nc[key_bucket].push_back(dynamic_cast(f1)->get_height_base()); + _nc_inner_walls[key_bucket].push_back(dynamic_cast(f1)->get_height_base()); + _nc_inner_walls[key_bucket].push_back(f1z); } else { //-- f2 is Building if (f1->get_class() != WATER) { @@ -1365,13 +1346,14 @@ void Map3d::stitch_jumpedge(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f } else { //- keep water flat, add the water height to the nc - _nc[key_bucket].push_back(f1z); + _nc_inner_walls[key_bucket].push_back(f1z); } //- expect a building to always be heighest adjacent feature - f2->add_vertical_wall(); - _nc[key_bucket].push_back(f2z); - _nc[key_bucket].push_back(dynamic_cast(f2)->get_height_base()); + _nc_inner_walls[key_bucket].push_back(dynamic_cast(f2)->get_height_base()); + _nc_inner_walls[key_bucket].push_back(f2z); } + //sort the nc since the water can be inbetween the floor and roof of a building + std::sort(_nc_inner_walls[key_bucket].begin(), _nc_inner_walls[key_bucket].end()); } //-- no Buildings involved else { @@ -1419,6 +1401,110 @@ void Map3d::stitch_jumpedge(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f } } +//void Map3d::stitch_jumpedge(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f2, int ringi2, int pi2) { +// Point2 p = f1->get_point2(ringi1, pi1); +// std::string key_bucket = gen_key_bucket(&p); +// int f1z = f1->get_vertex_elevation(ringi1, pi1); +// int f2z = f2->get_vertex_elevation(ringi2, pi2); +// +// //-- Buildings involved +// if ((f1->get_class() == BUILDING) || (f2->get_class() == BUILDING)) { +// if ((f1->get_class() == BUILDING) && (f2->get_class() == BUILDING)) { +// // add a wall to both buildings +// f1->add_vertical_wall(); +// f2->add_vertical_wall(); +// _nc[key_bucket].push_back(f1z); +// if (f1z != f2z) { +// _nc[key_bucket].push_back(f2z); +// } +// int f1base = dynamic_cast(f1)->get_height_base(); +// int f2base = dynamic_cast(f2)->get_height_base(); +// _nc[key_bucket].push_back(f1base); +// if (f1base != f2base) { +// _nc[key_bucket].push_back(f2base); +// } +// if (_building_inner_walls && _nc_inner_walls[key_bucket].empty()) { +// _nc_inner_walls[key_bucket].push_back(f1base); +// if (f1base != f2base) { +// _nc_inner_walls[key_bucket].push_back(f2base); +// } +// // push lowest building roof height to seperate node column +// _nc_inner_walls[key_bucket].push_back(std::min(f1z, f2z)); +// } +// } +// else if (f1->get_class() == BUILDING) { +// if (f2->get_class() != WATER) { +// f2->set_vertex_elevation(ringi2, pi2, dynamic_cast(f1)->get_height_base()); +// } +// else { +// //- keep water flat, add the water height to the nc +// _nc[key_bucket].push_back(f2z); +// } +// //- expect a building to always be heighest adjacent feature +// f1->add_vertical_wall(); +// _nc[key_bucket].push_back(f1z); +// _nc[key_bucket].push_back(dynamic_cast(f1)->get_height_base()); +// } +// else { //-- f2 is Building +// if (f1->get_class() != WATER) { +// f1->set_vertex_elevation(ringi1, pi1, dynamic_cast(f2)->get_height_base()); +// } +// else { +// //- keep water flat, add the water height to the nc +// _nc[key_bucket].push_back(f1z); +// } +// //- expect a building to always be heighest adjacent feature +// f2->add_vertical_wall(); +// _nc[key_bucket].push_back(f2z); +// _nc[key_bucket].push_back(dynamic_cast(f2)->get_height_base()); +// } +// } +// //-- no Buildings involved +// else { +// bool bStitched = false; +// int deltaz = std::abs(f1z - f2z); +// if (deltaz < this->_threshold_jump_edges) { +// //-- handle same class, average them +// if (f1->get_class() == f2->get_class()) { +// int avgz = (f1->get_vertex_elevation(ringi1, pi1) + f2->get_vertex_elevation(ringi2, pi2)) / 2; +// f1->set_vertex_elevation(ringi1, pi1, avgz); +// f2->set_vertex_elevation(ringi2, pi2, avgz); +// _nc[key_bucket].push_back(avgz); +// bStitched = true; +// } +// if (f1->is_hard() == false) { +// f1->set_vertex_elevation(ringi1, pi1, f2z); +// bStitched = true; +// } +// else if (f2->is_hard() == false) { +// f2->set_vertex_elevation(ringi2, pi2, f1z); +// bStitched = true; +// } +// } +// //-- then vertical walls must be added: nc to highest +// if (bStitched == false) { +// // stitch object withouth height to adjacent object which does have a height +// if (f1z == -9999 && f2z != -9999) { +// f1->set_vertex_elevation(ringi1, pi1, f2z); +// } +// else if (f2z == -9999 && f1z != -9999) { +// f2->set_vertex_elevation(ringi2, pi2, f1z); +// } +// else { +// //- add a wall to the heighest feature +// if (f1z > f2z) { +// f1->add_vertical_wall(); +// } +// else if (f2z > f1z) { +// f2->add_vertical_wall(); +// } +// _nc[key_bucket].push_back(f1z); +// _nc[key_bucket].push_back(f2z); +// } +// } +// } +//} + void Map3d::stitch_average(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f2, int ringi2, int pi2) { int avgz = (f1->get_vertex_elevation(ringi1, pi1) + f2->get_vertex_elevation(ringi2, pi2)) / 2; f1->set_vertex_elevation(ringi1, pi1, avgz); diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index ebdf18f6..94e1dcbb 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -659,8 +659,6 @@ void TopoFeature::construct_inner_walls(const NodeColumn& nc, int baseheight) { therings.push_back(iring); //-- process each vertex of the polygon separately - std::vector anc, bnc; - std::unordered_map>::const_iterator ncit; Point2 a, b; TopoFeature* fadj; int ringi = -1; @@ -693,90 +691,123 @@ void TopoFeature::construct_inner_walls(const NodeColumn& nc, int baseheight) { continue; } - if (fadj->get_class() == BUILDING) { - //-- check if there's a nc for either - ncit = nc.find(gen_key_bucket(&a)); - if (ncit != nc.end()) { - anc = ncit->second; - } - ncit = nc.find(gen_key_bucket(&b)); - if (ncit != nc.end()) { - bnc = ncit->second; - } + std::unordered_map>::const_iterator ncit; + std::vector anc, bnc; + //-- check if there's a nc for either + ncit = nc.find(gen_key_bucket(&a)); + if (ncit != nc.end()) { + anc = ncit->second; + } + ncit = nc.find(gen_key_bucket(&b)); + if (ncit != nc.end()) { + bnc = ncit->second; + } - if (anc.empty() && bnc.empty()) - continue; - //// if one of the nc is empty it a vertex within an inner wall, use other nc - //if (anc.empty()) - // anc = bnc; - //if (bnc.empty()) - // bnc = anc; + if (anc.empty() && bnc.empty()) + continue; + // if one of the nc is empty something is wrong + if (anc.empty() || bnc.empty()) { + std::cerr << "ERROR: the inner wall node column is empty for building: " << _id << std::endl; + break; + } - int az = anc.back(); - int bz = bnc.back(); + if (_id == "b31bc9c3c-00ba-11e6-b420-2bdcc4ab5d7f") { + std::cout << "stop"; + } - int fadj_az = baseheight; - int fadj_bz = baseheight; + std::vector awall, bwall, enda, endb; + std::vector; - if (_id == "b31bd5f7b-00ba-11e6-b420-2bdcc4ab5d7f" && ringi == 0 && ai == 1) { - std::cout << 'stop'; + //if (building_floor || fadj->get_class() != BUILDING) { + if (true || fadj->get_class() != BUILDING) { + awall.push_back(anc.front()); + bwall.push_back(bnc.front()); + } + //if (inner_walls) { + if (true) { + if (anc.front() != baseheight) { + awall.push_back(baseheight); + } + if (bnc.front() != baseheight) { + bwall.push_back(baseheight); + } + } + if (fadj->get_class() == BUILDING) { + int fadjroofheight = fadj->get_vertex_elevation(ringi, ai); + if (anc.back() != fadjroofheight) { + awall.push_back(fadjroofheight); } + if (bnc.back() != fadjroofheight) { + bwall.push_back(fadjroofheight); + } + } + ////////TODO: fix array, maybe add all and make unique?? - ////////////TODO - // make two triangles between the first and second height, second and third, etc, not the 'waaier' type + awall.push_back(anc.back()); + bwall.push_back(bnc.back()); + + int fadj_az = baseheight; + int fadj_bz = baseheight; + + //-- find the height of the vertex in the node column + std::vector::const_iterator sait, eait, sbit, ebit; + for (int i = 0; i < awall.size() - 1; i++) { + sait = std::find(anc.begin(), anc.end(), awall[i]); + sbit = std::find(bnc.begin(), bnc.end(), bwall[i]); + eait = std::find(anc.begin(), anc.end(), awall[i + 1]); + ebit = std::find(bnc.begin(), bnc.end(), bwall[i + 1]); + + // sait = std::find(anc.begin(), anc.end(), fadj_az); + // sbit = std::find(bnc.begin(), bnc.end(), fadj_bz); + // eait = anc.end(); + // ebit = bnc.end(); + // + ////if building_floors + // // for walls from floor upto building wall stitch to adjacent object + // if (fadj->get_class() != BUILDING) { + // //eait=eait-2; + // //ebit=ebit-2; + // eait = std::find(anc.begin(), anc.end(), baseheight); + // ebit = std::find(bnc.begin(), bnc.end(), baseheight); + // } + ////if building_floors - //-- find the height of the vertex in the node column - std::vector::const_iterator sait, eait, sbit, ebit; - sait = std::find(anc.begin(), anc.end(), fadj_az); - eait = std::find(anc.begin(), anc.end(), az); - sbit = std::find(bnc.begin(), bnc.end(), fadj_bz); - ebit = std::find(bnc.begin(), bnc.end(), bz); //-- iterate to triangulate - while ((sbit != ebit) && (sbit != bnc.end()) && ((sbit + 1) != bnc.end())) { + while (sbit != ebit && sbit != bnc.end() && (sbit + 1) != bnc.end() && + sait != eait && sait != anc.end() && (sait + 1) != anc.end()) { Point3 p; - if (anc.size() == 0 || sait == anc.end()) { - p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(az)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - } - else { + Triangle t; + int size; + if (sbit + 1 != bnc.end()) { p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - } - p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - sbit++; - p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - Triangle t; - int size = int(_vertices_vw.size()); - t.v0 = size - 2; - t.v1 = size - 3; - t.v2 = size - 1; - _triangles_vw.push_back(t); - } - while (sait != eait && sait != anc.end() && (sait + 1) != anc.end()) { - Point3 p; - if (bnc.size() == 0 || ebit == bnc.end()) { - p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(bz)); + p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + sbit++; + p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + size = int(_vertices_vw.size()); + t.v0 = size - 2; + t.v1 = size - 3; + t.v2 = size - 1; + _triangles_vw.push_back(t); } - else { - p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*ebit)); + if (sait + 1 != anc.end()) { + p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + sait++; + p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + size = int(_vertices_vw.size()); + t.v0 = size - 3; + t.v1 = size - 2; + t.v2 = size - 1; + _triangles_vw.push_back(t); } - p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - sait++; - p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - Triangle t; - int size = int(_vertices_vw.size()); - t.v0 = size - 3; - t.v1 = size - 2; - t.v2 = size - 1; - _triangles_vw.push_back(t); } } } From 636e9e785273e886e9967c12e8cec8bd41f49d35 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Mon, 28 May 2018 17:46:55 +0200 Subject: [PATCH 09/45] now produce valid solids in obj and citygml --- src/Building.cpp | 242 +++++++++++++++++++++++ src/Building.h | 1 + src/Map3d.cpp | 238 +++++----------------- src/Map3d.h | 2 +- src/TopoFeature.cpp | 466 +++----------------------------------------- src/TopoFeature.h | 3 +- 6 files changed, 330 insertions(+), 622 deletions(-) diff --git a/src/Building.cpp b/src/Building.cpp index 2eac8a1d..c94e0708 100644 --- a/src/Building.cpp +++ b/src/Building.cpp @@ -118,6 +118,248 @@ bool Building::add_elevation_point(Point2 &p, double z, float radius, int lascla return true; } +void Building::construct_building_walls(const NodeColumn& nc, int baseheight, bool building_inner_walls, bool building_floor) { + //-- gather all rings + std::vector therings; + therings.push_back(_p2->outer()); + for (Ring2& iring : _p2->inners()) + therings.push_back(iring); + + //-- process each vertex of the polygon separately + Point2 a, b; + TopoFeature* fadj; + int ringi = -1; + for (Ring2& ring : therings) { + ringi++; + for (int ai = 0; ai < ring.size(); ai++) { + //-- Point a + a = ring[ai]; + //-- find Point b + int bi; + if (ai == (ring.size() - 1)) { + b = ring.front(); + bi = 0; + } + else { + b = ring[ai + 1]; + bi = ai + 1; + } + + //-- find the adjacent polygon to segment ab (fadj) + fadj = nullptr; + int adj_a_ringi = 0; + int adj_a_pi = 0; + int adj_b_ringi = 0; + int adj_b_pi = 0; + for (auto& adj : *(_adjFeatures)) { + if (adj->has_segment(b, a, adj_b_ringi, adj_b_pi, adj_a_ringi, adj_a_pi)) { + fadj = adj; + break; + } + } + if (fadj == nullptr) { + continue; + } + + std::unordered_map>::const_iterator ncit; + std::vector anc, bnc; + //-- check if there's a nc for either + ncit = nc.find(gen_key_bucket(&a)); + if (ncit != nc.end()) { + anc = ncit->second; + } + ncit = nc.find(gen_key_bucket(&b)); + if (ncit != nc.end()) { + bnc = ncit->second; + } + + if (anc.empty() && bnc.empty()) + continue; + // if one of the nc is empty something is wrong + if (anc.empty() || bnc.empty()) { + std::cerr << "ERROR: the inner wall node column is empty for building: " << _id << std::endl; + break; + } + + std::vector awall, bwall, awallend, bwallend; + //-- find the height of the vertex in the node column + std::vector::const_iterator sait, eait, sbit, ebit; + int roofheight = this->get_vertex_elevation(ringi, ai); + int baseheight = this->get_height_base(); + if (fadj->get_class() != BUILDING) { + // start at adjacent height for correct stitching if no floor + if (building_floor) { + awall.push_back(baseheight); + bwall.push_back(baseheight); + } + else { + awall.push_back(fadj->get_vertex_elevation(adj_a_ringi, adj_a_pi)); + bwall.push_back(fadj->get_vertex_elevation(adj_b_ringi, adj_b_pi)); + } + // end at own roof height + awallend.push_back(roofheight); + bwallend.push_back(roofheight); + } + else { // case of shared wall between two connected buildings + int adjbaseheight = dynamic_cast(fadj)->get_height_base(); + int adjroofheight = fadj->get_vertex_elevation(adj_a_ringi, adj_a_pi); + int base = baseheight; + if (building_floor && baseheight < adjbaseheight) { + awall.push_back(baseheight); + awallend.push_back(adjbaseheight); + //store base for inner walls check + base = adjbaseheight; + } + + if (building_inner_walls) { + awall.push_back(base); + if (roofheight > adjroofheight) { + awallend.push_back(adjroofheight); + } + else { + awallend.push_back(roofheight); + } + + } + if (roofheight > adjroofheight) { + awall.push_back(adjroofheight); + awallend.push_back(roofheight); + } + bwall = awall; + bwallend = awallend; + } + + + // if (building_floor) { + // // anc.front() -> baseheight + // // bnc.front() -> baseheight + // if (anc.front() != baseheight) { + // awall.push_back(anc.front()); + // awallend.push_back(baseheight); + // } + // if (bnc.front() != baseheight) { + // bwall.push_back(bnc.front()); + // bwallend.push_back(baseheight); + // } + // } + // bool fadjlower = false; + // int fadjz = fadj->get_vertex_elevation(adj_a_ringi, adj_a_pi); + // if (roofheight > fadjz) { + // fadjlower = true; + // } + + // // fadj == BUILDING is known here + // if (building_inner_walls) { + // //baseheight -> roofheight + // //baseheight -> roofheight + // //or if fadj < roofheight + // //baseheight -> fadjheight + // //baseheight -> fadjheight + // //fadjheight -> roofheight + // //fadjheight -> roofheight + + // awall.push_back(baseheight); + // if (fadjlower) { + // awallend.push_back(fadjz); + // awall.push_back(fadjz); + // } + + // bwall.push_back(baseheight); + // if (fadjlower) { + // bwallend.push_back(fadjz); + // bwall.push_back(fadjz); + // } + // } + // if (fadjlower) { + // //fadjheight -> roofheight + // //fadjheight -> roofheight + // awall.push_back(fadjz); + // bwall.push_back(fadjz); + // } + // awallend.push_back(roofheight); + // bwallend.push_back(roofheight); + //} + + for (int i = 0; i < awall.size(); i++) { + sait = std::find(anc.begin(), anc.end(), awall[i]); + sbit = std::find(bnc.begin(), bnc.end(), bwall[i]); + eait = std::find(anc.begin(), anc.end(), awallend[i]); + ebit = std::find(bnc.begin(), bnc.end(), bwallend[i]); + + //-- iterate to triangulate + while (sbit != ebit && sbit != bnc.end() && sbit + 1 != bnc.end()) { + Point3 p; + p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + sbit++; + p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + Triangle t; + int size = int(_vertices_vw.size()); + t.v0 = size - 2; + t.v1 = size - 3; + t.v2 = size - 1; + _triangles_vw.push_back(t); + } + while (sait != eait && sait != anc.end() && sait + 1 != anc.end()) { + Point3 p; + p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*ebit)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + sait++; + p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); + _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + Triangle t; + int size = int(_vertices_vw.size()); + t.v0 = size - 3; + t.v1 = size - 2; + t.v2 = size - 1; + _triangles_vw.push_back(t); + } + + ////-- iterate to triangulate + //while (sbit != ebit && sbit != bnc.end() && (sbit + 1) != bnc.end() && + // sait != eait && sait != anc.end() && (sait + 1) != anc.end()) { + // Point3 p; + // Triangle t; + // int size; + // if (sbit + 1 != bnc.end()) { + // p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); + // _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + // p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); + // _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + // sbit++; + // p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); + // _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + // size = int(_vertices_vw.size()); + // t.v0 = size - 2; + // t.v1 = size - 3; + // t.v2 = size - 1; + // _triangles_vw.push_back(t); + // } + // if (sait + 1 != anc.end()) { + // p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); + // _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + // p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); + // _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + // sait++; + // p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); + // _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); + // size = int(_vertices_vw.size()); + // t.v0 = size - 3; + // t.v1 = size - 2; + // t.v2 = size - 1; + // _triangles_vw.push_back(t); + // } + //} + } + } + } +} + int Building::get_height_base() { return _height_base; } diff --git a/src/Building.h b/src/Building.h index 5ef13414..26250125 100644 --- a/src/Building.h +++ b/src/Building.h @@ -36,6 +36,7 @@ class Building: public Flat { Building(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref_top, float heightref_base, bool building_triangulate, bool building_include_floor); bool lift(); bool add_elevation_point(Point2 &p, double z, float radius, int lasclass); + void construct_building_walls(const NodeColumn& nc, int baseheight, bool building_inner_walls, bool building_floor); void get_obj(std::unordered_map< std::string, unsigned long > &dPts, int lod, std::string mtl, std::string &fs); void get_citygml(std::wostream& of); void get_citygml_imgeo(std::wostream& of); diff --git a/src/Map3d.cpp b/src/Map3d.cpp index dfaaa26f..04b961f0 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -698,10 +698,15 @@ bool Map3d::threeDfy(bool stitching) { // make values in nc unique nc.second.erase(unique(nc.second.begin(), nc.second.end()), nc.second.end()); } + for (auto& nc : _nc_building_walls) { + std::sort(nc.second.begin(), nc.second.end()); + // make values in nc unique + nc.second.erase(unique(nc.second.begin(), nc.second.end()), nc.second.end()); + } std::clog << "===== /BOWTIES =====\n"; for (auto& f : _lsFeatures) { - if (f->has_vertical_walls() == true) { + if (f->has_vertical_walls()) { f->fix_bowtie(); } } @@ -709,15 +714,13 @@ bool Map3d::threeDfy(bool stitching) { std::clog << "===== /VERTICAL WALLS =====\n"; for (auto& f : _lsFeatures) { - if (f->has_vertical_walls() == true) { - int baseheight = 0; - if (f->get_class() == BUILDING) { - baseheight = dynamic_cast(f)->get_height_base(); - if (_building_inner_walls) { - f->construct_inner_walls(_nc_inner_walls, baseheight); - } - } - f->construct_vertical_walls(_nc, baseheight); + if (f->get_class() == BUILDING) { + Building* b = dynamic_cast(f); + int baseheight = b->get_height_base(); + b->construct_building_walls(_nc_building_walls, baseheight, _building_inner_walls, _building_include_floor); + } + else if (f->has_vertical_walls()) { + f->construct_vertical_walls(_nc); } } std::clog << "===== VERTICAL WALLS/ =====\n"; @@ -1052,48 +1055,26 @@ void Map3d::stitch_lifted_features() { std::vector ringis, pis; for (auto& f : _lsFeatures) { if (f->get_class() != BRIDGE) { - //-- 1. store all touching top level (adjacent + incident) - std::vector* lstouching = f->get_adjacent_features(); - //-- 2. build the node-column for each vertex - // oring - Ring2 oring = f->get_Polygon2()->outer(); - for (int i = 0; i < oring.size(); i++) { - std::vector< std::tuple > star; - bool toprocess = false; - for (auto& fadj : *lstouching) { - ringis.clear(); - pis.clear(); - if (fadj->has_point2(oring[i], ringis, pis) == true) { - for (int k = 0; k < ringis.size(); k++) { - toprocess = true; - star.push_back(std::make_tuple(fadj, ringis[k], pis[k])); - } - } - } - if (toprocess == true) { - this->stitch_one_vertex(f, 0, i, star); - } - else if (f->get_class() == BUILDING) { - f->add_vertical_wall(); - Point2 tmp = f->get_point2(0, i); - std::string key_bucket = gen_key_bucket(&tmp); - int z = f->get_vertex_elevation(0, i); - _nc[key_bucket].push_back(z); - z = dynamic_cast(f)->get_height_base(); - _nc[key_bucket].push_back(z); - } - } - // irings - int noiring = 0; - for (Ring2& iring : f->get_Polygon2()->inners()) { - noiring++; - for (int i = 0; i < iring.size(); i++) { + //-- gather all rings + std::vector therings; + Polygon2* poly = f->get_Polygon2(); + therings.push_back(poly->outer()); + for (Ring2& iring : poly->inners()) + therings.push_back(iring); + + int ringi = -1; + for (Ring2& ring : therings) { + ringi++; + //-- 1. store all touching top level (adjacent + incident) + std::vector* lstouching = f->get_adjacent_features(); + //-- 2. build the node-column for each vertex + for (int i = 0; i < ring.size(); i++) { std::vector< std::tuple > star; bool toprocess = false; for (auto& fadj : *lstouching) { ringis.clear(); pis.clear(); - if (fadj->has_point2(iring[i], ringis, pis) == true) { + if (fadj->has_point2(ring[i], ringis, pis) == true) { for (int k = 0; k < ringis.size(); k++) { toprocess = true; star.push_back(std::make_tuple(fadj, ringis[k], pis[k])); @@ -1101,16 +1082,15 @@ void Map3d::stitch_lifted_features() { } } if (toprocess == true) { - this->stitch_one_vertex(f, noiring, i, star); + this->stitch_one_vertex(f, ringi, i, star); } else if (f->get_class() == BUILDING) { - f->add_vertical_wall(); - Point2 tmp = f->get_point2(0, i); + Point2 tmp = f->get_point2(ringi, i); std::string key_bucket = gen_key_bucket(&tmp); - int z = f->get_vertex_elevation(0, i); - _nc[key_bucket].push_back(z); - z = dynamic_cast(f)->get_height_base(); - _nc[key_bucket].push_back(z); + int z = dynamic_cast(f)->get_height_base(); + _nc_building_walls[key_bucket].push_back(z); + z = f->get_vertex_elevation(ringi, i); + _nc_building_walls[key_bucket].push_back(z); } } } @@ -1122,7 +1102,7 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st //-- get p and key_bucket once and check if nc location is empty Point2 p = f->get_point2(ringi, pi); std::string key_bucket = gen_key_bucket(&p); - if (_nc[key_bucket].empty() && _nc_inner_walls[key_bucket].empty()) { + if (_nc[key_bucket].empty() && _nc_building_walls[key_bucket].empty()) { //-- degree of vertex == 2 if (star.size() == 1) { if (std::get<0>(star[0])->get_class() != BRIDGE) { @@ -1177,18 +1157,12 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st for (int i = 0; i < zstar.size(); i++) { TopoClass topoClass = std::get<1>(zstar[i])->get_class(); if (topoClass == BUILDING) { - //add vw to all buildings - std::get<1>(zstar[i])->add_vertical_wall(); //-- store building indexes buildings.push_back(i); //-- set building to the one with the highest base if (building == -1 || dynamic_cast(std::get<1>(zstar[i]))->get_height_base() > dynamic_cast(std::get<1>(zstar[building]))->get_height_base()) { building = i; } - //-- store height of building with lowest roof - if (lowestbuilding == -1 || std::get<0>(zstar[i]) < std::get<0>(zstar[lowestbuilding])) { - lowestbuilding = i; - } } else if (topoClass == WATER) { water = i; @@ -1199,17 +1173,19 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st //-- get allocated the height value of the floor of the building. Any building will do if >1. //-- Also ignore water so it doesn't get snapped to the floor of a building if (building != -1) { - //-- push building heights if inner walls are needed and only once for the lowest building to overcome duplicates in the node column + //-- push building heights in the node column, both floor and roof heights int tmph = -99999; for (auto i : buildings) { - int h = dynamic_cast(std::get<1>(zstar[i]))->get_height_base(); - if (h != tmph) { //-- not to repeat the same height - _nc_inner_walls[key_bucket].push_back(h); - tmph = h; + int hfloor = dynamic_cast(std::get<1>(zstar[i]))->get_height_base(); + if (std::find(_nc_building_walls[key_bucket].begin(), _nc_building_walls[key_bucket].end(), hfloor) == _nc_building_walls[key_bucket].end()) { + _nc_building_walls[key_bucket].push_back(hfloor); + } + + int hroof = std::get<0>(zstar[i]); + if (std::find(_nc_building_walls[key_bucket].begin(), _nc_building_walls[key_bucket].end(), hroof) == _nc_building_walls[key_bucket].end()) { + _nc_building_walls[key_bucket].push_back(hroof); } } - // push lowest building roof height to seperate node column - _nc_inner_walls[key_bucket].push_back(std::get<0>(zstar[lowestbuilding])); int baseheight = dynamic_cast(std::get<1>(zstar[building]))->get_height_base(); for (auto& each : zstar) { if (std::get<1>(each)->get_class() != BUILDING && std::get<1>(each)->get_class() != WATER) { @@ -1319,13 +1295,13 @@ void Map3d::stitch_jumpedge(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f if (f1->get_class() == BUILDING && f2->get_class() == BUILDING) { int f1base = dynamic_cast(f1)->get_height_base(); int f2base = dynamic_cast(f2)->get_height_base(); - _nc_inner_walls[key_bucket].push_back(f1base); + _nc_building_walls[key_bucket].push_back(f1base); if (f1base != f2base) { - _nc_inner_walls[key_bucket].push_back(f2base); + _nc_building_walls[key_bucket].push_back(f2base); } - _nc_inner_walls[key_bucket].push_back(f1z); + _nc_building_walls[key_bucket].push_back(f1z); if (f1z != f2z) { - _nc_inner_walls[key_bucket].push_back(f2z); + _nc_building_walls[key_bucket].push_back(f2z); } } else if (f1->get_class() == BUILDING) { @@ -1334,11 +1310,11 @@ void Map3d::stitch_jumpedge(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f } else { //- keep water flat, add the water height to the nc - _nc_inner_walls[key_bucket].push_back(f2z); + _nc_building_walls[key_bucket].push_back(f2z); } //- expect a building to always be heighest adjacent feature - _nc_inner_walls[key_bucket].push_back(dynamic_cast(f1)->get_height_base()); - _nc_inner_walls[key_bucket].push_back(f1z); + _nc_building_walls[key_bucket].push_back(dynamic_cast(f1)->get_height_base()); + _nc_building_walls[key_bucket].push_back(f1z); } else { //-- f2 is Building if (f1->get_class() != WATER) { @@ -1346,14 +1322,12 @@ void Map3d::stitch_jumpedge(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f } else { //- keep water flat, add the water height to the nc - _nc_inner_walls[key_bucket].push_back(f1z); + _nc_building_walls[key_bucket].push_back(f1z); } //- expect a building to always be heighest adjacent feature - _nc_inner_walls[key_bucket].push_back(dynamic_cast(f2)->get_height_base()); - _nc_inner_walls[key_bucket].push_back(f2z); + _nc_building_walls[key_bucket].push_back(dynamic_cast(f2)->get_height_base()); + _nc_building_walls[key_bucket].push_back(f2z); } - //sort the nc since the water can be inbetween the floor and roof of a building - std::sort(_nc_inner_walls[key_bucket].begin(), _nc_inner_walls[key_bucket].end()); } //-- no Buildings involved else { @@ -1401,110 +1375,6 @@ void Map3d::stitch_jumpedge(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f } } -//void Map3d::stitch_jumpedge(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f2, int ringi2, int pi2) { -// Point2 p = f1->get_point2(ringi1, pi1); -// std::string key_bucket = gen_key_bucket(&p); -// int f1z = f1->get_vertex_elevation(ringi1, pi1); -// int f2z = f2->get_vertex_elevation(ringi2, pi2); -// -// //-- Buildings involved -// if ((f1->get_class() == BUILDING) || (f2->get_class() == BUILDING)) { -// if ((f1->get_class() == BUILDING) && (f2->get_class() == BUILDING)) { -// // add a wall to both buildings -// f1->add_vertical_wall(); -// f2->add_vertical_wall(); -// _nc[key_bucket].push_back(f1z); -// if (f1z != f2z) { -// _nc[key_bucket].push_back(f2z); -// } -// int f1base = dynamic_cast(f1)->get_height_base(); -// int f2base = dynamic_cast(f2)->get_height_base(); -// _nc[key_bucket].push_back(f1base); -// if (f1base != f2base) { -// _nc[key_bucket].push_back(f2base); -// } -// if (_building_inner_walls && _nc_inner_walls[key_bucket].empty()) { -// _nc_inner_walls[key_bucket].push_back(f1base); -// if (f1base != f2base) { -// _nc_inner_walls[key_bucket].push_back(f2base); -// } -// // push lowest building roof height to seperate node column -// _nc_inner_walls[key_bucket].push_back(std::min(f1z, f2z)); -// } -// } -// else if (f1->get_class() == BUILDING) { -// if (f2->get_class() != WATER) { -// f2->set_vertex_elevation(ringi2, pi2, dynamic_cast(f1)->get_height_base()); -// } -// else { -// //- keep water flat, add the water height to the nc -// _nc[key_bucket].push_back(f2z); -// } -// //- expect a building to always be heighest adjacent feature -// f1->add_vertical_wall(); -// _nc[key_bucket].push_back(f1z); -// _nc[key_bucket].push_back(dynamic_cast(f1)->get_height_base()); -// } -// else { //-- f2 is Building -// if (f1->get_class() != WATER) { -// f1->set_vertex_elevation(ringi1, pi1, dynamic_cast(f2)->get_height_base()); -// } -// else { -// //- keep water flat, add the water height to the nc -// _nc[key_bucket].push_back(f1z); -// } -// //- expect a building to always be heighest adjacent feature -// f2->add_vertical_wall(); -// _nc[key_bucket].push_back(f2z); -// _nc[key_bucket].push_back(dynamic_cast(f2)->get_height_base()); -// } -// } -// //-- no Buildings involved -// else { -// bool bStitched = false; -// int deltaz = std::abs(f1z - f2z); -// if (deltaz < this->_threshold_jump_edges) { -// //-- handle same class, average them -// if (f1->get_class() == f2->get_class()) { -// int avgz = (f1->get_vertex_elevation(ringi1, pi1) + f2->get_vertex_elevation(ringi2, pi2)) / 2; -// f1->set_vertex_elevation(ringi1, pi1, avgz); -// f2->set_vertex_elevation(ringi2, pi2, avgz); -// _nc[key_bucket].push_back(avgz); -// bStitched = true; -// } -// if (f1->is_hard() == false) { -// f1->set_vertex_elevation(ringi1, pi1, f2z); -// bStitched = true; -// } -// else if (f2->is_hard() == false) { -// f2->set_vertex_elevation(ringi2, pi2, f1z); -// bStitched = true; -// } -// } -// //-- then vertical walls must be added: nc to highest -// if (bStitched == false) { -// // stitch object withouth height to adjacent object which does have a height -// if (f1z == -9999 && f2z != -9999) { -// f1->set_vertex_elevation(ringi1, pi1, f2z); -// } -// else if (f2z == -9999 && f1z != -9999) { -// f2->set_vertex_elevation(ringi2, pi2, f1z); -// } -// else { -// //- add a wall to the heighest feature -// if (f1z > f2z) { -// f1->add_vertical_wall(); -// } -// else if (f2z > f1z) { -// f2->add_vertical_wall(); -// } -// _nc[key_bucket].push_back(f1z); -// _nc[key_bucket].push_back(f2z); -// } -// } -// } -//} - void Map3d::stitch_average(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f2, int ringi2, int pi2) { int avgz = (f1->get_vertex_elevation(ringi1, pi1) + f2->get_vertex_elevation(ringi2, pi2)) / 2; f1->set_vertex_elevation(ringi1, pi1, avgz); diff --git a/src/Map3d.h b/src/Map3d.h index 7a1b17f8..ede1b918 100644 --- a/src/Map3d.h +++ b/src/Map3d.h @@ -133,7 +133,7 @@ class Map3d { std::array,NUM_ALLOWEDLASTOPO> _las_classes_allowed; NodeColumn _nc; - NodeColumn _nc_inner_walls; + NodeColumn _nc_building_walls; std::vector _lsFeatures; std::vector _allowed_layers; bgi::rtree< PairIndexed, bgi::rstar<16> > _rtree; diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index 94e1dcbb..8a798bc0 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -179,36 +179,35 @@ void TopoFeature::get_obj(std::unordered_map< std::string, unsigned long > &dPts } //-- vertical triangles - if (_bVerticalWalls == true && _triangles_vw.size() > 0) { + if (_triangles_vw.size() > 0) { fs += mtl; fs += "Wall"; fs += "\n"; - } - - for (auto& t : _triangles_vw) { - unsigned long a, b, c; - auto it = dPts.find(_vertices_vw[t.v0].second); - if (it == dPts.end()) { - a = dPts.size() + 1; - dPts[_vertices_vw[t.v0].second] = a; - } - else - a = it->second; - it = dPts.find(_vertices_vw[t.v1].second); - if (it == dPts.end()) { - b = dPts.size() + 1; - dPts[_vertices_vw[t.v1].second] = b; - } - else - b = it->second; - it = dPts.find(_vertices_vw[t.v2].second); - if (it == dPts.end()) { - c = dPts.size() + 1; - dPts[_vertices_vw[t.v2].second] = c; - } - else - c = it->second; + for (auto& t : _triangles_vw) { + unsigned long a, b, c; + auto it = dPts.find(_vertices_vw[t.v0].second); + if (it == dPts.end()) { + a = dPts.size() + 1; + dPts[_vertices_vw[t.v0].second] = a; + } + else + a = it->second; + it = dPts.find(_vertices_vw[t.v1].second); + if (it == dPts.end()) { + b = dPts.size() + 1; + dPts[_vertices_vw[t.v1].second] = b; + } + else + b = it->second; + it = dPts.find(_vertices_vw[t.v2].second); + if (it == dPts.end()) { + c = dPts.size() + 1; + dPts[_vertices_vw[t.v2].second] = c; + } + else + c = it->second; - if ((a != b) && (a != c) && (b != c)) { - fs += "f "; fs += std::to_string(a); fs += " "; fs += std::to_string(b); fs += " "; fs += std::to_string(c); fs += "\n"; + if ((a != b) && (a != c) && (b != c)) { + fs += "f "; fs += std::to_string(a); fs += " "; fs += std::to_string(b); fs += " "; fs += std::to_string(c); fs += "\n"; + } } } } @@ -456,10 +455,7 @@ void TopoFeature::fix_bowtie() { } } -void TopoFeature::construct_vertical_walls(const NodeColumn& nc, int baseheight) { - if (this->has_vertical_walls() == false) - return; - +void TopoFeature::construct_vertical_walls(const NodeColumn& nc) { //-- gather all rings std::vector therings; therings.push_back(_p2->outer()); @@ -500,21 +496,14 @@ void TopoFeature::construct_vertical_walls(const NodeColumn& nc, int baseheight) break; } } - if (fadj == nullptr && this->get_class() != BUILDING) { + if (fadj == nullptr) { continue; } int az = this->get_vertex_elevation(ringi, ai); int bz = this->get_vertex_elevation(ringi, bi); - int fadj_az, fadj_bz; - if(fadj == nullptr) { - fadj_az = baseheight; - fadj_bz = baseheight; - } - else { - fadj_az = fadj->get_vertex_elevation(adj_a_ringi, adj_a_pi); - fadj_bz = fadj->get_vertex_elevation(adj_b_ringi, adj_b_pi); - } + int fadj_az = fadj->get_vertex_elevation(adj_a_ringi, adj_a_pi); + int fadj_bz = fadj->get_vertex_elevation(adj_b_ringi, adj_b_pi); //-- check if there's a nc for either ncit = nc.find(gen_key_bucket(&a)); @@ -651,399 +640,6 @@ void TopoFeature::construct_vertical_walls(const NodeColumn& nc, int baseheight) } } -void TopoFeature::construct_inner_walls(const NodeColumn& nc, int baseheight) { - //-- gather all rings - std::vector therings; - therings.push_back(_p2->outer()); - for (Ring2& iring : _p2->inners()) - therings.push_back(iring); - - //-- process each vertex of the polygon separately - Point2 a, b; - TopoFeature* fadj; - int ringi = -1; - for (Ring2& ring : therings) { - ringi++; - for (int ai = 0; ai < ring.size(); ai++) { - //-- Point a - a = ring[ai]; - //-- find Point b - if (ai == (ring.size() - 1)) { - b = ring.front(); - } - else { - b = ring[ai + 1]; - } - - //-- find the adjacent polygon to segment ab (fadj) - fadj = nullptr; - int adj_a_ringi = 0; - int adj_a_pi = 0; - int adj_b_ringi = 0; - int adj_b_pi = 0; - for (auto& adj : *(_adjFeatures)) { - if (adj->has_segment(b, a, adj_b_ringi, adj_b_pi, adj_a_ringi, adj_a_pi)) { - fadj = adj; - break; - } - } - if (fadj == nullptr) { - continue; - } - - std::unordered_map>::const_iterator ncit; - std::vector anc, bnc; - //-- check if there's a nc for either - ncit = nc.find(gen_key_bucket(&a)); - if (ncit != nc.end()) { - anc = ncit->second; - } - ncit = nc.find(gen_key_bucket(&b)); - if (ncit != nc.end()) { - bnc = ncit->second; - } - - if (anc.empty() && bnc.empty()) - continue; - // if one of the nc is empty something is wrong - if (anc.empty() || bnc.empty()) { - std::cerr << "ERROR: the inner wall node column is empty for building: " << _id << std::endl; - break; - } - - if (_id == "b31bc9c3c-00ba-11e6-b420-2bdcc4ab5d7f") { - std::cout << "stop"; - } - - std::vector awall, bwall, enda, endb; - std::vector; - - //if (building_floor || fadj->get_class() != BUILDING) { - if (true || fadj->get_class() != BUILDING) { - awall.push_back(anc.front()); - bwall.push_back(bnc.front()); - } - //if (inner_walls) { - if (true) { - if (anc.front() != baseheight) { - awall.push_back(baseheight); - } - if (bnc.front() != baseheight) { - bwall.push_back(baseheight); - } - } - if (fadj->get_class() == BUILDING) { - int fadjroofheight = fadj->get_vertex_elevation(ringi, ai); - if (anc.back() != fadjroofheight) { - awall.push_back(fadjroofheight); - } - if (bnc.back() != fadjroofheight) { - bwall.push_back(fadjroofheight); - } - } - ////////TODO: fix array, maybe add all and make unique?? - - - awall.push_back(anc.back()); - bwall.push_back(bnc.back()); - - int fadj_az = baseheight; - int fadj_bz = baseheight; - - //-- find the height of the vertex in the node column - std::vector::const_iterator sait, eait, sbit, ebit; - for (int i = 0; i < awall.size() - 1; i++) { - sait = std::find(anc.begin(), anc.end(), awall[i]); - sbit = std::find(bnc.begin(), bnc.end(), bwall[i]); - eait = std::find(anc.begin(), anc.end(), awall[i + 1]); - ebit = std::find(bnc.begin(), bnc.end(), bwall[i + 1]); - - // sait = std::find(anc.begin(), anc.end(), fadj_az); - // sbit = std::find(bnc.begin(), bnc.end(), fadj_bz); - // eait = anc.end(); - // ebit = bnc.end(); - // - ////if building_floors - // // for walls from floor upto building wall stitch to adjacent object - // if (fadj->get_class() != BUILDING) { - // //eait=eait-2; - // //ebit=ebit-2; - // eait = std::find(anc.begin(), anc.end(), baseheight); - // ebit = std::find(bnc.begin(), bnc.end(), baseheight); - // } - ////if building_floors - - - //-- iterate to triangulate - while (sbit != ebit && sbit != bnc.end() && (sbit + 1) != bnc.end() && - sait != eait && sait != anc.end() && (sait + 1) != anc.end()) { - Point3 p; - Triangle t; - int size; - if (sbit + 1 != bnc.end()) { - p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - sbit++; - p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - size = int(_vertices_vw.size()); - t.v0 = size - 2; - t.v1 = size - 3; - t.v2 = size - 1; - _triangles_vw.push_back(t); - } - if (sait + 1 != anc.end()) { - p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - sait++; - p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); - _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - size = int(_vertices_vw.size()); - t.v0 = size - 3; - t.v1 = size - 2; - t.v2 = size - 1; - _triangles_vw.push_back(t); - } - } - } - } - } -} - -//void TopoFeature::construct_inner_walls(const NodeColumn& nc, const NodeColumn& ncbuilding, int baseheight) { -// //-- gather all rings -// std::vector therings; -// therings.push_back(_p2->outer()); -// for (Ring2& iring : _p2->inners()) -// therings.push_back(iring); -// -// //-- process each vertex of the polygon separately -// std::vector anc, bnc; -// std::unordered_map>::const_iterator ncit; -// Point2 a, b; -// TopoFeature* fadj; -// int ringi = -1; -// for (Ring2& ring : therings) { -// ringi++; -// for (int ai = 0; ai < ring.size(); ai++) { -// //-- Point a -// a = ring[ai]; -// //-- find Point b -// int bi; -// if (ai == (ring.size() - 1)) { -// b = ring.front(); -// bi = 0; -// } -// else { -// b = ring[ai + 1]; -// bi = ai + 1; -// } -// //-- check if there's a nc for either -// ncit = nc.find(gen_key_bucket(&a)); -// if (ncit != nc.end()) { -// anc = ncit->second; -// // make values in anc unique -// //anc.erase(unique(anc.begin(), anc.end()), anc.end()); -// } -// ncit = nc.find(gen_key_bucket(&b)); -// if (ncit != nc.end()) { -// bnc = ncit->second; -// // make values in bnc unique -// // bnc.erase(unique(bnc.begin(), bnc.end()), bnc.end()); -// } -// -// if ((anc.empty() == true) && (bnc.empty() == true)) -// continue; -// -// //-- find the adjacent polygon to segment ab (fadj) -// fadj = nullptr; -// int adj_a_ringi = 0; -// int adj_a_pi = 0; -// int adj_b_ringi = 0; -// int adj_b_pi = 0; -// for (auto& adj : *(_adjFeatures)) { -// if (adj->has_segment(b, a, adj_b_ringi, adj_b_pi, adj_a_ringi, adj_a_pi) == true) { -// fadj = adj; -// break; -// } -// } -// if (fadj == nullptr && this->get_class() != BUILDING) { -// continue; -// } -// -// int az = this->get_vertex_elevation(ringi, ai); -// int bz = this->get_vertex_elevation(ringi, bi); -// int fadj_az, fadj_bz; -// if (fadj == nullptr) { -// fadj_az = baseheight; -// fadj_bz = baseheight; -// } -// else { -// fadj_az = fadj->get_vertex_elevation(adj_a_ringi, adj_a_pi); -// fadj_bz = fadj->get_vertex_elevation(adj_b_ringi, adj_b_pi); -// } -// -// //-- add extra vw for the inner walls of buildings -// //-- create walls from lowest roof to ground, height jumps are created later -// int tmp_az = az; -// int tmp_bz = bz; -// int tmp_fadj_az = baseheight; -// int tmp_fadj_bz = baseheight; -// bool adifferent = false; -// bool bdifferent = false; -// -// if (_id == "b31bd5f7b-00ba-11e6-b420-2bdcc4ab5d7f" && ringi == 0 && ai == 1) { -// std::cout << 'stop'; -// } -// -// //-- solve for the case two buildings are adjacent but their baseheights are different -// // in case they are different start at the lowest height (which is always the baseheight of the adjacent building) -// if (std::find(anc.begin(), anc.end(), tmp_fadj_az) == anc.end()) { -// tmp_fadj_az = anc.front(); -// adifferent = true; -// } -// -// if (std::find(bnc.begin(), bnc.end(), tmp_fadj_bz) == bnc.end()) { -// tmp_fadj_bz = bnc.front(); -// bdifferent = true; -// } -// // Add extra triangle in case we have multiple buildings connected with different base heights -// if (adifferent && bdifferent) { -// if (tmp_fadj_bz > tmp_fadj_az) { -// Point3 p; -// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_fadj_az)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(tmp_fadj_az)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(tmp_fadj_bz)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// Triangle t; -// int size = int(_vertices_vw.size()); -// t.v0 = size - 2; -// t.v1 = size - 3; -// t.v2 = size - 1; -// _triangles_vw.push_back(t); -// -// //set tmp_fadj_bz to new height -// tmp_fadj_bz = tmp_fadj_az; -// } -// if (tmp_fadj_az > tmp_fadj_bz) { -// Point3 p; -// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_fadj_bz)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(tmp_fadj_bz)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_fadj_az)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// Triangle t; -// int size = int(_vertices_vw.size()); -// t.v0 = size - 2; -// t.v1 = size - 3; -// t.v2 = size - 1; -// _triangles_vw.push_back(t); -// //set tmp_fadj_az to new height -// tmp_fadj_az = tmp_fadj_bz; -// } -// } -// if (adifferent) { -// Point3 p; -// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(baseheight)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(baseheight)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_fadj_az)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// Triangle t; -// int size = int(_vertices_vw.size()); -// t.v0 = size - 2; -// t.v1 = size - 3; -// t.v2 = size - 1; -// _triangles_vw.push_back(t); -// } -// if (bdifferent) { -// Point3 p; -// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_fadj_az)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(baseheight)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(tmp_fadj_bz)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// Triangle t; -// int size = int(_vertices_vw.size()); -// t.v0 = size - 2; -// t.v1 = size - 3; -// t.v2 = size - 1; -// _triangles_vw.push_back(t); -// } -// -// if (fadj->get_class() == BUILDING) { -// if (az > fadj_az && bz > fadj_bz) { -// tmp_az = fadj_az; -// tmp_bz = fadj_bz; -// } -// if ((tmp_az >= tmp_fadj_az || tmp_bz >= tmp_fadj_bz) && (tmp_az != tmp_fadj_az && tmp_bz != tmp_fadj_bz)) { -// //-- find the height of the vertex in the node column -// std::vector::const_iterator sait, eait, sbit, ebit; -// sait = std::find(anc.begin(), anc.end(), tmp_fadj_az); -// eait = std::find(anc.begin(), anc.end(), tmp_az); -// sbit = std::find(bnc.begin(), bnc.end(), tmp_fadj_bz); -// ebit = std::find(bnc.begin(), bnc.end(), tmp_bz); -// -// //-- iterate to triangulate -// while ((sbit != ebit) && (sbit != bnc.end()) && ((sbit + 1) != bnc.end())) { -// Point3 p; -// if (anc.size() == 0 || sait == anc.end()) { -// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(tmp_az)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// } -// else { -// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// } -// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// sbit++; -// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// Triangle t; -// int size = int(_vertices_vw.size()); -// t.v0 = size - 2; -// t.v1 = size - 3; -// t.v2 = size - 1; -// _triangles_vw.push_back(t); -// } -// while (sait != eait && sait != anc.end() && (sait + 1) != anc.end()) { -// Point3 p; -// if (bnc.size() == 0 || ebit == bnc.end()) { -// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(tmp_bz)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// } -// else { -// p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*ebit)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// } -// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// sait++; -// p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); -// _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); -// Triangle t; -// int size = int(_vertices_vw.size()); -// t.v0 = size - 3; -// t.v1 = size - 2; -// t.v2 = size - 1; -// _triangles_vw.push_back(t); -// } -// } -// } -// } -// } -//} - bool TopoFeature::has_segment(Point2& a, Point2& b, int& aringi, int& api, int& bringi, int& bpi) { double threshold = 0.001; double sqr_threshold = threshold * threshold; diff --git a/src/TopoFeature.h b/src/TopoFeature.h index bef5526b..8491268d 100644 --- a/src/TopoFeature.h +++ b/src/TopoFeature.h @@ -52,8 +52,7 @@ class TopoFeature { virtual bool get_shape(OGRLayer*, bool writeAttributes, AttributeMap extraAttributes = AttributeMap()) = 0; std::string get_id(); - void construct_vertical_walls(const NodeColumn& nc, int baseheight); - void construct_inner_walls(const NodeColumn& nc, int baseheight); + void construct_vertical_walls(const NodeColumn& nc); void fix_bowtie(); void add_adjacent_feature(TopoFeature* adjFeature); std::vector* get_adjacent_features(); From db440b56bb8b692282a59b1bff5f6a539a82c271 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Mon, 28 May 2018 17:48:24 +0200 Subject: [PATCH 10/45] code cleanup --- src/Building.cpp | 87 ------------------------------------------------ 1 file changed, 87 deletions(-) diff --git a/src/Building.cpp b/src/Building.cpp index c94e0708..47617440 100644 --- a/src/Building.cpp +++ b/src/Building.cpp @@ -229,57 +229,6 @@ void Building::construct_building_walls(const NodeColumn& nc, int baseheight, bo bwallend = awallend; } - - // if (building_floor) { - // // anc.front() -> baseheight - // // bnc.front() -> baseheight - // if (anc.front() != baseheight) { - // awall.push_back(anc.front()); - // awallend.push_back(baseheight); - // } - // if (bnc.front() != baseheight) { - // bwall.push_back(bnc.front()); - // bwallend.push_back(baseheight); - // } - // } - // bool fadjlower = false; - // int fadjz = fadj->get_vertex_elevation(adj_a_ringi, adj_a_pi); - // if (roofheight > fadjz) { - // fadjlower = true; - // } - - // // fadj == BUILDING is known here - // if (building_inner_walls) { - // //baseheight -> roofheight - // //baseheight -> roofheight - // //or if fadj < roofheight - // //baseheight -> fadjheight - // //baseheight -> fadjheight - // //fadjheight -> roofheight - // //fadjheight -> roofheight - - // awall.push_back(baseheight); - // if (fadjlower) { - // awallend.push_back(fadjz); - // awall.push_back(fadjz); - // } - - // bwall.push_back(baseheight); - // if (fadjlower) { - // bwallend.push_back(fadjz); - // bwall.push_back(fadjz); - // } - // } - // if (fadjlower) { - // //fadjheight -> roofheight - // //fadjheight -> roofheight - // awall.push_back(fadjz); - // bwall.push_back(fadjz); - // } - // awallend.push_back(roofheight); - // bwallend.push_back(roofheight); - //} - for (int i = 0; i < awall.size(); i++) { sait = std::find(anc.begin(), anc.end(), awall[i]); sbit = std::find(bnc.begin(), bnc.end(), bwall[i]); @@ -319,42 +268,6 @@ void Building::construct_building_walls(const NodeColumn& nc, int baseheight, bo t.v2 = size - 1; _triangles_vw.push_back(t); } - - ////-- iterate to triangulate - //while (sbit != ebit && sbit != bnc.end() && (sbit + 1) != bnc.end() && - // sait != eait && sait != anc.end() && (sait + 1) != anc.end()) { - // Point3 p; - // Triangle t; - // int size; - // if (sbit + 1 != bnc.end()) { - // p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); - // _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - // p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); - // _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - // sbit++; - // p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); - // _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - // size = int(_vertices_vw.size()); - // t.v0 = size - 2; - // t.v1 = size - 3; - // t.v2 = size - 1; - // _triangles_vw.push_back(t); - // } - // if (sait + 1 != anc.end()) { - // p = Point3(bg::get<0>(b), bg::get<1>(b), z_to_float(*sbit)); - // _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - // p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); - // _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - // sait++; - // p = Point3(bg::get<0>(a), bg::get<1>(a), z_to_float(*sait)); - // _vertices_vw.push_back(std::make_pair(p, gen_key_bucket(&p))); - // size = int(_vertices_vw.size()); - // t.v0 = size - 3; - // t.v1 = size - 2; - // t.v2 = size - 1; - // _triangles_vw.push_back(t); - // } - //} } } } From 940c037a046e0716ac949f873c296c7c002ba1ab Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Tue, 29 May 2018 14:49:48 +0200 Subject: [PATCH 11/45] add config option for inner walls --- src/Building.cpp | 12 +++++++----- src/Building.h | 5 +++-- src/Map3d.cpp | 11 +++++++---- src/Map3d.h | 1 + src/main.cpp | 13 +++++++++++++ 5 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/Building.cpp b/src/Building.cpp index 47617440..3dd06cc6 100644 --- a/src/Building.cpp +++ b/src/Building.cpp @@ -35,15 +35,17 @@ float Building::_heightref_top; float Building::_heightref_base; bool Building::_building_triangulate; bool Building::_building_include_floor; +bool Building::_building_inner_walls; std::set Building::_las_classes_roof; std::set Building::_las_classes_ground; -Building::Building(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref_top, float heightref_base, bool building_triangulate, bool building_include_floor) +Building::Building(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref_top, float heightref_base, bool building_triangulate, bool building_include_floor, bool building_inner_walls) : Flat(wkt, layername, attributes, pid) { _heightref_top = heightref_top; _heightref_base = heightref_base; _building_triangulate = building_triangulate; _building_include_floor = building_include_floor; + _building_inner_walls = building_inner_walls; } void Building::set_las_classes_roof(std::set theset) @@ -118,7 +120,7 @@ bool Building::add_elevation_point(Point2 &p, double z, float radius, int lascla return true; } -void Building::construct_building_walls(const NodeColumn& nc, int baseheight, bool building_inner_walls, bool building_floor) { +void Building::construct_building_walls(const NodeColumn& nc) { //-- gather all rings std::vector therings; therings.push_back(_p2->outer()); @@ -188,7 +190,7 @@ void Building::construct_building_walls(const NodeColumn& nc, int baseheight, bo int baseheight = this->get_height_base(); if (fadj->get_class() != BUILDING) { // start at adjacent height for correct stitching if no floor - if (building_floor) { + if (_building_include_floor) { awall.push_back(baseheight); bwall.push_back(baseheight); } @@ -204,14 +206,14 @@ void Building::construct_building_walls(const NodeColumn& nc, int baseheight, bo int adjbaseheight = dynamic_cast(fadj)->get_height_base(); int adjroofheight = fadj->get_vertex_elevation(adj_a_ringi, adj_a_pi); int base = baseheight; - if (building_floor && baseheight < adjbaseheight) { + if (_building_include_floor && baseheight < adjbaseheight) { awall.push_back(baseheight); awallend.push_back(adjbaseheight); //store base for inner walls check base = adjbaseheight; } - if (building_inner_walls) { + if (_building_inner_walls) { awall.push_back(base); if (roofheight > adjroofheight) { awallend.push_back(adjroofheight); diff --git a/src/Building.h b/src/Building.h index 26250125..cbbe3f03 100644 --- a/src/Building.h +++ b/src/Building.h @@ -33,10 +33,10 @@ class Building: public Flat { public: - Building(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref_top, float heightref_base, bool building_triangulate, bool building_include_floor); + Building(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref_top, float heightref_base, bool building_triangulate, bool building_include_floor, bool building_inner_walls); bool lift(); bool add_elevation_point(Point2 &p, double z, float radius, int lasclass); - void construct_building_walls(const NodeColumn& nc, int baseheight, bool building_inner_walls, bool building_floor); + void construct_building_walls(const NodeColumn& nc); void get_obj(std::unordered_map< std::string, unsigned long > &dPts, int lod, std::string mtl, std::string &fs); void get_citygml(std::wostream& of); void get_citygml_imgeo(std::wostream& of); @@ -61,6 +61,7 @@ class Building: public Flat { static float _heightref_base; static bool _building_triangulate; static bool _building_include_floor; + static bool _building_inner_walls; static std::set _las_classes_roof; static std::set _las_classes_ground; }; diff --git a/src/Map3d.cpp b/src/Map3d.cpp index 04b961f0..3377d960 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -41,7 +41,7 @@ Map3d::Map3d() { _building_triangulate = true; _building_lod = 1; _building_include_floor = false; - _building_inner_walls = true; + _building_inner_walls = false; _terrain_simplification = 0; _forest_simplification = 0; _terrain_simplification_tinsimp = 0.0; @@ -94,6 +94,10 @@ void Map3d::set_building_triangulate(bool triangulate) { _building_triangulate = triangulate; } +void Map3d::set_building_inner_walls(bool inner_walls) { + _building_inner_walls = inner_walls; +} + void Map3d::set_building_lod(int lod) { _building_lod = lod; } @@ -716,8 +720,7 @@ bool Map3d::threeDfy(bool stitching) { for (auto& f : _lsFeatures) { if (f->get_class() == BUILDING) { Building* b = dynamic_cast(f); - int baseheight = b->get_height_base(); - b->construct_building_walls(_nc_building_walls, baseheight, _building_inner_walls, _building_include_floor); + b->construct_building_walls(_nc_building_walls); } else if (f->has_vertical_walls()) { f->construct_vertical_walls(_nc); @@ -925,7 +928,7 @@ void Map3d::extract_feature(OGRFeature *f, std::string layername, const char *id attributes[boost::locale::to_lower(f->GetFieldDefnRef(i)->GetNameRef())] = std::make_pair(f->GetFieldDefnRef(i)->GetType(), f->GetFieldAsString(i)); } if (layertype == "Building") { - Building* p3 = new Building(wkt, layername, attributes, id, _building_heightref_roof, _building_heightref_ground, _building_triangulate, _building_include_floor); + Building* p3 = new Building(wkt, layername, attributes, id, _building_heightref_roof, _building_heightref_ground, _building_triangulate, _building_include_floor, _building_inner_walls); _lsFeatures.push_back(p3); } else if (layertype == "Terrain") { diff --git a/src/Map3d.h b/src/Map3d.h index ede1b918..40e9b065 100644 --- a/src/Map3d.h +++ b/src/Map3d.h @@ -84,6 +84,7 @@ class Map3d { void set_building_heightref_ground(float heightref); void set_building_include_floor(bool include); void set_building_triangulate(bool triangulate); + void set_building_inner_walls(bool inner_walls); void set_building_lod(int lod); void set_terrain_simplification(int simplification); void set_forest_simplification(int simplification); diff --git a/src/main.cpp b/src/main.cpp index 777a9e8a..3722d6fe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -217,6 +217,12 @@ int main(int argc, const char * argv[]) { else map3d.set_building_include_floor(false); } + if (n["Building"]["inner_walls"]) { + if (n["Building"]["inner_walls"].as() == "true") + map3d.set_building_inner_walls(true); + else + map3d.set_building_inner_walls(false); + } } if (n["Terrain"]) { if (n["Terrain"]["simplification"]) @@ -754,6 +760,13 @@ bool validate_yaml(const char* arg, std::set& allowedFeatures) { std::cerr << "\tOption 'Building.floor' invalid; must be 'true' or 'false'.\n"; } } + if (n["Building"]["inner_walls"]) { + std::string s = n["Building"]["inner_walls"].as(); + if ((s != "true") && (s != "false")) { + wentgood = false; + std::cerr << "\tOption 'Building.inner_walls' invalid; must be 'true' or 'false'.\n"; + } + } } if (n["Terrain"]) { if (n["Terrain"]["simplification"]) { From 45723edc66c522d1bed59610aba56c4e6d9ce34f Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Wed, 30 May 2018 14:12:33 +0200 Subject: [PATCH 12/45] Change gml:pos to gml:posList and make citygml schema names same as imgeo --- src/Bridge.cpp | 8 ++++---- src/Building.cpp | 20 ++++++++++---------- src/Road.cpp | 8 ++++---- src/Terrain.cpp | 8 ++++---- src/TopoFeature.cpp | 17 +++++++++-------- src/io.cpp | 32 +++++++++++++++++++------------- 6 files changed, 50 insertions(+), 43 deletions(-) diff --git a/src/Bridge.cpp b/src/Bridge.cpp index 5b8e5c9a..39da2a0c 100644 --- a/src/Bridge.cpp +++ b/src/Bridge.cpp @@ -74,17 +74,17 @@ void Bridge::get_cityjson(nlohmann::json& j, std::unordered_map"; - of << "get_id() << "\">"; + of << "get_id() << "\">"; get_citygml_attributes(of, _attributes); - of << ""; + of << ""; of << ""; for (auto& t : _triangles) get_triangle_as_gml_surfacemember(of, t); for (auto& t : _triangles_vw) get_triangle_as_gml_surfacemember(of, t, true); of << ""; - of << ""; - of << ""; + of << ""; + of << ""; of << ""; } diff --git a/src/Building.cpp b/src/Building.cpp index 3dd06cc6..5d96343d 100644 --- a/src/Building.cpp +++ b/src/Building.cpp @@ -390,26 +390,26 @@ void Building::get_citygml(std::wostream& of) { float h = z_to_float(this->get_height()); float hbase = z_to_float(this->get_height_base()); of << ""; - of << "get_id() << "\">"; + of << "get_id() << "\">"; get_citygml_attributes(of, _attributes); of << ""; of << "" << hbase << ""; of << ""; - of << "" << h << ""; + of << "" << h << ""; //-- LOD0 footprint - of << ""; + of << ""; of << ""; get_polygon_lifted_gml(of, this->_p2, hbase, true); - of << ""; - of << ""; + of << ""; + of << ""; //-- LOD0 roofedge - of << ""; + of << ""; of << ""; get_polygon_lifted_gml(of, this->_p2, h, true); of << ""; - of << ""; + of << ""; //-- LOD1 Solid - of << ""; + of << ""; of << ""; of << ""; of << ""; @@ -430,8 +430,8 @@ void Building::get_citygml(std::wostream& of) { of << ""; of << ""; of << ""; - of << ""; - of << ""; + of << ""; + of << ""; of << ""; } diff --git a/src/Road.cpp b/src/Road.cpp index f02cfeec..2d69bca2 100644 --- a/src/Road.cpp +++ b/src/Road.cpp @@ -75,17 +75,17 @@ void Road::get_cityjson(nlohmann::json& j, std::unordered_map"; - of << "get_id() << "\">"; + of << "get_id() << "\">"; get_citygml_attributes(of, _attributes); - of << ""; + of << ""; of << ""; for (auto& t : _triangles) get_triangle_as_gml_surfacemember(of, t); for (auto& t : _triangles_vw) get_triangle_as_gml_surfacemember(of, t, true); of << ""; - of << ""; - of << ""; + of << ""; + of << ""; of << ""; } diff --git a/src/Terrain.cpp b/src/Terrain.cpp index c26aa2b9..2ff9286b 100644 --- a/src/Terrain.cpp +++ b/src/Terrain.cpp @@ -68,17 +68,17 @@ void Terrain::get_cityjson(nlohmann::json& j, std::unordered_map"; - of << "get_id() << "\">"; + of << "get_id() << "\">"; get_citygml_attributes(of, _attributes); - of << ""; + of << ""; of << ""; for (auto& t : _triangles) get_triangle_as_gml_surfacemember(of, t); for (auto& t : _triangles_vw) get_triangle_as_gml_surfacemember(of, t, true); of << ""; - of << ""; - of << ""; + of << ""; + of << ""; of << ""; } diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index 8a798bc0..d1934561 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -928,16 +928,17 @@ void TopoFeature::get_triangle_as_gml_triangle(std::wostream& of, Triangle& t, b of << ""; of << ""; if (verticalwall == false) { - of << "" << _vertices[t.v0].second << ""; - of << "" << _vertices[t.v1].second << ""; - of << "" << _vertices[t.v2].second << ""; - of << "" << _vertices[t.v0].second << ""; + of << "" + << _vertices[t.v0].second << " " + << _vertices[t.v1].second << " " + << _vertices[t.v2].second << " " + << _vertices[t.v0].second << ""; } else { - of << "" << _vertices_vw[t.v0].second << ""; - of << "" << _vertices_vw[t.v1].second << ""; - of << "" << _vertices_vw[t.v2].second << ""; - of << "" << _vertices_vw[t.v0].second << ""; + of << _vertices_vw[t.v0].second << " " + << _vertices_vw[t.v1].second << " " + << _vertices_vw[t.v2].second << " " + << _vertices_vw[t.v0].second << ""; } of << ""; of << ""; diff --git a/src/io.cpp b/src/io.cpp index dbfa9281..2a923a35 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -56,14 +56,14 @@ void get_citygml_namespaces(std::wostream& of) { of << "xmlns:xAL=\"urn:oasis:names:tc:ciq:xsdschema:xAL:2.0\"\n"; of << "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"; of << "xmlns:gml=\"http://www.opengis.net/gml\"\n"; - of << "xmlns:bldg=\"http://www.opengis.net/citygml/building/2.0\"\n"; + of << "xmlns:bui=\"http://www.opengis.net/citygml/building/2.0\"\n"; of << "xmlns:wtr=\"http://www.opengis.net/citygml/waterbody/2.0\"\n"; of << "xmlns:veg=\"http://www.opengis.net/citygml/vegetation/2.0\"\n"; of << "xmlns:dem=\"http://www.opengis.net/citygml/relief/2.0\"\n"; - of << "xmlns:tran=\"http://www.opengis.net/citygml/transportation/2.0\"\n"; - of << "xmlns:luse=\"http://www.opengis.net/citygml/landuse/2.0\"\n"; + of << "xmlns:tra=\"http://www.opengis.net/citygml/transportation/2.0\"\n"; + of << "xmlns:lu=\"http://www.opengis.net/citygml/landuse/2.0\"\n"; of << "xmlns:gen=\"http://www.opengis.net/citygml/generics/2.0\"\n"; - of << "xmlns:brg=\"http://www.opengis.net/citygml/bridge/2.0\"\n"; + of << "xmlns:bri=\"http://www.opengis.net/citygml/bridge/2.0\"\n"; of << "xmlns:app=\"http://www.opengis.net/citygml/appearance/2.0\"\n"; of << "xmlns:tun=\"http://www.opengis.net/citygml/tunnel/2.0\"\n"; of << "xmlns:cif=\"http://www.opengis.net/citygml/cityfurniture/2.0\"\n"; @@ -100,9 +100,11 @@ void get_polygon_lifted_gml(std::wostream& of, Polygon2* p2, double height, bool auto r = p2->outer(); of << ""; of << ""; + of << ""; for (int i = 0; i < r.size(); i++) - of << "" << bg::get<0>(r[i]) << " " << bg::get<1>(r[i]) << " " << height << ""; - of << "" << bg::get<0>(r[0]) << " " << bg::get<1>(r[0]) << " " << height << ""; + of << bg::get<0>(r[i]) << " " << bg::get<1>(r[i]) << " " << height << " "; + of << bg::get<0>(r[0]) << " " << bg::get<1>(r[0]) << " " << height; + of << ""; of << ""; of << ""; //-- irings @@ -110,9 +112,11 @@ void get_polygon_lifted_gml(std::wostream& of, Polygon2* p2, double height, bool for (Ring2& r : irings) { of << ""; of << ""; + of << ""; for (int i = 0; i < r.size(); i++) - of << "" << bg::get<0>(r[i]) << " " << bg::get<1>(r[i]) << " " << height << ""; - of << "" << bg::get<0>(r[0]) << " " << bg::get<1>(r[0]) << " " << height << ""; + of << bg::get<0>(r[i]) << " " << bg::get<1>(r[i]) << " " << height << " "; + of << bg::get<0>(r[0]) << " " << bg::get<1>(r[0]) << " " << height; + of << ""; of << ""; of << ""; } @@ -127,11 +131,13 @@ void get_extruded_line_gml(std::wostream& of, Point2* a, Point2* b, double high, of << ""; of << ""; of << ""; - of << "" << bg::get<0>(b) << " " << bg::get<1>(b) << " " << low << ""; - of << "" << bg::get<0>(a) << " " << bg::get<1>(a) << " " << low << ""; - of << "" << bg::get<0>(a) << " " << bg::get<1>(a) << " " << high << ""; - of << "" << bg::get<0>(b) << " " << bg::get<1>(b) << " " << high << ""; - of << "" << bg::get<0>(b) << " " << bg::get<1>(b) << " " << low << ""; + of << ""; + of << bg::get<0>(b) << " " << bg::get<1>(b) << " " << low << " "; + of << bg::get<0>(a) << " " << bg::get<1>(a) << " " << low << " "; + of << bg::get<0>(a) << " " << bg::get<1>(a) << " " << high << " "; + of << bg::get<0>(b) << " " << bg::get<1>(b) << " " << high << " "; + of << bg::get<0>(b) << " " << bg::get<1>(b) << " " << low; + of << ""; of << ""; of << ""; of << ""; From f7851734c49b35ec76fa956a5ccd74d9baefac50 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Wed, 30 May 2018 14:24:18 +0200 Subject: [PATCH 13/45] fix citygml namespace typo --- src/Building.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Building.cpp b/src/Building.cpp index 5d96343d..18797f35 100644 --- a/src/Building.cpp +++ b/src/Building.cpp @@ -400,7 +400,7 @@ void Building::get_citygml(std::wostream& of) { of << ""; of << ""; get_polygon_lifted_gml(of, this->_p2, hbase, true); - of << ""; + of << ""; of << ""; //-- LOD0 roofedge of << ""; From 6169414074761541f73a574112881397feaadd50 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Wed, 30 May 2018 15:48:04 +0200 Subject: [PATCH 14/45] add building wall if no adjacent object --- src/Building.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Building.cpp b/src/Building.cpp index 18797f35..9507a9f2 100644 --- a/src/Building.cpp +++ b/src/Building.cpp @@ -159,9 +159,6 @@ void Building::construct_building_walls(const NodeColumn& nc) { break; } } - if (fadj == nullptr) { - continue; - } std::unordered_map>::const_iterator ncit; std::vector anc, bnc; @@ -188,9 +185,9 @@ void Building::construct_building_walls(const NodeColumn& nc) { std::vector::const_iterator sait, eait, sbit, ebit; int roofheight = this->get_vertex_elevation(ringi, ai); int baseheight = this->get_height_base(); - if (fadj->get_class() != BUILDING) { + if (fadj == nullptr || fadj->get_class() != BUILDING) { // start at adjacent height for correct stitching if no floor - if (_building_include_floor) { + if (fadj == nullptr ||_building_include_floor) { awall.push_back(baseheight); bwall.push_back(baseheight); } From b1f646672a28603525d08450e7e8044ab7688a2a Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Thu, 31 May 2018 12:20:44 +0200 Subject: [PATCH 15/45] fix parsing extent --- src/main.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 3722d6fe..ba96dba7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -305,7 +305,6 @@ int main(int argc, const char * argv[]) { double xmin, xmax, ymin, ymax; bool wentgood = true; try { - (n["radius_vertex_elevation"].as()); xmin = boost::lexical_cast(extent_split[0]); ymin = boost::lexical_cast(extent_split[1]); xmax = boost::lexical_cast(extent_split[2]); @@ -982,7 +981,6 @@ bool validate_yaml(const char* arg, std::set& allowedFeatures) { double xmin, xmax, ymin, ymax; bool correct = true; try { - (n["radius_vertex_elevation"].as()); xmin = boost::lexical_cast(extent_split[0]); ymin = boost::lexical_cast(extent_split[1]); xmax = boost::lexical_cast(extent_split[2]); From 93572a6bd0bab56125de299365a8634ff872c0ac Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Thu, 31 May 2018 12:47:40 +0200 Subject: [PATCH 16/45] fix extent selection --- src/Map3d.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Map3d.cpp b/src/Map3d.cpp index 3377d960..5297b3bf 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -854,12 +854,12 @@ bool Map3d::extract_and_add_polygon(GDALDataset* dataSource, PolygonFile* file) //-- check if extent is given and polygons need filtering bool useRequestedExtent = false; - OGREnvelope envelope = OGREnvelope(); + OGREnvelope extent = OGREnvelope(); if (boost::geometry::area(_requestedExtent) > 0) { - envelope.MinX = bg::get(_requestedExtent); - envelope.MaxX = bg::get(_requestedExtent); - envelope.MinY = bg::get(_requestedExtent); - envelope.MaxY = bg::get(_requestedExtent); + extent.MinX = bg::get(_requestedExtent); + extent.MaxX = bg::get(_requestedExtent); + extent.MinY = bg::get(_requestedExtent); + extent.MaxY = bg::get(_requestedExtent); useRequestedExtent = true; } @@ -876,7 +876,7 @@ bool Map3d::extract_and_add_polygon(GDALDataset* dataSource, PolygonFile* file) } //-- add the polygon of no extent is used or if the envelope is within the extent - if (!useRequestedExtent || envelope.Intersects(env)) { + if (!useRequestedExtent || extent.Intersects(env)) { switch (geometry->getGeometryType()) { case wkbPolygon: case wkbPolygon25D: { From 4fd4b539afc7b7d15d6a3a4f1fe25d2621d3a60f Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Thu, 31 May 2018 14:04:24 +0200 Subject: [PATCH 17/45] fix validation road_outliers --- src/main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index ba96dba7..08ff736c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -865,6 +865,12 @@ bool validate_yaml(const char* arg, std::set& allowedFeatures) { std::cerr << "\tOption 'Road.height' invalid; must be 'percentile-XX'.\n"; } } + if (n["Road"]["threshold_outliers"]) { + if (is_string_integer(n["Road"]["threshold_outliers"].as()) == false) { + wentgood = false; + std::cerr << "\tOption 'Road.threshold_outliers' invalid; must be an integer.\n"; + } + } if (n["Road"]["use_LAS_classes"]) { YAML::Node tmp = n["Road"]["use_LAS_classes"]; for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) { From 624ce7c0ed835bae4e0eaafd5c93d215d7b8a42f Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Thu, 31 May 2018 18:01:39 +0200 Subject: [PATCH 18/45] improve road outlier detection to connected stretch --- src/TopoFeature.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index d1934561..b4a8409b 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -1131,6 +1131,8 @@ void Boundary3D::detect_outliers(int degrees_incline) { for (Ring2& ring : rings) { ringi++; std::vector ringz = _p2z[ringi]; + std::vector inclines(_p2z[ringi].size()); + std::vector changez(_p2z[ringi].size(), 0); float PI = 3.14159265; for (int i = 0; i < ring.size(); i++) { @@ -1152,7 +1154,52 @@ void Boundary3D::detect_outliers(int degrees_incline) { incline = incline - 2 * PI; } incline = incline * 180 / PI; + //if (incline > 0) we have a peak down, otherwise we have a peak up + if (abs(incline) > degrees_incline) { + if (abs(len2z) > abs(len1z)) { + if (incline > 0) { + // going from 'flat' surface up + changez[i] = 1; + } + else { + // going from 'flat' surface down + changez[i] = -1; + } + } + } + } + // find the index of the first peak up (value == 1) + int start_idx = std::find(changez.begin(), changez.end(), 1) - changez.begin(); + if (start_idx == changez.size()) { + // else find the index of the first peak down (value == -1) + start_idx = std::find(changez.begin(), changez.end(), -1) - changez.begin(); + if (start_idx == changez.size()) { + start_idx = 0; + } + } + // Detect and fix outliers starting from the first peak up detected previously + for (int j = 0; j < ring.size(); ++j) { + //make round-trip through vector starting at start_idx + int i = (j + start_idx + ring.size()) % ring.size(); + int i0 = i - 1; + int i2 = i + 1; + if (i == 0) { + i0 = ring.size() - 1; + } + if (i2 == ring.size()) { + i2 = 0; + } + float len1z = (ringz[i] - ringz[i0]) / 100.0; + float len2z = (ringz[i2] - ringz[i]) / 100.0; + float incline = atan2(len2z, distance(ring[i], ring[i2])) - atan2(len1z, distance(ring[i0], ring[i])); + if (incline <= -PI) { + incline = 2 * PI + incline; + } + if (incline > PI) { + incline = incline - 2 * PI; + } + incline = incline * 180 / PI; //if (incline > 0) we have a peak down, otherwise we have a peak up if (abs(incline) > degrees_incline) { //find the outlier by sorting and comparing distance From 1d298fa086c3d499fbc6925bd32a138bd7de8fd6 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Mon, 18 Jun 2018 15:47:48 +0200 Subject: [PATCH 19/45] Fix check polygon file exist, its now using GDAL instead of ifopen --- src/main.cpp | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 08ff736c..0447ff16 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -370,14 +370,29 @@ int main(int argc, const char * argv[]) { //-- check if all polygon files exist bool bPolyData = false; - for (auto file : polygonFiles) { - std::ifstream f(file.filename); - if (f.good()) { - bPolyData = true; - } - else { +#if GDAL_VERSION_MAJOR < 2 + if (OGRSFDriverRegistrar::GetRegistrar()->GetDriverCount() == 0) + OGRRegisterAll(); +#else + if (GDALGetDriverCount() == 0) + GDALAllRegister(); +#endif + + for (auto file = polygonFiles.begin(); file != polygonFiles.end(); ++file) { +#if GDAL_VERSION_MAJOR < 2 + OGRDataSource *dataSource = OGRSFDriverRegistrar::Open(file->filename.c_str(), false); +#else + GDALDataset *dataSource = (GDALDataset*)GDALOpenEx(file->filename.c_str(), GDAL_OF_READONLY, NULL, NULL, NULL); +#endif + if (dataSource == NULL) { bPolyData = false; - std::cerr << "ERROR: Missing polygon data, cannot open file " << file.filename << std::endl; + } +#if GDAL_VERSION_MAJOR < 2 + OGRDataSource::DestroyDataSource(dataSource); +#else + GDALClose(dataSource); +#endif + if (bPolyData == false) { return EXIT_FAILURE; } } From 7c01b32c371f317e878c77d19f3902c186289359 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Tue, 19 Jun 2018 13:57:44 +0200 Subject: [PATCH 20/45] fix datasource exist check --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 0447ff16..afb65b65 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -384,8 +384,8 @@ int main(int argc, const char * argv[]) { #else GDALDataset *dataSource = (GDALDataset*)GDALOpenEx(file->filename.c_str(), GDAL_OF_READONLY, NULL, NULL, NULL); #endif - if (dataSource == NULL) { - bPolyData = false; + if (dataSource != NULL) { + bPolyData = true; } #if GDAL_VERSION_MAJOR < 2 OGRDataSource::DestroyDataSource(dataSource); From aa61a52748b5b1d6d434de64dc9b9258b5bc2d70 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Tue, 19 Jun 2018 15:07:15 +0200 Subject: [PATCH 21/45] fix opening of postgres connection due to GDAL gnm driver. --- src/Map3d.cpp | 16 ++++++++++++---- src/main.cpp | 9 +++++++-- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Map3d.cpp b/src/Map3d.cpp index 5297b3bf..02360916 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -788,15 +788,20 @@ bool Map3d::add_polygons_files(std::vector &files) { #endif for (auto file = files.begin(); file != files.end(); ++file) { - std::clog << "Reading input dataset: " << file->filename << std::endl; + std::string logstring = "Reading input dataset: " + file->filename; + if (strncmp(file->filename.c_str(), "PG:", strlen("PG:")) == 0) { + logstring = "Opening PostgreSQL database connection."; + } + std::clog << logstring << std::endl; + #if GDAL_VERSION_MAJOR < 2 OGRDataSource *dataSource = OGRSFDriverRegistrar::Open(file->filename.c_str(), false); #else - GDALDataset *dataSource = (GDALDataset*)GDALOpenEx(file->filename.c_str(), GDAL_OF_READONLY, NULL, NULL, NULL); + GDALDataset *dataSource = (GDALDataset*)GDALOpenEx(file->filename.c_str(), GDAL_OF_READONLY | GDAL_OF_VECTOR, NULL, NULL, NULL); #endif if (dataSource == NULL) { - std::cerr << "\tERROR: could not open file: " + file->filename << std::endl; + std::cerr << "\tERROR: " << logstring << std::endl; return false; } @@ -842,7 +847,10 @@ bool Map3d::extract_and_add_polygon(GDALDataset* dataSource, PolygonFile* file) std::cerr << "ERROR: field '" << idfield << "' not found in layer '" << l.first << "'.\n"; return false; } - if (dataLayer->FindFieldIndex(heightfield, false) == -1) { + if (strlen(heightfield) == 0) { + std::clog << "Using all polygons in layer '" << l.first << "'.\n"; + } + else if (dataLayer->FindFieldIndex(heightfield, false) == -1) { std::clog << "Warning: field '" << heightfield << "' not found in layer '" << l.first << "', using all polygons.\n"; } dataLayer->ResetReading(); diff --git a/src/main.cpp b/src/main.cpp index afb65b65..ef3787a9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -335,7 +335,7 @@ int main(int argc, const char * argv[]) { uniqueid = (*it)["uniqueid"].as(); } // Get the correct height attribute - std::string heightfield = "heightfield"; + std::string heightfield = ""; if ((*it)["height_field"]) { heightfield = (*it)["height_field"].as(); } @@ -382,7 +382,7 @@ int main(int argc, const char * argv[]) { #if GDAL_VERSION_MAJOR < 2 OGRDataSource *dataSource = OGRSFDriverRegistrar::Open(file->filename.c_str(), false); #else - GDALDataset *dataSource = (GDALDataset*)GDALOpenEx(file->filename.c_str(), GDAL_OF_READONLY, NULL, NULL, NULL); + GDALDataset *dataSource = (GDALDataset*)GDALOpenEx(file->filename.c_str(), GDAL_OF_READONLY | GDAL_OF_VECTOR, NULL, NULL, NULL); #endif if (dataSource != NULL) { bPolyData = true; @@ -393,6 +393,11 @@ int main(int argc, const char * argv[]) { GDALClose(dataSource); #endif if (bPolyData == false) { + std::string logstring = "Reading input dataset: " + file->filename; + if (strncmp(file->filename.c_str(), "PG:", strlen("PG:")) == 0) { + logstring = "Opening PostgreSQL database connection."; + } + std::cerr << "\tERROR: " << logstring << std::endl; return EXIT_FAILURE; } } From 6f56368bde7b7e651e27e7a79a4f449610f41301 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Mon, 25 Jun 2018 15:55:15 +0200 Subject: [PATCH 22/45] Fix CSV output formats and skip 3dfying for these --- src/Bridge.cpp | 1 - src/Building.cpp | 7 +++++-- src/TopoFeature.cpp | 2 -- src/main.cpp | 31 ++++++++++++++++++------------- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/Bridge.cpp b/src/Bridge.cpp index 39da2a0c..aff9f303 100644 --- a/src/Bridge.cpp +++ b/src/Bridge.cpp @@ -57,7 +57,6 @@ bool Bridge::add_elevation_point(Point2 &p, double z, float radius, int lasclass bool Bridge::lift() { lift_each_boundary_vertices(_heightref); - //smooth_boundary(5); return true; } diff --git a/src/Building.cpp b/src/Building.cpp index 9507a9f2..fba12bc7 100644 --- a/src/Building.cpp +++ b/src/Building.cpp @@ -64,7 +64,7 @@ std::string Building::get_all_z_values() { std::sort(allz.begin(), allz.end()); std::stringstream ss; for (auto& z : allz) - ss << z << "|"; + ss << z / 100.0 << "|"; return ss.str(); } @@ -285,7 +285,10 @@ bool Building::is_hard() { } void Building::get_csv(std::wostream& of) { - of << this->get_id() << ";" << std::setprecision(2) << std::fixed << this->get_height() << ";" << this->get_height_base() << "\n"; + of << this->get_id() << ";" << + std::setprecision(2) << std::fixed << + this->get_height_roof_at_percentile(_heightref_top) / 100.0 << ";" << + this->get_height_ground_at_percentile(_heightref_base) / 100.0 << "\n"; } std::string Building::get_mtl() { diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index b4a8409b..50a2b661 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -1081,8 +1081,6 @@ bool Flat::lift_percentile(float percentile) { z = _zvaluesinside[_zvaluesinside.size() * percentile]; } this->lift_all_boundary_vertices_same_height(z); - _zvaluesinside.clear(); - _zvaluesinside.shrink_to_fit(); return true; } diff --git a/src/main.cpp b/src/main.cpp index ef3787a9..bce63911 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -499,26 +499,31 @@ int main(int argc, const char * argv[]) { std::clog << "3dfying all input polygons...\n"; bool threedfy = true; bool cdt = true; - if (outputs.size() == 1) { - if (outputs.count("CSV-BUILDINGS-MULTIPLE") == 1) { + int outputcount = 0; + for (auto& each : outputs) { + if (each.second != "") { + outputcount++; + } + } + if (outputcount == 1) { + if (outputs.count("CSV-BUILDINGS") == 1) { + threedfy = false; + cdt = false; + std::clog << "CSV-BUILDINGS: no 3D reconstruction" << std::endl; + } + else if (outputs.count("CSV-BUILDINGS-MULTIPLE") == 1) { threedfy = false; + cdt = false; std::clog << "CSV-BUILDINGS-MULTIPLE: no 3D reconstruction" << std::endl; } else if (outputs.count("CSV-BUILDINGS-ALL-Z") == 1) { threedfy = false; + cdt = false; std::clog << "CSV-BUILDINGS-ALL-Z: no 3D reconstruction" << std::endl; } - else { - if (outputs.count("OBJ-NoID") == 1) { - // for OBJ-NoID only lift objects, skip stitching - bStitching = false; - } - if (outputs.count("CSV-BUILDINGS") == 1) { - // for CSV-BUILDINGS only lift objects, skip stitching and skip CDT. - // TODO: Make CSV-BUILDINGS like CSV-BUILDINGS-MULTIPLE so no lifting is needed at all - bStitching = false; - cdt = false; - } + else if (outputs.count("OBJ-NoID") == 1) { + // for OBJ-NoID only lift objects, skip stitching + bStitching = false; } } if (threedfy) { From 97b0386dd67d4204e5b42b2232ba70f5b6581ff1 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Fri, 29 Jun 2018 12:07:43 +0200 Subject: [PATCH 23/45] fix output type check for skipping 3dfying --- src/main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index bce63911..ada98223 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -506,22 +506,22 @@ int main(int argc, const char * argv[]) { } } if (outputcount == 1) { - if (outputs.count("CSV-BUILDINGS") == 1) { + if (outputs["CSV-BUILDINGS"] != "") { threedfy = false; cdt = false; std::clog << "CSV-BUILDINGS: no 3D reconstruction" << std::endl; } - else if (outputs.count("CSV-BUILDINGS-MULTIPLE") == 1) { + else if (outputs["CSV-BUILDINGS-MULTIPLE"] != "") { threedfy = false; cdt = false; std::clog << "CSV-BUILDINGS-MULTIPLE: no 3D reconstruction" << std::endl; } - else if (outputs.count("CSV-BUILDINGS-ALL-Z") == 1) { + else if (outputs["CSV-BUILDINGS-ALL-Z"] != "") { threedfy = false; cdt = false; std::clog << "CSV-BUILDINGS-ALL-Z: no 3D reconstruction" << std::endl; } - else if (outputs.count("OBJ-NoID") == 1) { + else if (outputs["OBJ-NoID"] != "") { // for OBJ-NoID only lift objects, skip stitching bStitching = false; } From e56c366306988b71a545a6e6d0c77f81fd2966a3 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Fri, 13 Jul 2018 16:00:05 +0200 Subject: [PATCH 24/45] seperate stitching of top to stich_one_vertex and bottom stiching to stitch_bridges --- src/Map3d.cpp | 96 ++++++++++++++++++++++++++++----------------- src/Map3d.h | 1 + src/TopoFeature.cpp | 4 +- 3 files changed, 63 insertions(+), 38 deletions(-) diff --git a/src/Map3d.cpp b/src/Map3d.cpp index 02360916..7ab90849 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -1065,7 +1065,7 @@ void Map3d::collect_adjacent_features(TopoFeature* f) { void Map3d::stitch_lifted_features() { std::vector ringis, pis; for (auto& f : _lsFeatures) { - if (f->get_class() != BRIDGE) { + if (!(f->get_class() == BRIDGE && f->get_top_level())) { //-- gather all rings std::vector therings; Polygon2* poly = f->get_Polygon2(); @@ -1116,10 +1116,10 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st if (_nc[key_bucket].empty() && _nc_building_walls[key_bucket].empty()) { //-- degree of vertex == 2 if (star.size() == 1) { - if (std::get<0>(star[0])->get_class() != BRIDGE) { + if (!(std::get<0>(star[0])->get_class() == BRIDGE && std::get<0>(star[0])->get_top_level())) { TopoFeature* fadj = std::get<0>(star[0]); //-- if not building or both soft, then average. - if (f->get_class() != BUILDING && fadj->get_class() != BUILDING && (f->is_hard() == false && fadj->is_hard() == false)) { + if (f->get_class() != BUILDING && fadj->get_class() != BUILDING && (f->is_hard() == false && fadj->is_hard() == false)){ stitch_average(f, ringi, pi, fadj, std::get<1>(star[0]), std::get<2>(star[0])); } else { @@ -1141,7 +1141,7 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st ringi, pi)); for (auto& fadj : star) { - if (std::get<0>(fadj)->get_class() != BRIDGE) { + if (!(std::get<0>(fadj)->get_class() == BRIDGE && std::get<0>(fadj)->get_top_level())) { zstar.push_back(std::make_tuple( std::get<0>(fadj)->get_vertex_elevation(std::get<1>(fadj), std::get<2>(fadj)), std::get<0>(fadj), @@ -1217,10 +1217,18 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st if (deltaz < this->_threshold_jump_edges) { fnext = it2; // it and it2 are same class, set height to first since averaging doesn't work if >2 objects of same class within threshold - // this mainly applies for bridges and outlier detection of roads, otherwise it shouldn't be possible + // this mainly applies for bridges and outlier detection of roads, otherwise it shouldn't be happening if (std::get<1>(*it)->get_class() == std::get<1>(*it2)->get_class()) { std::get<0>(*it2) = std::get<0>(*it); } + else if (std::get<1>(*it)->get_class() == BRIDGE) { + //stitch bridge to adjacent object + std::get<0>(*it) = std::get<0>(*it2); + } + else if (std::get<1>(*it2)->get_class() == BRIDGE) { + //stitch bridge to adjacent object + std::get<0>(*it2) = std::get<0>(*it); + } // it is hard, it2 is hard // keep height when both are hard surfaces, add vw else if (std::get<1>(*it)->is_hard()) { @@ -1350,14 +1358,13 @@ void Map3d::stitch_jumpedge(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f int avgz = (f1->get_vertex_elevation(ringi1, pi1) + f2->get_vertex_elevation(ringi2, pi2)) / 2; f1->set_vertex_elevation(ringi1, pi1, avgz); f2->set_vertex_elevation(ringi2, pi2, avgz); - _nc[key_bucket].push_back(avgz); bStitched = true; } - if (f1->is_hard() == false) { + if (f1->is_hard() == false || f1->get_class() == BRIDGE) { f1->set_vertex_elevation(ringi1, pi1, f2z); bStitched = true; } - else if (f2->is_hard() == false) { + else if (f2->is_hard() == false || f2->get_class() == BRIDGE) { f2->set_vertex_elevation(ringi2, pi2, f1z); bStitched = true; } @@ -1397,7 +1404,10 @@ void Map3d::stitch_average(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f2 void Map3d::stitch_bridges() { std::vector ringis, pis; for (auto& f : _lsFeatures) { - if (f->get_class() == BRIDGE) { + if (f->get_class() == BRIDGE && f->get_top_level()) { + if (f->get_id() == "O0000.50296c5b81dd447594084b91e4461944") { + std::cout << "stop" << std::endl; + } //-- 1. store all touching top level (adjacent + incident) std::vector* lstouching = f->get_adjacent_features(); @@ -1422,6 +1432,17 @@ void Map3d::stitch_bridges() { if (unique > 0) { bool bridgeAdj = false; + + for (auto& fadj : *lstouching) { + ringis.clear(); + pis.clear(); + if (fadj->get_class() == BRIDGE && fadj->get_top_level() == false && + fadj->has_point2(ring[i], ringis, pis) && std::find(nc.begin(), nc.end(), fadj->get_vertex_elevation(ringis[0], pis[0])) != nc.end()) { + unique = unique - 1; + std::cout << "Unique new: " << unique << std::endl; + } + } + /* this can be 3 cases; 1. location where two bridges and other object meet without height jump. 2. location where one bridge meets multiple objects which should be a stitching error @@ -1431,7 +1452,7 @@ void Map3d::stitch_bridges() { for (auto& fadj : *lstouching) { ringis.clear(); pis.clear(); - if (fadj->get_class() == BRIDGE && fadj->has_point2(ring[i], ringis, pis) == true) { + if (fadj->get_class() == BRIDGE && fadj->get_top_level() && fadj->has_point2(ring[i], ringis, pis)) { bridgeAdj = true; } } @@ -1442,13 +1463,9 @@ void Map3d::stitch_bridges() { */ if (bridgeAdj || unique > 1) { //TODO: use NC closest to height of previous vertex? Then is no choice to make and it might support multiple overlapping bridges? - if (f->get_top_level() == false) { - f->set_vertex_elevation(ringi, i, nc.front()); - } - else { - f->set_vertex_elevation(ringi, i, nc.back()); - } - + //This only works if previous is updated before checking, that is not the case now + //auto const it = std::lower_bound(nc.begin(), nc.end(), f->get_vertex_elevation(ringi, i-1)); + f->set_vertex_elevation(ringi, i, nc.front()); // if there are more then 1 unique height in NC we have a height jump. bool heightjump = false; if (unique > 1) { @@ -1472,13 +1489,17 @@ void Map3d::stitch_bridges() { std::vector vertices; if (endCornerIdx < startCorner.first) { + for (int i = startCorner.first + 1; i < ring.size(); i++) { + vertices.push_back(i); + } for (int i = 0; i < endCornerIdx; i++) { vertices.push_back(i); } - endCornerIdx = ring.size(); } - for (int i = startCorner.first + 1; i < endCornerIdx; i++) { - vertices.push_back(i); + else { + for (int i = startCorner.first + 1; i < endCornerIdx; i++) { + vertices.push_back(i); + } } for (int pi : vertices) { @@ -1493,7 +1514,6 @@ void Map3d::stitch_bridges() { stitchz = fadj->get_vertex_elevation(ringis[0], pis[0]); } } - if (!otherAdj) { //No other type of objects then bridges are adjacent //This is empty stretch of points where only bridges connect, interpolate between prevCorner and nextCorner Point2 p = f->get_point2(ringi, pi); @@ -1503,7 +1523,6 @@ void Map3d::stitch_bridges() { if (_nc[key_bucket].empty()) { // Add height to NC for adjacent bridge // interpolate height between previous and next corner distance weighted - // interpolate height between previous and next corner distance weighted z = interpolate_height(f, p, ringi, startCorner.first, ringi, endCorner.first); _nc[key_bucket].push_back(z); } @@ -1512,25 +1531,30 @@ void Map3d::stitch_bridges() { } f->set_vertex_elevation(ringi, pi, z); } - else if (startCorner.second && endCorner.second) { //startCorner.heightjump && endCorner.heightjump + //else if (startCorner.second && endCorner.second) { //startCorner.heightjump && endCorner.heightjump //This is empty stretch of points at height jump and need to place VW Point2 p = f->get_point2(ringi, pi); std::string key_bucket = gen_key_bucket(&p); // interpolate height between previous and next corner distance weighted int z = interpolate_height(f, p, ringi, startCorner.first, ringi, endCorner.first); - f->set_vertex_elevation(ringi, pi, z); - // Add height to NC and add VW - _nc[key_bucket].push_back(z); - f->add_vertical_wall(); - } - else { - //are these the connected points? Only other case possible? - //cases (not exaustive): - //prevCorner.heightjump && this.otherAdj - //nextCorner.heightjump && this.otherAdj - // Stitch to acjacent feature - f->set_vertex_elevation(ringi, pi, stitchz); - } + if (abs(stitchz - z) > _threshold_jump_edges) { + f->set_vertex_elevation(ringi, pi, z); + // Add height to NC and add VW + _nc[key_bucket].push_back(z); + f->add_vertical_wall(); + } + else { + f->set_vertex_elevation(ringi, pi, stitchz); + } + //} + //else { + // //are these the connected points? Only other case possible? + // //cases (not exaustive): + // //prevCorner.heightjump && this.otherAdj + // //nextCorner.heightjump && this.otherAdj + // // Stitch to acjacent feature + // f->set_vertex_elevation(ringi, pi, stitchz); + //} } } } diff --git a/src/Map3d.h b/src/Map3d.h index 40e9b065..e68f3412 100644 --- a/src/Map3d.h +++ b/src/Map3d.h @@ -135,6 +135,7 @@ class Map3d { NodeColumn _nc; NodeColumn _nc_building_walls; + std::unordered_map _bridge_stitches; std::vector _lsFeatures; std::vector _allowed_layers; bgi::rtree< PairIndexed, bgi::rstar<16> > _rtree; diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index 50a2b661..7feae7ef 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -518,7 +518,7 @@ void TopoFeature::construct_vertical_walls(const NodeColumn& nc) { continue; //-- Make exeption for bridges, they have vw's from bottom up, swap. Also skip if adjacent feature is bridge, vw is then created by bridge - if (this->get_class() == BRIDGE) { + if (this->get_class() == BRIDGE && this->get_top_level()) { //-- find the height of the vertex in the node column std::vector::const_iterator sait, eait, sbit, ebit; sait = std::find(anc.begin(), anc.end(), az); @@ -572,7 +572,7 @@ void TopoFeature::construct_vertical_walls(const NodeColumn& nc) { _triangles_vw.push_back(t); } } - if (fadj->get_class() == BRIDGE) { + if (this->get_class() == BRIDGE || fadj->get_class() == BRIDGE) { continue; } From 3897a4d07cf2011c91fb434ff850be86a49cf9b0 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Fri, 13 Jul 2018 18:05:05 +0200 Subject: [PATCH 25/45] put all bridge stitching in separate function again and its a likely outcome! --- src/Map3d.cpp | 108 ++++++++++++++++++++++++++++++++++---------------- src/Map3d.h | 2 +- 2 files changed, 74 insertions(+), 36 deletions(-) diff --git a/src/Map3d.cpp b/src/Map3d.cpp index 7ab90849..da0af455 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -1065,7 +1065,7 @@ void Map3d::collect_adjacent_features(TopoFeature* f) { void Map3d::stitch_lifted_features() { std::vector ringis, pis; for (auto& f : _lsFeatures) { - if (!(f->get_class() == BRIDGE && f->get_top_level())) { + if (f->get_class() != BRIDGE) { //-- gather all rings std::vector therings; Polygon2* poly = f->get_Polygon2(); @@ -1113,23 +1113,19 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st //-- get p and key_bucket once and check if nc location is empty Point2 p = f->get_point2(ringi, pi); std::string key_bucket = gen_key_bucket(&p); - if (_nc[key_bucket].empty() && _nc_building_walls[key_bucket].empty()) { + if (_nc.find(key_bucket) == _nc.end() && _nc_building_walls.find(key_bucket) == _nc_building_walls.end()) { //-- degree of vertex == 2 if (star.size() == 1) { - if (!(std::get<0>(star[0])->get_class() == BRIDGE && std::get<0>(star[0])->get_top_level())) { + if (std::get<0>(star[0])->get_class() != BRIDGE) { TopoFeature* fadj = std::get<0>(star[0]); //-- if not building or both soft, then average. - if (f->get_class() != BUILDING && fadj->get_class() != BUILDING && (f->is_hard() == false && fadj->is_hard() == false)){ + if (f->get_class() != BUILDING && fadj->get_class() != BUILDING && (f->is_hard() == false && fadj->is_hard() == false)) { stitch_average(f, ringi, pi, fadj, std::get<1>(star[0]), std::get<2>(star[0])); } else { stitch_jumpedge(f, ringi, pi, fadj, std::get<1>(star[0]), std::get<2>(star[0])); } } - else { - // for bridges we need to create VW and therefor add the height to the NC - _nc[key_bucket].push_back(f->get_vertex_elevation(ringi, pi)); - } } //-- degree of vertex >= 3: more complex cases else if (star.size() > 1) { @@ -1141,7 +1137,7 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st ringi, pi)); for (auto& fadj : star) { - if (!(std::get<0>(fadj)->get_class() == BRIDGE && std::get<0>(fadj)->get_top_level())) { + if (std::get<0>(fadj)->get_class() != BRIDGE) { zstar.push_back(std::make_tuple( std::get<0>(fadj)->get_vertex_elevation(std::get<1>(fadj), std::get<2>(fadj)), std::get<0>(fadj), @@ -1221,14 +1217,6 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st if (std::get<1>(*it)->get_class() == std::get<1>(*it2)->get_class()) { std::get<0>(*it2) = std::get<0>(*it); } - else if (std::get<1>(*it)->get_class() == BRIDGE) { - //stitch bridge to adjacent object - std::get<0>(*it) = std::get<0>(*it2); - } - else if (std::get<1>(*it2)->get_class() == BRIDGE) { - //stitch bridge to adjacent object - std::get<0>(*it2) = std::get<0>(*it); - } // it is hard, it2 is hard // keep height when both are hard surfaces, add vw else if (std::get<1>(*it)->is_hard()) { @@ -1360,11 +1348,11 @@ void Map3d::stitch_jumpedge(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f f2->set_vertex_elevation(ringi2, pi2, avgz); bStitched = true; } - if (f1->is_hard() == false || f1->get_class() == BRIDGE) { + if (f1->is_hard() == false) { f1->set_vertex_elevation(ringi1, pi1, f2z); bStitched = true; } - else if (f2->is_hard() == false || f2->get_class() == BRIDGE) { + else if (f2->is_hard() == false) { f2->set_vertex_elevation(ringi2, pi2, f1z); bStitched = true; } @@ -1403,6 +1391,45 @@ void Map3d::stitch_average(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f2 void Map3d::stitch_bridges() { std::vector ringis, pis; + for (auto& f : _lsFeatures) { + if (f->get_class() == BRIDGE && f->get_top_level() == false) { + if (f->get_id() == "O0000.5cc4c028393642bd8e65738bc8004d0a") { + std::cout << "stop" << std::endl; + } + //-- 1. store all touching top level (adjacent + incident) + std::vector* lstouching = f->get_adjacent_features(); + + //-- gather all rings + std::vector rings; + rings.push_back(f->get_Polygon2()->outer()); + for (Ring2& iring : f->get_Polygon2()->inners()) + rings.push_back(iring); + + int ringi = -1; + for (Ring2& ring : rings) { + ringi++; + + for (int i = 0; i < ring.size(); i++) { + for (auto& fadj : *lstouching) { + ringis.clear(); + pis.clear(); + if (!(fadj->get_class() == BRIDGE && fadj->get_top_level()) && fadj->has_point2(ring[i], ringis, pis)) { + int z = fadj->get_vertex_elevation(ringis[0], pis[0]); + if (abs(f->get_vertex_elevation(ringi, i) - z < _threshold_jump_edges)) { + f->set_vertex_elevation(ringi, i, z); + // Add height to NC + Point2 p = f->get_point2(ringi, i); + std::string key_bucket = gen_key_bucket(&p); + _nc[key_bucket].push_back(z); + _bridge_stitches[key_bucket] = z; + } + } + } + } + } + } + } + for (auto& f : _lsFeatures) { if (f->get_class() == BRIDGE && f->get_top_level()) { if (f->get_id() == "O0000.50296c5b81dd447594084b91e4461944") { @@ -1425,23 +1452,26 @@ void Map3d::stitch_bridges() { for (int i = 0; i < ring.size(); i++) { Point2 p = f->get_point2(ringi, i); std::string key_bucket = gen_key_bucket(&p); - std::vector nc = _nc[key_bucket]; - std::sort(nc.begin(), nc.end()); - auto ncuIt = std::unique(nc.begin(), nc.end()); - int unique = std::distance(nc.begin(), ncuIt); - + std::vector nc; + int unique = 0; + if (_nc.find(key_bucket) != _nc.end()) { + nc = _nc[key_bucket]; + std::sort(nc.begin(), nc.end()); + auto ncuIt = std::unique(nc.begin(), nc.end()); + unique = std::distance(nc.begin(), ncuIt); + } if (unique > 0) { bool bridgeAdj = false; - for (auto& fadj : *lstouching) { - ringis.clear(); - pis.clear(); - if (fadj->get_class() == BRIDGE && fadj->get_top_level() == false && - fadj->has_point2(ring[i], ringis, pis) && std::find(nc.begin(), nc.end(), fadj->get_vertex_elevation(ringis[0], pis[0])) != nc.end()) { - unique = unique - 1; - std::cout << "Unique new: " << unique << std::endl; - } - } + //for (auto& fadj : *lstouching) { + // ringis.clear(); + // pis.clear(); + // if (fadj->get_class() == BRIDGE && fadj->get_top_level() == false && + // fadj->has_point2(ring[i], ringis, pis) && std::find(nc.begin(), nc.end(), fadj->get_vertex_elevation(ringis[0], pis[0])) != nc.end()) { + // unique = unique - 1; + // std::cout << "Unique new: " << unique << std::endl; + // } + //} /* this can be 3 cases; 1. location where two bridges and other object meet without height jump. @@ -1520,7 +1550,7 @@ void Map3d::stitch_bridges() { std::string key_bucket = gen_key_bucket(&p); int z = 0; //Check if height exists in NC thus is created by adjacent bridge - if (_nc[key_bucket].empty()) { + if (_nc.find(key_bucket) == _nc.end()) { // Add height to NC for adjacent bridge // interpolate height between previous and next corner distance weighted z = interpolate_height(f, p, ringi, startCorner.first, ringi, endCorner.first); @@ -1537,7 +1567,15 @@ void Map3d::stitch_bridges() { std::string key_bucket = gen_key_bucket(&p); // interpolate height between previous and next corner distance weighted int z = interpolate_height(f, p, ringi, startCorner.first, ringi, endCorner.first); - if (abs(stitchz - z) > _threshold_jump_edges) { + + // when there is no value in _bridge_stitches the upper part is floating thus stitch bottem to adjacent object + // TODO: This doesnt work for the case we have an high adjacent object, need to check the jump_edges also somehow but this doesnt work in other cases + if (_bridge_stitches.find(key_bucket) == _bridge_stitches.end()) { + f->set_vertex_elevation(ringi, pi, stitchz); + } + // when there is a value in _bridge_stitches the upper part is stitched to the adjacent thus place to bottom and make VW + // and when the height between the adjacent object and the interpolated height of the corners are larger then threshold jump edges + else if (abs(stitchz - z) > _threshold_jump_edges) { f->set_vertex_elevation(ringi, pi, z); // Add height to NC and add VW _nc[key_bucket].push_back(z); diff --git a/src/Map3d.h b/src/Map3d.h index e68f3412..ba8973ef 100644 --- a/src/Map3d.h +++ b/src/Map3d.h @@ -135,7 +135,7 @@ class Map3d { NodeColumn _nc; NodeColumn _nc_building_walls; - std::unordered_map _bridge_stitches; + std::unordered_map _bridge_stitches; std::vector _lsFeatures; std::vector _allowed_layers; bgi::rtree< PairIndexed, bgi::rstar<16> > _rtree; From 104eff6a0d4860ae4a975e41caf5ee3a9103cf92 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Wed, 18 Jul 2018 11:46:34 +0200 Subject: [PATCH 26/45] add vector of stitched bridges --- src/Map3d.cpp | 123 ++++++++++++++++++++++++++++------------------ src/TopoFeature.h | 2 +- 2 files changed, 75 insertions(+), 50 deletions(-) diff --git a/src/Map3d.cpp b/src/Map3d.cpp index da0af455..d3a6eb1e 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -1393,9 +1393,6 @@ void Map3d::stitch_bridges() { std::vector ringis, pis; for (auto& f : _lsFeatures) { if (f->get_class() == BRIDGE && f->get_top_level() == false) { - if (f->get_id() == "O0000.5cc4c028393642bd8e65738bc8004d0a") { - std::cout << "stop" << std::endl; - } //-- 1. store all touching top level (adjacent + incident) std::vector* lstouching = f->get_adjacent_features(); @@ -1427,14 +1424,16 @@ void Map3d::stitch_bridges() { } } } + Bridge* b = dynamic_cast(f); + b->detect_outliers(_road_threshold_outliers); } } for (auto& f : _lsFeatures) { if (f->get_class() == BRIDGE && f->get_top_level()) { - if (f->get_id() == "O0000.50296c5b81dd447594084b91e4461944") { - std::cout << "stop" << std::endl; - } + //if (f->get_id() == "O0000.5cbe3212cc834f47ba6dfbc4dc83f71c") { + // std::cout << "stop" << std::endl; + //} //-- 1. store all touching top level (adjacent + incident) std::vector* lstouching = f->get_adjacent_features(); @@ -1463,16 +1462,6 @@ void Map3d::stitch_bridges() { if (unique > 0) { bool bridgeAdj = false; - //for (auto& fadj : *lstouching) { - // ringis.clear(); - // pis.clear(); - // if (fadj->get_class() == BRIDGE && fadj->get_top_level() == false && - // fadj->has_point2(ring[i], ringis, pis) && std::find(nc.begin(), nc.end(), fadj->get_vertex_elevation(ringis[0], pis[0])) != nc.end()) { - // unique = unique - 1; - // std::cout << "Unique new: " << unique << std::endl; - // } - //} - /* this can be 3 cases; 1. location where two bridges and other object meet without height jump. 2. location where one bridge meets multiple objects which should be a stitching error @@ -1487,18 +1476,36 @@ void Map3d::stitch_bridges() { } } } + + int previ = i - 1; + if (i == 0) { + previ = ring.size() - 1; + } + Point2 prevp = f->get_point2(ringi, previ); + std::string prev_key_bucket = gen_key_bucket(&prevp); + int nexti = i + 1; + if (i == ring.size() - 1) { + nexti = 0; + } + Point2 nextp = f->get_point2(ringi, nexti); + std::string next_key_bucket = gen_key_bucket(&nextp); + if ((_bridge_stitches.find(key_bucket) != _bridge_stitches.end() && + _bridge_stitches.find(next_key_bucket) == _bridge_stitches.end()) || + (_bridge_stitches.find(key_bucket) != _bridge_stitches.end() && + _bridge_stitches.find(prev_key_bucket) == _bridge_stitches.end()) || + + /* Two cases which are seen as corners of the bridge object, either a height jump or where two bridge objects meet 1. if bridge adjacent, use nc. This is the case where a bridge is split into multiple parts touching the adjacent object 2. if 2 or more unique values in NC there is a height jump which should only occur at vw of bridge, skip values upto next occurence */ - if (bridgeAdj || unique > 1) { - //TODO: use NC closest to height of previous vertex? Then is no choice to make and it might support multiple overlapping bridges? + bridgeAdj || unique > 1) { //This only works if previous is updated before checking, that is not the case now //auto const it = std::lower_bound(nc.begin(), nc.end(), f->get_vertex_elevation(ringi, i-1)); f->set_vertex_elevation(ringi, i, nc.front()); // if there are more then 1 unique height in NC we have a height jump. bool heightjump = false; - if (unique > 1) { + if (_bridge_stitches.find(next_key_bucket) != _bridge_stitches.end()) { heightjump = true; } corners.push_back(std::make_pair(i, heightjump)); @@ -1533,6 +1540,9 @@ void Map3d::stitch_bridges() { } for (int pi : vertices) { + //if (f->get_id() == "O0000.59d49cd362f54875b4a3196c1363606f" && pi == 18) { + // std::cout << "stop" << std::endl; + //} bool otherAdj = false; int stitchz = 0; std::vector< std::tuple > star; @@ -1551,9 +1561,14 @@ void Map3d::stitch_bridges() { int z = 0; //Check if height exists in NC thus is created by adjacent bridge if (_nc.find(key_bucket) == _nc.end()) { + // check height of previous vertex + int previ = pi - 1; + if (pi == 0) { + previ = ring.size() - 1; + } // Add height to NC for adjacent bridge - // interpolate height between previous and next corner distance weighted - z = interpolate_height(f, p, ringi, startCorner.first, ringi, endCorner.first); + // interpolate height between previous vertex and next corner distance weighted + z = interpolate_height(f, p, ringi, previ, ringi, endCorner.first); _nc[key_bucket].push_back(z); } else { @@ -1561,38 +1576,48 @@ void Map3d::stitch_bridges() { } f->set_vertex_elevation(ringi, pi, z); } - //else if (startCorner.second && endCorner.second) { //startCorner.heightjump && endCorner.heightjump - //This is empty stretch of points at height jump and need to place VW + else { Point2 p = f->get_point2(ringi, pi); std::string key_bucket = gen_key_bucket(&p); - // interpolate height between previous and next corner distance weighted - int z = interpolate_height(f, p, ringi, startCorner.first, ringi, endCorner.first); - - // when there is no value in _bridge_stitches the upper part is floating thus stitch bottem to adjacent object - // TODO: This doesnt work for the case we have an high adjacent object, need to check the jump_edges also somehow but this doesnt work in other cases - if (_bridge_stitches.find(key_bucket) == _bridge_stitches.end()) { - f->set_vertex_elevation(ringi, pi, stitchz); - } - // when there is a value in _bridge_stitches the upper part is stitched to the adjacent thus place to bottom and make VW - // and when the height between the adjacent object and the interpolated height of the corners are larger then threshold jump edges - else if (abs(stitchz - z) > _threshold_jump_edges) { - f->set_vertex_elevation(ringi, pi, z); - // Add height to NC and add VW - _nc[key_bucket].push_back(z); - f->add_vertical_wall(); + + // This is where the top part is stitched, interpolate between corners + if (startCorner.second && endCorner.second == false) { + // interpolate height between start and end corner distance weighted + int interz = interpolate_height(f, p, ringi, startCorner.first, ringi, endCorner.first); + + // Allways stitch if there is a lower object, otherwise use interpolated height + if (stitchz < interz) { + f->set_vertex_elevation(ringi, pi, stitchz); + } + else { + f->set_vertex_elevation(ringi, pi, interz); + // Add height to NC and add VW + _nc[key_bucket].push_back(interz); + f->add_vertical_wall(); + } } - else { - f->set_vertex_elevation(ringi, pi, stitchz); + else { // when there is no value in _bridge_stitches the upper part is floating thus stitch bottem to adjacent object if within threshold of previous vertex or corner + // check height of previous vertex + int previ = pi - 1; + if (pi == 0) { + previ = ring.size() - 1; + } + + // interpolate height between previous vertex and next corner distance weighted + int interz = interpolate_height(f, p, ringi, previ, ringi, endCorner.first); + int prevz = f->get_vertex_elevation(ringi, previ); + //Allways stich to lower object or if interpolated between corners within threshold or previous within threshold + if (stitchz < interz || abs(stitchz - interz) < _threshold_jump_edges || abs(stitchz - prevz) < _threshold_jump_edges) { + f->set_vertex_elevation(ringi, pi, stitchz); + } + else { + f->set_vertex_elevation(ringi, pi, interz); + // Add height to NC and add VW + _nc[key_bucket].push_back(interz); + f->add_vertical_wall(); + } } - //} - //else { - // //are these the connected points? Only other case possible? - // //cases (not exaustive): - // //prevCorner.heightjump && this.otherAdj - // //nextCorner.heightjump && this.otherAdj - // // Stitch to acjacent feature - // f->set_vertex_elevation(ringi, pi, stitchz); - //} + } } } } diff --git a/src/TopoFeature.h b/src/TopoFeature.h index 8491268d..2733fd72 100644 --- a/src/TopoFeature.h +++ b/src/TopoFeature.h @@ -137,10 +137,10 @@ class Boundary3D: public TopoFeature { virtual bool lift() = 0; virtual void get_citygml(std::wostream& of) = 0; virtual void get_cityjson(nlohmann::json& j, std::unordered_map &dPts) = 0; + void detect_outliers(int degrees_incline); protected: int _simplification; void smooth_boundary(int passes = 1); - void detect_outliers(int degrees_incline); }; //--------------------------------------------- From a04bac46cb81855cf1133e8a631c428050966522 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Tue, 24 Jul 2018 14:23:04 +0200 Subject: [PATCH 27/45] new version of stitch_bridges using storing stitches of upper bridge --- src/Map3d.cpp | 469 ++++++++++++++++++++++++++++++++------------ src/TopoFeature.cpp | 88 +++++---- 2 files changed, 386 insertions(+), 171 deletions(-) diff --git a/src/Map3d.cpp b/src/Map3d.cpp index d3a6eb1e..b0e05c56 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -1412,28 +1412,27 @@ void Map3d::stitch_bridges() { pis.clear(); if (!(fadj->get_class() == BRIDGE && fadj->get_top_level()) && fadj->has_point2(ring[i], ringis, pis)) { int z = fadj->get_vertex_elevation(ringis[0], pis[0]); - if (abs(f->get_vertex_elevation(ringi, i) - z < _threshold_jump_edges)) { + if (abs(f->get_vertex_elevation(ringi, i) - z) < _threshold_jump_edges) { f->set_vertex_elevation(ringi, i, z); - // Add height to NC - Point2 p = f->get_point2(ringi, i); - std::string key_bucket = gen_key_bucket(&p); - _nc[key_bucket].push_back(z); - _bridge_stitches[key_bucket] = z; + if (!(fadj->get_class() == BRIDGE && fadj->get_top_level() == f->get_top_level())) { + // Add height to NC + Point2 p = f->get_point2(ringi, i); + std::string key_bucket = gen_key_bucket(&p); + _nc[key_bucket].push_back(z); + _bridge_stitches[key_bucket] = z; + } } } } } } Bridge* b = dynamic_cast(f); - b->detect_outliers(_road_threshold_outliers); + //b->detect_outliers(_road_threshold_outliers); } } for (auto& f : _lsFeatures) { if (f->get_class() == BRIDGE && f->get_top_level()) { - //if (f->get_id() == "O0000.5cbe3212cc834f47ba6dfbc4dc83f71c") { - // std::cout << "stop" << std::endl; - //} //-- 1. store all touching top level (adjacent + incident) std::vector* lstouching = f->get_adjacent_features(); @@ -1447,75 +1446,80 @@ void Map3d::stitch_bridges() { for (Ring2& ring : rings) { ringi++; + //Search for corners to base stitching on + //Corners are based on highest level already stitched before + //and where a bridge is adjacent std::vector< std::pair > corners; for (int i = 0; i < ring.size(); i++) { + // find begin of stitched stretch Point2 p = f->get_point2(ringi, i); std::string key_bucket = gen_key_bucket(&p); - std::vector nc; - int unique = 0; - if (_nc.find(key_bucket) != _nc.end()) { - nc = _nc[key_bucket]; - std::sort(nc.begin(), nc.end()); - auto ncuIt = std::unique(nc.begin(), nc.end()); - unique = std::distance(nc.begin(), ncuIt); - } - if (unique > 0) { - bool bridgeAdj = false; - - /* this can be 3 cases; - 1. location where two bridges and other object meet without height jump. - 2. location where one bridge meets multiple objects which should be a stitching error - 3. error in stitching adjacent objects - */ - if (unique <= 1) { // only check for bridge adjacent if there is only 1 unique value in NC for performance - for (auto& fadj : *lstouching) { - ringis.clear(); - pis.clear(); - if (fadj->get_class() == BRIDGE && fadj->get_top_level() && fadj->has_point2(ring[i], ringis, pis)) { - bridgeAdj = true; - } - } - } - int previ = i - 1; - if (i == 0) { - previ = ring.size() - 1; - } - Point2 prevp = f->get_point2(ringi, previ); - std::string prev_key_bucket = gen_key_bucket(&prevp); + bool setheight = false; + int previ = i - 1; + if (i == 0) { + previ = ring.size() - 1; + } + Point2 prevp = f->get_point2(ringi, previ); + std::string prev_key_bucket = gen_key_bucket(&prevp); + if (_bridge_stitches.find(key_bucket) != _bridge_stitches.end() && + _bridge_stitches.find(prev_key_bucket) == _bridge_stitches.end()) { + // add start of stitched stretch to corners + corners.push_back(std::make_pair(i, true)); + setheight = true; + } + else { + // find end of stitching stretch int nexti = i + 1; if (i == ring.size() - 1) { nexti = 0; } Point2 nextp = f->get_point2(ringi, nexti); std::string next_key_bucket = gen_key_bucket(&nextp); - if ((_bridge_stitches.find(key_bucket) != _bridge_stitches.end() && - _bridge_stitches.find(next_key_bucket) == _bridge_stitches.end()) || - (_bridge_stitches.find(key_bucket) != _bridge_stitches.end() && - _bridge_stitches.find(prev_key_bucket) == _bridge_stitches.end()) || - - - /* Two cases which are seen as corners of the bridge object, either a height jump or where two bridge objects meet - 1. if bridge adjacent, use nc. This is the case where a bridge is split into multiple parts touching the adjacent object - 2. if 2 or more unique values in NC there is a height jump which should only occur at vw of bridge, skip values upto next occurence - */ - bridgeAdj || unique > 1) { - //This only works if previous is updated before checking, that is not the case now - //auto const it = std::lower_bound(nc.begin(), nc.end(), f->get_vertex_elevation(ringi, i-1)); - f->set_vertex_elevation(ringi, i, nc.front()); - // if there are more then 1 unique height in NC we have a height jump. - bool heightjump = false; - if (_bridge_stitches.find(next_key_bucket) != _bridge_stitches.end()) { - heightjump = true; + if (_bridge_stitches.find(key_bucket) != _bridge_stitches.end() && + _bridge_stitches.find(next_key_bucket) == _bridge_stitches.end()) { + // add end of stitched stretch to corners + corners.push_back(std::make_pair(i, false)); + setheight = true; + } + } + if (!setheight) { // no corner found yet + bool bridgeAdj = false; + bool otherAdj = false; + //find if there are adjacent bridges + for (auto& fadj : *lstouching) { + ringis.clear(); + pis.clear(); + if (fadj->has_point2(ring[i], ringis, pis)) { + if (fadj->get_class() == BRIDGE && fadj->get_top_level()) { + bridgeAdj = true; + } + else if (fadj->get_class() != BRIDGE) { + otherAdj = true; + } } - corners.push_back(std::make_pair(i, heightjump)); + } + + //TODO: Check previous/next vertex like with the _bridge_stitches? + if (bridgeAdj && otherAdj) { + // add end of stitched stretch to corners, set heightjump to false to stitch to adjacent object + corners.push_back(std::make_pair(i, false)); + setheight = true; } } + if (setheight) { + // set corner height to lowest value in the NC + if (_nc.find(key_bucket) == _nc.end()) { + std::clog << "ERROR: NodeColumn not filled at " << key_bucket << std::endl; + } + int z = _nc[key_bucket].front(); + f->set_vertex_elevation(ringi, i, z); + } } // Set height of vertices in between two corners for (int c = 0; c < corners.size(); c++) { - // prepair vertices to loop for this stretch + // prepair vertices list to loop for this stretch std::pair startCorner = corners[c]; // set endCorner to first or corners if end of array is reached std::pair endCorner = corners.front(); @@ -1540,82 +1544,56 @@ void Map3d::stitch_bridges() { } for (int pi : vertices) { - //if (f->get_id() == "O0000.59d49cd362f54875b4a3196c1363606f" && pi == 18) { - // std::cout << "stop" << std::endl; - //} - bool otherAdj = false; + Point2 p = f->get_point2(ringi, pi); + std::string key_bucket = gen_key_bucket(&p); int stitchz = 0; - std::vector< std::tuple > star; - for (auto& fadj : *lstouching) { - ringis.clear(); - pis.clear(); - if (fadj->get_class() != BRIDGE && fadj->has_point2(ring[pi], ringis, pis) == true) { - otherAdj = true; - stitchz = fadj->get_vertex_elevation(ringis[0], pis[0]); - } + if (_nc.find(key_bucket) != _nc.end()) { + stitchz = _nc[key_bucket].front(); } - if (!otherAdj) { //No other type of objects then bridges are adjacent - //This is empty stretch of points where only bridges connect, interpolate between prevCorner and nextCorner - Point2 p = f->get_point2(ringi, pi); - std::string key_bucket = gen_key_bucket(&p); - int z = 0; - //Check if height exists in NC thus is created by adjacent bridge - if (_nc.find(key_bucket) == _nc.end()) { - // check height of previous vertex - int previ = pi - 1; - if (pi == 0) { - previ = ring.size() - 1; + else { + for (auto& fadj : *lstouching) { + ringis.clear(); + pis.clear(); + if (fadj->get_class() != BRIDGE && fadj->has_point2(ring[pi], ringis, pis)) { + stitchz = fadj->get_vertex_elevation(ringis[0], pis[0]); + break; } - // Add height to NC for adjacent bridge - // interpolate height between previous vertex and next corner distance weighted - z = interpolate_height(f, p, ringi, previ, ringi, endCorner.first); - _nc[key_bucket].push_back(z); + } + } + + if (startCorner.second) { // Stretch where the top is stitched, interpolate between corners and add VW + // interpolate between start and end corner distance weighted + int interz = interpolate_height(f, p, ringi, startCorner.first, ringi, endCorner.first); + // Allways stitch if there is a lower object, otherwise use interpolated height + if (stitchz < interz) { + f->set_vertex_elevation(ringi, pi, stitchz); } else { - z = _nc[key_bucket].front(); + f->set_vertex_elevation(ringi, pi, interz); + // Add height to NC and add VW + _nc[key_bucket].push_back(interz); + f->add_vertical_wall(); } - f->set_vertex_elevation(ringi, pi, z); } - else { - Point2 p = f->get_point2(ringi, pi); - std::string key_bucket = gen_key_bucket(&p); - - // This is where the top part is stitched, interpolate between corners - if (startCorner.second && endCorner.second == false) { - // interpolate height between start and end corner distance weighted - int interz = interpolate_height(f, p, ringi, startCorner.first, ringi, endCorner.first); - - // Allways stitch if there is a lower object, otherwise use interpolated height - if (stitchz < interz) { - f->set_vertex_elevation(ringi, pi, stitchz); - } - else { - f->set_vertex_elevation(ringi, pi, interz); - // Add height to NC and add VW - _nc[key_bucket].push_back(interz); - f->add_vertical_wall(); - } + else { // Stretch where the bottom needs to be stitched, find adjacent object within threshold and stitch, otherwise interpolate between previous vertex and next corner + // check height of previous vertex + int previ = pi - 1; + if (pi == 0) { + previ = ring.size() - 1; } - else { // when there is no value in _bridge_stitches the upper part is floating thus stitch bottem to adjacent object if within threshold of previous vertex or corner - // check height of previous vertex - int previ = pi - 1; - if (pi == 0) { - previ = ring.size() - 1; - } - // interpolate height between previous vertex and next corner distance weighted - int interz = interpolate_height(f, p, ringi, previ, ringi, endCorner.first); - int prevz = f->get_vertex_elevation(ringi, previ); - //Allways stich to lower object or if interpolated between corners within threshold or previous within threshold - if (stitchz < interz || abs(stitchz - interz) < _threshold_jump_edges || abs(stitchz - prevz) < _threshold_jump_edges) { - f->set_vertex_elevation(ringi, pi, stitchz); - } - else { - f->set_vertex_elevation(ringi, pi, interz); - // Add height to NC and add VW - _nc[key_bucket].push_back(interz); - f->add_vertical_wall(); - } + // interpolate height between previous vertex and next corner distance weighted + int interz = interpolate_height(f, p, ringi, previ, ringi, endCorner.first); + int prevz = f->get_vertex_elevation(ringi, previ); + //Allways stich to lower object or if interpolated between corners within threshold or previous within threshold + if (stitchz < interz || abs(stitchz - interz) < _threshold_jump_edges || abs(stitchz - prevz) < _threshold_jump_edges) { + f->set_vertex_elevation(ringi, pi, stitchz); + } + else { + f->set_vertex_elevation(ringi, pi, interz); + // Add height to NC and add VW + _nc[key_bucket].push_back(interz); + f->add_vertical_wall(); } } } @@ -1625,6 +1603,241 @@ void Map3d::stitch_bridges() { } } +//void Map3d::stitch_bridges() { +// std::vector ringis, pis; +// for (auto& f : _lsFeatures) { +// if (f->get_class() == BRIDGE && f->get_top_level() == false) { +// //-- 1. store all touching top level (adjacent + incident) +// std::vector* lstouching = f->get_adjacent_features(); +// +// //-- gather all rings +// std::vector rings; +// rings.push_back(f->get_Polygon2()->outer()); +// for (Ring2& iring : f->get_Polygon2()->inners()) +// rings.push_back(iring); +// +// int ringi = -1; +// for (Ring2& ring : rings) { +// ringi++; +// +// for (int i = 0; i < ring.size(); i++) { +// for (auto& fadj : *lstouching) { +// ringis.clear(); +// pis.clear(); +// if (!(fadj->get_class() == BRIDGE && fadj->get_top_level()) && fadj->has_point2(ring[i], ringis, pis)) { +// int z = fadj->get_vertex_elevation(ringis[0], pis[0]); +// if (abs(f->get_vertex_elevation(ringi, i)) - z < _threshold_jump_edges) { +// f->set_vertex_elevation(ringi, i, z); +// // Add height to NC +// Point2 p = f->get_point2(ringi, i); +// std::string key_bucket = gen_key_bucket(&p); +// _nc[key_bucket].push_back(z); +// _bridge_stitches[key_bucket] = z; +// } +// } +// } +// } +// } +// Bridge* b = dynamic_cast(f); +// b->detect_outliers(_road_threshold_outliers); +// } +// } +// +// for (auto& f : _lsFeatures) { +// if (f->get_class() == BRIDGE && f->get_top_level()) { +// //if (f->get_id() == "O0000.5cbe3212cc834f47ba6dfbc4dc83f71c") { +// // std::cout << "stop" << std::endl; +// //} +// //-- 1. store all touching top level (adjacent + incident) +// std::vector* lstouching = f->get_adjacent_features(); +// +// //-- gather all rings +// std::vector rings; +// rings.push_back(f->get_Polygon2()->outer()); +// for (Ring2& iring : f->get_Polygon2()->inners()) +// rings.push_back(iring); +// +// int ringi = -1; +// for (Ring2& ring : rings) { +// ringi++; +// +// std::vector< std::pair > corners; +// for (int i = 0; i < ring.size(); i++) { +// Point2 p = f->get_point2(ringi, i); +// std::string key_bucket = gen_key_bucket(&p); +// std::vector nc; +// int unique = 0; +// if (_nc.find(key_bucket) != _nc.end()) { +// nc = _nc[key_bucket]; +// std::sort(nc.begin(), nc.end()); +// auto ncuIt = std::unique(nc.begin(), nc.end()); +// unique = std::distance(nc.begin(), ncuIt); +// } +// if (unique > 0) { +// bool bridgeAdj = false; +// +// /* this can be 3 cases; +// 1. location where two bridges and other object meet without height jump. +// 2. location where one bridge meets multiple objects which should be a stitching error +// 3. error in stitching adjacent objects +// */ +// if (unique <= 1) { // only check for bridge adjacent if there is only 1 unique value in NC for performance +// for (auto& fadj : *lstouching) { +// ringis.clear(); +// pis.clear(); +// if (fadj->get_class() == BRIDGE && fadj->get_top_level() && fadj->has_point2(ring[i], ringis, pis)) { +// bridgeAdj = true; +// } +// } +// } +// +// int previ = i - 1; +// if (i == 0) { +// previ = ring.size() - 1; +// } +// Point2 prevp = f->get_point2(ringi, previ); +// std::string prev_key_bucket = gen_key_bucket(&prevp); +// int nexti = i + 1; +// if (i == ring.size() - 1) { +// nexti = 0; +// } +// Point2 nextp = f->get_point2(ringi, nexti); +// std::string next_key_bucket = gen_key_bucket(&nextp); +// if ((_bridge_stitches.find(key_bucket) != _bridge_stitches.end() && +// _bridge_stitches.find(next_key_bucket) == _bridge_stitches.end()) || +// (_bridge_stitches.find(key_bucket) != _bridge_stitches.end() && +// _bridge_stitches.find(prev_key_bucket) == _bridge_stitches.end()) || +// +// +// /* Two cases which are seen as corners of the bridge object, either a height jump or where two bridge objects meet +// 1. if bridge adjacent, use nc. This is the case where a bridge is split into multiple parts touching the adjacent object +// 2. if 2 or more unique values in NC there is a height jump which should only occur at vw of bridge, skip values upto next occurence +// */ +// bridgeAdj || unique > 1) { +// //This only works if previous is updated before checking, that is not the case now +// //auto const it = std::lower_bound(nc.begin(), nc.end(), f->get_vertex_elevation(ringi, i-1)); +// f->set_vertex_elevation(ringi, i, nc.front()); +// // if there are more then 1 unique height in NC we have a height jump. +// bool heightjump = false; +// if (_bridge_stitches.find(next_key_bucket) != _bridge_stitches.end()) { +// heightjump = true; +// } +// corners.push_back(std::make_pair(i, heightjump)); +// } +// } +// } +// +// // Set height of vertices in between two corners +// for (int c = 0; c < corners.size(); c++) { +// // prepair vertices to loop for this stretch +// std::pair startCorner = corners[c]; +// // set endCorner to first or corners if end of array is reached +// std::pair endCorner = corners.front(); +// if (c + 1 < corners.size()) { +// endCorner = corners[c + 1]; +// } +// int endCornerIdx = endCorner.first; +// +// std::vector vertices; +// if (endCornerIdx < startCorner.first) { +// for (int i = startCorner.first + 1; i < ring.size(); i++) { +// vertices.push_back(i); +// } +// for (int i = 0; i < endCornerIdx; i++) { +// vertices.push_back(i); +// } +// } +// else { +// for (int i = startCorner.first + 1; i < endCornerIdx; i++) { +// vertices.push_back(i); +// } +// } +// +// for (int pi : vertices) { +// //if (f->get_id() == "O0000.59d49cd362f54875b4a3196c1363606f" && pi == 18) { +// // std::cout << "stop" << std::endl; +// //} +// bool otherAdj = false; +// int stitchz = 0; +// for (auto& fadj : *lstouching) { +// ringis.clear(); +// pis.clear(); +// if (fadj->get_class() != BRIDGE && fadj->has_point2(ring[pi], ringis, pis) == true) { +// otherAdj = true; +// stitchz = fadj->get_vertex_elevation(ringis[0], pis[0]); +// } +// } +// if (!otherAdj) { //No other type of objects then bridges are adjacent +// //This is empty stretch of points where only bridges connect, interpolate between prevCorner and nextCorner +// Point2 p = f->get_point2(ringi, pi); +// std::string key_bucket = gen_key_bucket(&p); +// int z = 0; +// //Check if height exists in NC thus is created by adjacent bridge +// if (_nc.find(key_bucket) == _nc.end()) { +// // check height of previous vertex +// int previ = pi - 1; +// if (pi == 0) { +// previ = ring.size() - 1; +// } +// // Add height to NC for adjacent bridge +// // interpolate height between previous vertex and next corner distance weighted +// z = interpolate_height(f, p, ringi, previ, ringi, endCorner.first); +// _nc[key_bucket].push_back(z); +// } +// else { +// z = _nc[key_bucket].front(); +// } +// f->set_vertex_elevation(ringi, pi, z); +// } +// else { +// Point2 p = f->get_point2(ringi, pi); +// std::string key_bucket = gen_key_bucket(&p); +// +// // This is where the top part is stitched, interpolate between corners +// if (startCorner.second && endCorner.second == false) { +// // interpolate height between start and end corner distance weighted +// int interz = interpolate_height(f, p, ringi, startCorner.first, ringi, endCorner.first); +// +// // Allways stitch if there is a lower object, otherwise use interpolated height +// if (stitchz < interz) { +// f->set_vertex_elevation(ringi, pi, stitchz); +// } +// else { +// f->set_vertex_elevation(ringi, pi, interz); +// // Add height to NC and add VW +// _nc[key_bucket].push_back(interz); +// f->add_vertical_wall(); +// } +// } +// else { // when there is no value in _bridge_stitches the upper part is floating thus stitch bottem to adjacent object if within threshold of previous vertex or corner +// // check height of previous vertex +// int previ = pi - 1; +// if (pi == 0) { +// previ = ring.size() - 1; +// } +// +// // interpolate height between previous vertex and next corner distance weighted +// int interz = interpolate_height(f, p, ringi, previ, ringi, endCorner.first); +// int prevz = f->get_vertex_elevation(ringi, previ); +// //Allways stich to lower object or if interpolated between corners within threshold or previous within threshold +// if (stitchz < interz || abs(stitchz - interz) < _threshold_jump_edges || abs(stitchz - prevz) < _threshold_jump_edges) { +// f->set_vertex_elevation(ringi, pi, stitchz); +// } +// else { +// f->set_vertex_elevation(ringi, pi, interz); +// // Add height to NC and add VW +// _nc[key_bucket].push_back(interz); +// f->add_vertical_wall(); +// } +// } +// } +// } +// } +// } +// } +// } +//} + int Map3d::interpolate_height(TopoFeature* f, const Point2 &p, int prevringi, int prevpi, int nextringi, int nextpi) { // interpolate height between previous and next corner distance weighted double dprev = distance(p, f->get_point2(prevringi, prevpi)); diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index 7feae7ef..e527ec3f 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -1172,55 +1172,57 @@ void Boundary3D::detect_outliers(int degrees_incline) { // else find the index of the first peak down (value == -1) start_idx = std::find(changez.begin(), changez.end(), -1) - changez.begin(); if (start_idx == changez.size()) { - start_idx = 0; + start_idx = ring.size(); } } - // Detect and fix outliers starting from the first peak up detected previously - for (int j = 0; j < ring.size(); ++j) { - //make round-trip through vector starting at start_idx - int i = (j + start_idx + ring.size()) % ring.size(); - int i0 = i - 1; - int i2 = i + 1; - if (i == 0) { - i0 = ring.size() - 1; - } - if (i2 == ring.size()) { - i2 = 0; - } - float len1z = (ringz[i] - ringz[i0]) / 100.0; - float len2z = (ringz[i2] - ringz[i]) / 100.0; - float incline = atan2(len2z, distance(ring[i], ring[i2])) - atan2(len1z, distance(ring[i0], ring[i])); - if (incline <= -PI) { - incline = 2 * PI + incline; - } - if (incline > PI) { - incline = incline - 2 * PI; - } - incline = incline * 180 / PI; - //if (incline > 0) we have a peak down, otherwise we have a peak up - if (abs(incline) > degrees_incline) { - //find the outlier by sorting and comparing distance - std::vector heights = { ringz[i0], ringz[i], ringz[i2] }; - std::sort(heights.begin(), heights.end()); - int h = heights[0]; - if (abs(heights[2] - heights[1]) > abs(heights[0] - heights[1])) { - h = heights[2]; + if (start_idx != ring.size()) { + // Detect and fix outliers starting from the first peak up detected previously + for (int j = 0; j < ring.size(); ++j) { + //make round-trip through vector starting at start_idx + int i = (j + start_idx + ring.size()) % ring.size(); + int i0 = i - 1; + int i2 = i + 1; + if (i == 0) { + i0 = ring.size() - 1; } - - if (ringz[i0] == h) { - //put to height of closest vertex for now - _p2z[ringi][i0] = ringz[i]; - ringz[i0] = ringz[i]; + if (i2 == ring.size()) { + i2 = 0; } - else if (ringz[i] == h) { - _p2z[ringi][i] = (ringz[i0] + ringz[i2]) / 2; - ringz[i] = (ringz[i0] + ringz[i2]) / 2; + float len1z = (ringz[i] - ringz[i0]) / 100.0; + float len2z = (ringz[i2] - ringz[i]) / 100.0; + float incline = atan2(len2z, distance(ring[i], ring[i2])) - atan2(len1z, distance(ring[i0], ring[i])); + if (incline <= -PI) { + incline = 2 * PI + incline; } - else if (ringz[i2] == h) { - //put to height of closest vertex for now - _p2z[ringi][i2] = ringz[i]; - ringz[i2] = ringz[i]; + if (incline > PI) { + incline = incline - 2 * PI; + } + incline = incline * 180 / PI; + //if (incline > 0) we have a peak down, otherwise we have a peak up + if (abs(incline) > degrees_incline) { + //find the outlier by sorting and comparing distance + std::vector heights = { ringz[i0], ringz[i], ringz[i2] }; + std::sort(heights.begin(), heights.end()); + int h = heights[0]; + if (abs(heights[2] - heights[1]) > abs(heights[0] - heights[1])) { + h = heights[2]; + } + + if (ringz[i0] == h) { + //put to height of closest vertex for now + _p2z[ringi][i0] = ringz[i]; + ringz[i0] = ringz[i]; + } + else if (ringz[i] == h) { + _p2z[ringi][i] = (ringz[i0] + ringz[i2]) / 2; + ringz[i] = (ringz[i0] + ringz[i2]) / 2; + } + else if (ringz[i2] == h) { + //put to height of closest vertex for now + _p2z[ringi][i2] = ringz[i]; + ringz[i2] = ringz[i]; + } } } } From 64dea2add04fbfea51519188121bcdcd6ba3932d Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Wed, 1 Aug 2018 16:48:57 +0200 Subject: [PATCH 28/45] add polynomial fitting for 2d and 3d data in outlier detection --- src/TopoFeature.cpp | 316 ++++++++++++++++++++++++++++------------ src/definitions.h | 3 + thirdparty/givensQR.hpp | 154 ++++++++++++++++++++ thirdparty/matrix.hpp | 131 +++++++++++++++++ thirdparty/polyfit.hpp | 291 ++++++++++++++++++++++++++++++++++++ 5 files changed, 798 insertions(+), 97 deletions(-) create mode 100644 thirdparty/givensQR.hpp create mode 100644 thirdparty/matrix.hpp create mode 100644 thirdparty/polyfit.hpp diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index e527ec3f..07b8f5b4 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -28,6 +28,7 @@ #include "TopoFeature.h" #include "io.h" +#include "polyfit.hpp" TopoFeature::TopoFeature(char *wkt, std::string layername, AttributeMap attributes, std::string pid) { _id = pid; @@ -1075,20 +1076,21 @@ int Flat::get_height() { } bool Flat::lift_percentile(float percentile) { - int z = -9999; - if (_zvaluesinside.empty() == false) { - std::nth_element(_zvaluesinside.begin(), _zvaluesinside.begin() + (_zvaluesinside.size() * percentile), _zvaluesinside.end()); - z = _zvaluesinside[_zvaluesinside.size() * percentile]; - } - this->lift_all_boundary_vertices_same_height(z); - return true; +int z = -9999; +if (_zvaluesinside.empty() == false) { + std::nth_element(_zvaluesinside.begin(), _zvaluesinside.begin() + (_zvaluesinside.size() * percentile), _zvaluesinside.end()); + z = _zvaluesinside[_zvaluesinside.size() * percentile]; +} +this->lift_all_boundary_vertices_same_height(z); +return true; } //------------------------------- //------------------------------- Boundary3D::Boundary3D(char *wkt, std::string layername, AttributeMap attributes, std::string pid) - : TopoFeature(wkt, layername, attributes, pid) {} + : TopoFeature(wkt, layername, attributes, pid) { +} int Boundary3D::get_number_vertices() { return (int(_vertices.size()) + int(_vertices_vw.size())); @@ -1118,7 +1120,6 @@ void Boundary3D::smooth_boundary(int passes) { } void Boundary3D::detect_outliers(int degrees_incline) { - // find spikes in roads (due to misclassified lidar points) and fix by averaging between previous and next vertex. //-- gather all rings std::vector rings; rings.push_back(_p2->outer()); @@ -1128,105 +1129,226 @@ void Boundary3D::detect_outliers(int degrees_incline) { int ringi = -1; for (Ring2& ring : rings) { ringi++; - std::vector ringz = _p2z[ringi]; - std::vector inclines(_p2z[ringi].size()); - std::vector changez(_p2z[ringi].size(), 0); - float PI = 3.14159265; - for (int i = 0; i < ring.size(); i++) { - int i0 = i - 1; - int i2 = i + 1; - if (i == 0) { - i0 = ring.size() - 1; - } - if (i2 == ring.size()) { - i2 = 0; - } - float len1z = (ringz[i] - ringz[i0]) / 100.0; - float len2z = (ringz[i2] - ringz[i]) / 100.0; - float incline = atan2(len2z, distance(ring[i], ring[i2])) - atan2(len1z, distance(ring[i0], ring[i])); - if (incline <= -PI) { - incline = 2 * PI + incline; - } - if (incline > PI) { - incline = incline - 2 * PI; + // find spikes in roads (due to misclassified lidar points) and fix by averaging between previous and next vertex. + std::vector x, y, coeffs, x1, y1, z1; + for (int i = 0; i < _p2z[ringi].size(); i++) { + x.push_back(i); + y.push_back(_p2z[ringi][i]); + } + + std::vector xtmp = x, ytmp = y; + + int niter = _p2z[ringi].size() - 3; + std::vector indices; + std::vector residualRanges; + std::vector gaps; + std::vector < std::vector > residualCoeffs; + for (int i = 0; i < niter; i++) { + // Fit the model + std::vector coeffs = polyfitqr(xtmp, ytmp, 2); + std::vector correctedvalues = polyvalqr(coeffs, xtmp); + std::vector residuals; + + for (int j = 0; j < correctedvalues.size(); j++) { + residuals.push_back(ytmp[j] - correctedvalues[j]); } - incline = incline * 180 / PI; - //if (incline > 0) we have a peak down, otherwise we have a peak up - if (abs(incline) > degrees_incline) { - if (abs(len2z) > abs(len1z)) { - if (incline > 0) { - // going from 'flat' surface up - changez[i] = 1; - } - else { - // going from 'flat' surface down - changez[i] = -1; - } + + if (_id == "L0004.3232f41ea7fb409aa7b64ba27318f4dd") { + std::cout << ""; + // std::cout << "coeffs:" << std::endl; + // for (int i = 0; i < 3; i++) { + // std::cout << coeffs[i] << std::endl; + // } + // std::cout << std::endl; + + std::cout << "x-y-z values" << std::endl; + std::cout << std::fixed << std::setprecision(3); + for (int i = 0; i < ring.size(); i++) { + //std::cout << ring[i].x() << "\t" << ring[i].y() << "\t" << _p2z[ringi][i] << std::endl; + x1.push_back(ring[i].x()); + y1.push_back(ring[i].y()); + z1.push_back(_p2z[ringi][i]); } + std::vector res; + std::vector coeffs = polyfit3d(x1, y1, z1, 2, res); + std::cout << std::endl; + + // std::cout << "fitted values" << std::endl; + // for (int i = 0; i < correctedvalues.size(); i++) { + // std::cout << correctedvalues[i] << std::endl; + // } + // std::cout << std::endl; + + // std::cout << "heights\tvalues\tfitted values\tresiduals" << std::endl; + // for (int i = 0; i < correctedvalues.size(); i++) { + // std::cout << y[i] << "\t" << correctedvalues[i] << "\t\t" << residuals[i] << std::endl; + // } + // std::cout << std::endl; } - } - // find the index of the first peak up (value == 1) - int start_idx = std::find(changez.begin(), changez.end(), 1) - changez.begin(); - if (start_idx == changez.size()) { - // else find the index of the first peak down (value == -1) - start_idx = std::find(changez.begin(), changez.end(), -1) - changez.begin(); - if (start_idx == changez.size()) { - start_idx = ring.size(); + + // Calculate the maximum residual + auto min = std::min_element(residuals.begin(), residuals.end(), abs_compare); + auto max = std::max_element(residuals.begin(), residuals.end(), abs_compare); + double res_range = abs(*min) + abs(*max); + int imax = max - residuals.begin(); + double vtx = xtmp[imax]; + //double height = y[vtx]; + //double residual = *max; + + // print and remove the maximum residual + //std::cout << "vertex: " << vtx << ", abs res: " << res_range << std::endl; + + // store the index of the vertex marked as an outlier + indices.push_back(vtx); + // store size of the gap with the previous residual, do before storing residual_range + if (!residualRanges.empty()) { + gaps.push_back(abs(res_range - residualRanges.back())); + } + // store the residual range of the outlier + residualRanges.push_back(res_range); + // store the coefficients for later use + residualCoeffs.push_back(coeffs); + + // remove the outlier form both x_ite and y_ite + xtmp.erase(xtmp.begin() + imax); + ytmp.erase(ytmp.begin() + imax); + + // stop if residuals are <1 (mm precision) + if (res_range < 1) { + break; } } - if (start_idx != ring.size()) { - // Detect and fix outliers starting from the first peak up detected previously - for (int j = 0; j < ring.size(); ++j) { - //make round-trip through vector starting at start_idx - int i = (j + start_idx + ring.size()) % ring.size(); - int i0 = i - 1; - int i2 = i + 1; - if (i == 0) { - i0 = ring.size() - 1; - } - if (i2 == ring.size()) { - i2 = 0; - } - float len1z = (ringz[i] - ringz[i0]) / 100.0; - float len2z = (ringz[i2] - ringz[i]) / 100.0; - float incline = atan2(len2z, distance(ring[i], ring[i2])) - atan2(len1z, distance(ring[i0], ring[i])); - if (incline <= -PI) { - incline = 2 * PI + incline; - } - if (incline > PI) { - incline = incline - 2 * PI; - } - incline = incline * 180 / PI; - //if (incline > 0) we have a peak down, otherwise we have a peak up - if (abs(incline) > degrees_incline) { - //find the outlier by sorting and comparing distance - std::vector heights = { ringz[i0], ringz[i], ringz[i2] }; - std::sort(heights.begin(), heights.end()); - int h = heights[0]; - if (abs(heights[2] - heights[1]) > abs(heights[0] - heights[1])) { - h = heights[2]; - } + // find the largest gap + auto maxgap = std::max_element(gaps.begin(), gaps.end()); + // skip if gaps is empty + if (maxgap != gaps.end()) { + int maxgapi = maxgap - gaps.begin(); - if (ringz[i0] == h) { - //put to height of closest vertex for now - _p2z[ringi][i0] = ringz[i]; - ringz[i0] = ringz[i]; - } - else if (ringz[i] == h) { - _p2z[ringi][i] = (ringz[i0] + ringz[i2]) / 2; - ringz[i] = (ringz[i0] + ringz[i2]) / 2; - } - else if (ringz[i2] == h) { - //put to height of closest vertex for now - _p2z[ringi][i2] = ringz[i]; - ringz[i2] = ringz[i]; - } - } + //std::cout << "max gap i: " << maxgapi << ", max gap: " << *maxgap << std::endl; + + // get the new values based on the coeffs of the equation at max abs residual +1 + std::vector correctedvalues = polyvalqr(residualCoeffs[maxgapi+1], x); + + // replace the old values with the new values upto the maximum gap (not including since gap is this to next) + for (int i = 0; i <= maxgapi; i++) { + int idx = indices[i]; + //std::cout << "old: " << y[idx] << ", new: " << correctedvalues[idx] << std::endl; + _p2z[ringi][idx] = correctedvalues[idx]; } } } + + + ////-- gather all rings + //std::vector rings; + //rings.push_back(_p2->outer()); + //for (Ring2& iring : _p2->inners()) + // rings.push_back(iring); + + //int ringi = -1; + //for (Ring2& ring : rings) { + // ringi++; + // std::vector ringz = _p2z[ringi]; + // std::vector inclines(_p2z[ringi].size()); + // std::vector changez(_p2z[ringi].size(), 0); + // float PI = 3.14159265; + + // for (int i = 0; i < ring.size(); i++) { + // int i0 = i - 1; + // int i2 = i + 1; + // if (i == 0) { + // i0 = ring.size() - 1; + // } + // if (i2 == ring.size()) { + // i2 = 0; + // } + // float len1z = (ringz[i] - ringz[i0]) / 100.0; + // float len2z = (ringz[i2] - ringz[i]) / 100.0; + // float incline = atan2(len2z, distance(ring[i], ring[i2])) - atan2(len1z, distance(ring[i0], ring[i])); + // if (incline <= -PI) { + // incline = 2 * PI + incline; + // } + // if (incline > PI) { + // incline = incline - 2 * PI; + // } + // incline = incline * 180 / PI; + // //if (incline > 0) we have a peak down, otherwise we have a peak up + // if (abs(incline) > degrees_incline) { + // if (abs(len2z) > abs(len1z)) { + // if (incline > 0) { + // // going from 'flat' surface up + // changez[i] = 1; + // } + // else { + // // going from 'flat' surface down + // changez[i] = -1; + // } + // } + // } + // } + // // find the index of the first peak up (value == 1) + // int start_idx = std::find(changez.begin(), changez.end(), 1) - changez.begin(); + // if (start_idx == changez.size()) { + // // else find the index of the first peak down (value == -1) + // start_idx = std::find(changez.begin(), changez.end(), -1) - changez.begin(); + // if (start_idx == changez.size()) { + // start_idx = ring.size(); + // } + // } + + // if (start_idx != ring.size()) { + // // Detect and fix outliers starting from the first peak up detected previously + // for (int j = 0; j < ring.size(); ++j) { + // //make round-trip through vector starting at start_idx + // int i = (j + start_idx + ring.size()) % ring.size(); + // int i0 = i - 1; + // int i2 = i + 1; + // if (i == 0) { + // i0 = ring.size() - 1; + // } + // if (i2 == ring.size()) { + // i2 = 0; + // } + // float len1z = (ringz[i] - ringz[i0]) / 100.0; + // float len2z = (ringz[i2] - ringz[i]) / 100.0; + // float incline = atan2(len2z, distance(ring[i], ring[i2])) - atan2(len1z, distance(ring[i0], ring[i])); + // if (incline <= -PI) { + // incline = 2 * PI + incline; + // } + // if (incline > PI) { + // incline = incline - 2 * PI; + // } + // incline = incline * 180 / PI; + // //if (incline > 0) we have a peak down, otherwise we have a peak up + // if (abs(incline) > degrees_incline) { + // //find the outlier by sorting and comparing distance + // std::vector heights = { ringz[i0], ringz[i], ringz[i2] }; + // std::sort(heights.begin(), heights.end()); + // int h = heights[0]; + // if (abs(heights[2] - heights[1]) > abs(heights[0] - heights[1])) { + // h = heights[2]; + // } + + // if (ringz[i0] == h) { + // //put to height of closest vertex for now + // _p2z[ringi][i0] = ringz[i]; + // ringz[i0] = ringz[i]; + // } + // else if (ringz[i] == h) { + // _p2z[ringi][i] = (ringz[i0] + ringz[i2]) / 2; + // ringz[i] = (ringz[i0] + ringz[i2]) / 2; + // } + // else if (ringz[i2] == h) { + // //put to height of closest vertex for now + // _p2z[ringi][i2] = ringz[i]; + // ringz[i2] = ringz[i]; + // } + // } + // } + // } + //} } //------------------------------- diff --git a/src/definitions.h b/src/definitions.h index dba49112..a918c3b1 100644 --- a/src/definitions.h +++ b/src/definitions.h @@ -76,4 +76,7 @@ typedef enum { NUM_ALLOWEDLASTOPO = 8 } AllowedLASTopo; +static bool abs_compare(double a, double b) { + return (std::abs(a) < std::abs(b)); +} #endif diff --git a/thirdparty/givensQR.hpp b/thirdparty/givensQR.hpp new file mode 100644 index 00000000..0f8b4a27 --- /dev/null +++ b/thirdparty/givensQR.hpp @@ -0,0 +1,154 @@ +#pragma once +#include "matrix.hpp" +#include +#include +#include + +namespace mathalgo +{ + using namespace std; + template + class Givens + { + public: + Givens() : m_oJ(2,2), m_oQ(1,1), m_oR(1,1) + { + } + + /* + Calculate the inverse of a matrix using the QR decomposition. + + param: + A matrix to inverse + */ + const matrix Inverse( matrix& oMatrix ) + { + if ( oMatrix.cols() != oMatrix.rows() ) + { + throw domain_error( "matrix has to be square" ); + } + matrix oIdentity = matrix::identity( oMatrix.rows() ); + Decompose( oMatrix ); + return Solve( oIdentity ); + } + + /* + Performs QR factorization using Givens rotations. + */ + void Decompose( matrix& oMatrix ) + { + int nRows = oMatrix.rows(); + int nCols = oMatrix.cols(); + + + if ( nRows == nCols ) + { + nCols--; + } + else if ( nRows < nCols ) + { + nCols = nRows - 1; + } + + m_oQ = matrix::identity(nRows); + m_oR = oMatrix; + + for ( int j = 0; j < nCols; j++ ) + { + for ( int i = j + 1; i < nRows; i++ ) + { + GivensRotation( m_oR(j,j), m_oR(i,j) ); + PreMultiplyGivens( m_oR, j, i ); + PreMultiplyGivens( m_oQ, j, i ); + } + } + + m_oQ = m_oQ.transpose(); + } + + /* + Find the solution for a matrix. + http://en.wikipedia.org/wiki/QR_decomposition#Using_for_solution_to_linear_inverse_problems + */ + matrix Solve( matrix& oMatrix ) + { + matrix oQtM( m_oQ.transpose() * oMatrix ); + int nCols = m_oR.cols(); + matrix oS( 1, nCols ); + for (int i = nCols-1; i >= 0; i-- ) + { + oS(0,i) = oQtM(i, 0); + for ( int j = i + 1; j < nCols; j++ ) + { + oS(0,i) -= oS(0,j) * m_oR(i, j); + } + oS(0,i) /= m_oR(i, i); + } + + return oS; + } + + const matrix& GetQ() + { + return m_oQ; + } + + const matrix& GetR() + { + return m_oR; + } + + private: + /* + Givens rotation is a rotation in the plane spanned by two coordinates axes. + http://en.wikipedia.org/wiki/Givens_rotation + */ + void GivensRotation( T a, T b ) + { + T t,s,c; + if (b == 0) + { + c = (a >=0)?1:-1; + s = 0; + } + else if (a == 0) + { + c = 0; + s = (b >=0)?-1:1; + } + else if (abs(b) > abs(a)) + { + t = a/b; + s = -1/sqrt(1+t*t); + c = -s*t; + } + else + { + t = b/a; + c = 1/sqrt(1+t*t); + s = -c*t; + } + m_oJ(0,0) = c; m_oJ(0,1) = -s; + m_oJ(1,0) = s; m_oJ(1,1) = c; + } + + /* + Get the premultiplication of a given matrix + by the Givens rotation. + */ + void PreMultiplyGivens( matrix& oMatrix, int i, int j ) + { + int nRowSize = oMatrix.cols(); + + for ( int nRow = 0; nRow < nRowSize; nRow++ ) + { + double nTemp = oMatrix(i,nRow) * m_oJ(0,0) + oMatrix(j,nRow) * m_oJ(0,1); + oMatrix(j,nRow) = oMatrix(i,nRow) * m_oJ(1,0) + oMatrix(j,nRow) * m_oJ(1,1); + oMatrix(i,nRow) = nTemp; + } + } + + private: + matrix m_oQ, m_oR, m_oJ; + }; +} \ No newline at end of file diff --git a/thirdparty/matrix.hpp b/thirdparty/matrix.hpp new file mode 100644 index 00000000..4e851bc0 --- /dev/null +++ b/thirdparty/matrix.hpp @@ -0,0 +1,131 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace mathalgo +{ + using namespace std; + + template + class matrix + { + public: + matrix(unsigned int nRows, unsigned int nCols) : + m_nRows( nRows ), + m_nCols( nCols ), + m_oData( nRows*nCols, 0 ) + { + if ( !nRows || !nCols ) + { + throw range_error( "invalid matrix size" ); + } + } + + static matrix identity( unsigned int nSize ) + { + matrix oResult( nSize, nSize ); + + int nCount = 0; + std::generate( oResult.m_oData.begin(), oResult.m_oData.end(), + [&nCount, nSize]() { return !(nCount++%(nSize + 1)); } ); + + return oResult; + } + + inline T& operator()(unsigned int nRow, unsigned int nCol) + { + if ( nRow >= m_nRows || nCol >= m_nCols ) + { + std::string msg = "position out of range, nRow: " + std::to_string(nRow) + " & nCol: " + std::to_string(nCol); + throw out_of_range( msg.c_str() ); + } + + return m_oData[nCol+m_nCols*nRow]; + } + + inline matrix operator*(matrix& other) + { + if ( m_nCols != other.m_nRows ) + { + throw domain_error( "matrix dimensions are not multiplicable" ); + } + + matrix oResult( m_nRows, other.m_nCols ); + for ( unsigned int r = 0; r < m_nRows; ++r ) + { + for ( unsigned int ocol = 0; ocol < other.m_nCols; ++ocol ) + { + for ( unsigned int c = 0; c < m_nCols; ++c ) + { + oResult(r,ocol) += (*this)(r,c) * other(c,ocol); + } + } + } + + return oResult; + } + + inline matrix transpose() + { + matrix oResult( m_nCols, m_nRows ); + for ( unsigned int r = 0; r < m_nRows; ++r ) + { + for ( unsigned int c = 0; c < m_nCols; ++c ) + { + oResult(c,r) += (*this)(r,c); + } + } + return oResult; + } + + inline matrix row(unsigned int nRow) { + if (nRow >= m_nRows) { + std::string msg = "position out of range, nRow: " + std::to_string(nRow); + throw out_of_range(msg.c_str()); + } + + matrix oResult(1, m_nCols); + for (unsigned int c = 0; c < m_nCols; ++c) { + oResult(0, c) = (*this)(nRow, c); + } + + return oResult; + } + + inline unsigned int rows() + { + return m_nRows; + } + + inline unsigned int cols() + { + return m_nCols; + } + + inline vector data() + { + return m_oData; + } + + void print() + { + for ( unsigned int r = 0; r < m_nRows; r++ ) + { + for ( unsigned int c = 0; c < m_nCols; c++ ) + { + std::cout << (*this)(r,c) << "\t"; + } + std::cout << std::endl; + } + } + + private: + std::vector m_oData; + + unsigned int m_nRows; + unsigned int m_nCols; + }; +}; \ No newline at end of file diff --git a/thirdparty/polyfit.hpp b/thirdparty/polyfit.hpp new file mode 100644 index 00000000..ed76a5ff --- /dev/null +++ b/thirdparty/polyfit.hpp @@ -0,0 +1,291 @@ +#pragma once + +#ifdef BOOST_UBLAS_TYPE_CHECK +# undef BOOST_UBLAS_TYPE_CHECK +#endif +#define BOOST_UBLAS_TYPE_CHECK 0 +#ifndef _USE_MATH_DEFINES +# define _USE_MATH_DEFINES +#endif + +#include +#include +#include +#include + +/* + Finds the coefficients of a polynomial p(x) of degree n that fits the data, + p(x(i)) to y(i), in a least squares sense. The result p is a row vector of + length n+1 containing the polynomial coefficients in incremental powers. + + param: + oX x axis values + oY y axis values + nDegree polynomial degree including the constant + + return: + coefficients of a polynomial starting at the constant coefficient and + ending with the coefficient of power to nDegree. C++0x-compatible + compilers make returning locally created vectors very efficient. + +*/ +template +std::vector polyfit( const std::vector& oX, const std::vector& oY, int nDegree ) +{ + using namespace boost::numeric::ublas; + + if ( oX.size() != oY.size() ) + throw std::invalid_argument( "X and Y vector sizes do not match" ); + + // more intuative this way + nDegree++; + + size_t nCount = oX.size(); + matrix oXMatrix( nCount, nDegree ); + matrix oYMatrix( nCount, 1 ); + + // copy y matrix + for ( size_t i = 0; i < nCount; i++ ) + { + oYMatrix(i, 0) = oY[i]; + } + + // create the X matrix + for ( size_t nRow = 0; nRow < nCount; nRow++ ) + { + T nVal = 1.0f; + for ( int nCol = 0; nCol < nDegree; nCol++ ) + { + oXMatrix(nRow, nCol) = nVal; + nVal *= oX[nRow]; + } + } + + // transpose X matrix + matrix oXtMatrix( trans(oXMatrix) ); + // multiply transposed X matrix with X matrix + matrix oXtXMatrix( prec_prod(oXtMatrix, oXMatrix) ); + // multiply transposed X matrix with Y matrix + matrix oXtYMatrix( prec_prod(oXtMatrix, oYMatrix) ); + + // lu decomposition + permutation_matrix pert(oXtXMatrix.size1()); + const std::size_t singular = lu_factorize(oXtXMatrix, pert); + // must be singular + BOOST_ASSERT( singular == 0 ); + + // backsubstitution + lu_substitute(oXtXMatrix, pert, oXtYMatrix); + + // copy the result to coeff + return std::vector( oXtYMatrix.data().begin(), oXtYMatrix.data().end() ); +} + +/* + Calculates the value of a polynomial of degree n evaluated at x. The input + argument pCoeff is a vector of length n+1 whose elements are the coefficients + in incremental powers of the polynomial to be evaluated. + + param: + oCoeff polynomial coefficients generated by polyfit() function + oX x axis values + + return: + Fitted Y values. C++0x-compatible compilers make returning locally + created vectors very efficient. +*/ +template +std::vector polyval( const std::vector& oCoeff, const std::vector& oX ) +{ + size_t nCount = oX.size(); + size_t nDegree = oCoeff.size(); + std::vector oY( nCount ); + + for ( size_t i = 0; i < nCount; i++ ) + { + T nY = 0; + T nXT = 1; + T nX = oX[i]; + for ( size_t j = 0; j < nDegree; j++ ) + { + // multiply current x by a coefficient + nY += oCoeff[j] * nXT; + // power up the X + nXT *= nX; + } + oY[i] = nY; + } + + return oY; +} + +#include "matrix.hpp" +#include "GivensQR.hpp" +/* +Finds the coefficients of a polynomial p(x) of degree n that fits the data, +p(x(i)) to y(i), in a least squares sense. The result p is a row vector of +length n+1 containing the polynomial coefficients in incremental powers. + +param: +oX x axis values +oY y axis values +nDegree polynomial degree including the constant + +return: +coefficients of a polynomial starting at the constant coefficient and +ending with the coefficient of power to nDegree. C++0x-compatible +compilers make returning locally created vectors very efficient. + +*/ +template +std::vector polyfitqr(const std::vector& oX, const std::vector& oY, int nDegree) { + if (oX.size() != oY.size()) + throw std::invalid_argument("X and Y vector sizes do not match"); + + // more intuative this way + nDegree++; + + size_t nCount = oX.size(); + mathalgo::matrix oXMatrix(nCount, nDegree); + mathalgo::matrix oYMatrix(nCount, 1); + + // copy y matrix + for (size_t i = 0; i < nCount; i++) { + oYMatrix(i, 0) = oY[i]; + } + + // create the X matrix + for (size_t nRow = 0; nRow < nCount; nRow++) { + T nVal = 1.0f; + for (int nCol = 0; nCol < nDegree; nCol++) { + oXMatrix(nRow, nCol) = nVal; + nVal *= oX[nRow]; + } + } + + // transpose X matrix + mathalgo::matrix oXtMatrix(oXMatrix.transpose()); + // multiply transposed X matrix with X matrix + mathalgo::matrix oXtXMatrix(oXtMatrix * oXMatrix); + // multiply transposed X matrix with Y matrix + mathalgo::matrix oXtYMatrix(oXtMatrix * oYMatrix); + + mathalgo::Givens oGivens; + oGivens.Decompose(oXtXMatrix); + mathalgo::matrix oCoeff = oGivens.Solve(oXtYMatrix); + // copy the result to coeff + return oCoeff.data(); +} + +/* +Calculates the value of a polynomial of degree n evaluated at x. The input +argument pCoeff is a vector of length n+1 whose elements are the coefficients +in incremental powers of the polynomial to be evaluated. + +param: +oCoeff polynomial coefficients generated by polyfit() function +oX x axis values + +return: +Fitted Y values. C++0x-compatible compilers make returning locally +created vectors very efficient. +*/ +template +std::vector polyvalqr(const std::vector& oCoeff, const std::vector& oX) { + size_t nCount = oX.size(); + size_t nDegree = oCoeff.size(); + std::vector oY(nCount); + + for (size_t i = 0; i < nCount; i++) { + T nY = 0; + T nXT = 1; + T nX = oX[i]; + for (size_t j = 0; j < nDegree; j++) { + // multiply current x by a coefficient + nY += oCoeff[j] * nXT; + // power up the X + nXT *= nX; + } + oY[i] = nY; + } + + return oY; +} + +// 3D plane fitting +template +std::vector polyfit3d(std::vector& oX, std::vector& oY, std::vector& oZ, int nDegree, std::vector& residuals) { + if (oX.size() != oY.size() || oX.size() != oZ.size()) + throw std::invalid_argument("X and Y or X and Z vector sizes do not match"); + + // more intuative this way + nDegree++; + + size_t nCount = oX.size(); + size_t nCols = std::pow(nDegree, 2); + mathalgo::matrix oXYMatrix(nCount, nCols); + mathalgo::matrix oZMatrix(nCount, 1); + + // normalize x and y matrix + for (size_t i = 1; i < nCount; i++) { + oX[i] = oX[i] - oX[0]; + oY[i] = oY[i] - oY[0]; + } + oX[0] = 0; + oY[0] = 0; + + // copy z matrix + for (size_t i = 0; i < nCount; i++) { + oZMatrix(i, 0) = oZ[i]; + } + + // create the XY matrix + for (size_t nRow = 0; nRow < nCount; nRow++) { + int nCol = 0; + for (size_t i = 0; i < nDegree; i++) { + for (size_t j = 0; j < nDegree; j++) { + oXYMatrix(nRow, i*nDegree+j) = std::pow(oX[nRow], i) * std::pow(oY[nRow], j); + nCol++; + } + } + } + + // transpose X matrix + mathalgo::matrix oXYtMatrix(oXYMatrix.transpose()); + // multiply transposed X matrix with X matrix + mathalgo::matrix oXYtXYMatrix(oXYtMatrix * oXYMatrix); + // multiply transposed X matrix with Y matrix + mathalgo::matrix oXYtZMatrix(oXYtMatrix * oZMatrix); + + mathalgo::Givens oGivens; + oGivens.Decompose(oXYtXYMatrix); + mathalgo::matrix oCoeff = oGivens.Solve(oXYtZMatrix); + + residuals = polyval3d(oCoeff.data(), oXYMatrix); + // copy the result to coeff + return oCoeff.data(); +} + +template +std::vector polyval3d(std::vector& oCoeff, mathalgo::matrix& oXY) { + size_t nCount = oXY.rows(); + size_t nDegree = oCoeff.size(); + mathalgo::matrix oCoeffMatrix(oCoeff.size(), 1); + + std::vector oZ; + + // copy coeffs matrix + for (size_t i = 0; i < nDegree; i++) { + oCoeffMatrix(i, 0) = oCoeff[i]; + } + + for (size_t i = 0; i < oXY.rows(); i++) { + // transpose Y matrix + mathalgo::matrix oYtMatrix(oXY.row(i)); + // multiply transposed Y matrix with Z matrix + mathalgo::matrix oYtZMatrix(oYtMatrix * oCoeffMatrix); + oZ.push_back(oYtZMatrix(0, 0)); + } + + return oZ; +} \ No newline at end of file From d6c30e23f639d7d8c4a0cd236f79fb1dbb338919 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Thu, 2 Aug 2018 18:05:07 +0200 Subject: [PATCH 29/45] 3d polynomial plane fitting working --- src/TopoFeature.cpp | 90 +++++++++++++++++++++--------------------- thirdparty/polyfit.hpp | 57 +++++++++++++++----------- 2 files changed, 80 insertions(+), 67 deletions(-) diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index 07b8f5b4..3482c969 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -1131,60 +1131,59 @@ void Boundary3D::detect_outliers(int degrees_incline) { ringi++; // find spikes in roads (due to misclassified lidar points) and fix by averaging between previous and next vertex. - std::vector x, y, coeffs, x1, y1, z1; - for (int i = 0; i < _p2z[ringi].size(); i++) { - x.push_back(i); - y.push_back(_p2z[ringi][i]); + std::vector idx; + std::vector x, y, z, coeffs; + for (int i = 0; i < ring.size(); i++) { + idx.push_back(i); + x.push_back(ring[i].x()); + y.push_back(ring[i].y()); + z.push_back(_p2z[ringi][i]); } - std::vector xtmp = x, ytmp = y; + std::vector xtmp = x, ytmp = y, ztmp = z; - int niter = _p2z[ringi].size() - 3; + // itterate only if >6 points in the ring or the LS will not work + int niter = _p2z[ringi].size() - 6; std::vector indices; std::vector residualRanges; std::vector gaps; std::vector < std::vector > residualCoeffs; for (int i = 0; i < niter; i++) { // Fit the model - std::vector coeffs = polyfitqr(xtmp, ytmp, 2); - std::vector correctedvalues = polyvalqr(coeffs, xtmp); + //std::vector coeffs = polyfitqr(xtmp, ytmp, 2); + //std::vector correctedvalues = polyvalqr(coeffs, xtmp); + //std::vector residuals; + + //for (int j = 0; j < correctedvalues.size(); j++) { + // residuals.push_back(ytmp[j] - correctedvalues[j]); + //} + std::vector correctedvalues; + std::vector coeffs = polyfit3d(xtmp, ytmp, ztmp, correctedvalues); std::vector residuals; for (int j = 0; j < correctedvalues.size(); j++) { - residuals.push_back(ytmp[j] - correctedvalues[j]); + residuals.push_back(ztmp[j] - correctedvalues[j]); } - if (_id == "L0004.3232f41ea7fb409aa7b64ba27318f4dd") { + //if (_id == "L0004.3232f41ea7fb409aa7b64ba27318f4dd") { // road outlier detection + if (_id == "L0002.e1f202d3ec3345178b79c5c898ea9c0c") { //crossing std::cout << ""; - // std::cout << "coeffs:" << std::endl; - // for (int i = 0; i < 3; i++) { - // std::cout << coeffs[i] << std::endl; - // } - // std::cout << std::endl; - - std::cout << "x-y-z values" << std::endl; - std::cout << std::fixed << std::setprecision(3); - for (int i = 0; i < ring.size(); i++) { - //std::cout << ring[i].x() << "\t" << ring[i].y() << "\t" << _p2z[ringi][i] << std::endl; - x1.push_back(ring[i].x()); - y1.push_back(ring[i].y()); - z1.push_back(_p2z[ringi][i]); - } - std::vector res; - std::vector coeffs = polyfit3d(x1, y1, z1, 2, res); - std::cout << std::endl; - - // std::cout << "fitted values" << std::endl; - // for (int i = 0; i < correctedvalues.size(); i++) { - // std::cout << correctedvalues[i] << std::endl; - // } - // std::cout << std::endl; - - // std::cout << "heights\tvalues\tfitted values\tresiduals" << std::endl; - // for (int i = 0; i < correctedvalues.size(); i++) { - // std::cout << y[i] << "\t" << correctedvalues[i] << "\t\t" << residuals[i] << std::endl; - // } - // std::cout << std::endl; + // std::cout << "coeffs:" << std::endl; + // for (int i = 0; i < 3; i++) { + // std::cout << coeffs[i] << std::endl; + // } + // std::cout << std::endl; + + // std::cout << "fitted values" << std::endl; + // for (int i = 0; i < correctedvalues.size(); i++) { + // std::cout << correctedvalues[i] << std::endl; + // } + // std::cout << std::endl; + + //std::cout << "heights\tfitted values\tresiduals" << std::endl; + //for (int i = 0; i < fitted.size(); i++) { + // std::cout << z1[i] << "\t" << fitted[i] << "\t\t" << res[i] << std::endl; + //} } // Calculate the maximum residual @@ -1192,11 +1191,10 @@ void Boundary3D::detect_outliers(int degrees_incline) { auto max = std::max_element(residuals.begin(), residuals.end(), abs_compare); double res_range = abs(*min) + abs(*max); int imax = max - residuals.begin(); - double vtx = xtmp[imax]; + int vtx = idx[imax]; //double height = y[vtx]; - //double residual = *max; + //double iresidual = *max; - // print and remove the maximum residual //std::cout << "vertex: " << vtx << ", abs res: " << res_range << std::endl; // store the index of the vertex marked as an outlier @@ -1210,9 +1208,11 @@ void Boundary3D::detect_outliers(int degrees_incline) { // store the coefficients for later use residualCoeffs.push_back(coeffs); - // remove the outlier form both x_ite and y_ite + // remove the outlier from idx, xtmp, xtmp and xtmp arrays for next iteration + idx.erase(idx.begin() + imax); xtmp.erase(xtmp.begin() + imax); ytmp.erase(ytmp.begin() + imax); + ztmp.erase(ztmp.begin() + imax); // stop if residuals are <1 (mm precision) if (res_range < 1) { @@ -1229,8 +1229,10 @@ void Boundary3D::detect_outliers(int degrees_incline) { //std::cout << "max gap i: " << maxgapi << ", max gap: " << *maxgap << std::endl; // get the new values based on the coeffs of the equation at max abs residual +1 - std::vector correctedvalues = polyvalqr(residualCoeffs[maxgapi+1], x); + mathalgo::matrix oXYMatrix = combineXY(x, y); + std::vector correctedvalues = polyval3d(residualCoeffs[maxgapi+1], oXYMatrix); + //std::cout << _id << std::endl; // replace the old values with the new values upto the maximum gap (not including since gap is this to next) for (int i = 0; i <= maxgapi; i++) { int idx = indices[i]; diff --git a/thirdparty/polyfit.hpp b/thirdparty/polyfit.hpp index ed76a5ff..7e259202 100644 --- a/thirdparty/polyfit.hpp +++ b/thirdparty/polyfit.hpp @@ -212,20 +212,12 @@ std::vector polyvalqr(const std::vector& oCoeff, const std::vector& oX) return oY; } -// 3D plane fitting template -std::vector polyfit3d(std::vector& oX, std::vector& oY, std::vector& oZ, int nDegree, std::vector& residuals) { - if (oX.size() != oY.size() || oX.size() != oZ.size()) - throw std::invalid_argument("X and Y or X and Z vector sizes do not match"); - - // more intuative this way - nDegree++; - +mathalgo::matrix combineXY(std::vector& oX, std::vector& oY) { size_t nCount = oX.size(); - size_t nCols = std::pow(nDegree, 2); + size_t nCols = 3 * 2; // 3 unknowns in 2 dimensions mathalgo::matrix oXYMatrix(nCount, nCols); - mathalgo::matrix oZMatrix(nCount, 1); - + // normalize x and y matrix for (size_t i = 1; i < nCount; i++) { oX[i] = oX[i] - oX[0]; @@ -234,22 +226,41 @@ std::vector polyfit3d(std::vector& oX, std::vector& oY, std::vector& oX[0] = 0; oY[0] = 0; + // create the XY matrix + for (size_t nRow = 0; nRow < nCount; nRow++) { + oXYMatrix(nRow, 0) = 1; + oXYMatrix(nRow, 1) = oX[nRow]; + oXYMatrix(nRow, 2) = oY[nRow]; + oXYMatrix(nRow, 3) = oX[nRow] * oY[nRow]; + oXYMatrix(nRow, 4) = std::pow(oX[nRow], 2); + oXYMatrix(nRow, 5) = std::pow(oY[nRow], 2); + + //int nCol = 0; + //for (size_t i = 0; i < nDegree; i++) { + // for (size_t j = 0; j < nDegree; j++) { + // oXYMatrix(nRow, i*nDegree+j) = std::pow(oX[nRow], i) * std::pow(oY[nRow], j); + // nCol++; + // } + //} + } + return oXYMatrix; +} + +// 3D plane fitting +template +std::vector polyfit3d(std::vector& oX, std::vector& oY, std::vector& oZ, std::vector& calculated) { + if (oX.size() != oY.size() || oX.size() != oZ.size()) + throw std::invalid_argument("X and Y or X and Z vector sizes do not match"); + + size_t nCount = oX.size(); + mathalgo::matrix oXYMatrix(combineXY(oX, oY)); + mathalgo::matrix oZMatrix(nCount, 1); + // copy z matrix for (size_t i = 0; i < nCount; i++) { oZMatrix(i, 0) = oZ[i]; } - // create the XY matrix - for (size_t nRow = 0; nRow < nCount; nRow++) { - int nCol = 0; - for (size_t i = 0; i < nDegree; i++) { - for (size_t j = 0; j < nDegree; j++) { - oXYMatrix(nRow, i*nDegree+j) = std::pow(oX[nRow], i) * std::pow(oY[nRow], j); - nCol++; - } - } - } - // transpose X matrix mathalgo::matrix oXYtMatrix(oXYMatrix.transpose()); // multiply transposed X matrix with X matrix @@ -261,7 +272,7 @@ std::vector polyfit3d(std::vector& oX, std::vector& oY, std::vector& oGivens.Decompose(oXYtXYMatrix); mathalgo::matrix oCoeff = oGivens.Solve(oXYtZMatrix); - residuals = polyval3d(oCoeff.data(), oXYMatrix); + calculated = polyval3d(oCoeff.data(), oXYMatrix); // copy the result to coeff return oCoeff.data(); } From b0328120ea693e9daecec194dbdcd61aa3c8f111 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Tue, 7 Aug 2018 17:37:41 +0200 Subject: [PATCH 30/45] finalize outlier filtering with plane fitting for roads and bridges --- src/Map3d.cpp | 6 +- src/Road.cpp | 3 +- src/TopoFeature.cpp | 287 ++++++++++++-------------------------------- src/TopoFeature.h | 2 +- 4 files changed, 82 insertions(+), 216 deletions(-) diff --git a/src/Map3d.cpp b/src/Map3d.cpp index b0e05c56..5e5b69ad 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -1393,6 +1393,10 @@ void Map3d::stitch_bridges() { std::vector ringis, pis; for (auto& f : _lsFeatures) { if (f->get_class() == BRIDGE && f->get_top_level() == false) { + // Make bridge face flat + Bridge* b = dynamic_cast(f); + b->detect_outliers(true); + //-- 1. store all touching top level (adjacent + incident) std::vector* lstouching = f->get_adjacent_features(); @@ -1426,8 +1430,6 @@ void Map3d::stitch_bridges() { } } } - Bridge* b = dynamic_cast(f); - //b->detect_outliers(_road_threshold_outliers); } } diff --git a/src/Road.cpp b/src/Road.cpp index 2d69bca2..af3cdf4d 100644 --- a/src/Road.cpp +++ b/src/Road.cpp @@ -57,8 +57,7 @@ bool Road::add_elevation_point(Point2 &p, double z, float radius, int lasclass) bool Road::lift() { lift_each_boundary_vertices(_heightref); - //smooth_boundary(5); - detect_outliers(_threshold_outliers); + detect_outliers(true); return true; } diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index 3482c969..9f852def 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -29,6 +29,10 @@ #include "TopoFeature.h" #include "io.h" #include "polyfit.hpp" +#include +#include + +using namespace boost::accumulators; TopoFeature::TopoFeature(char *wkt, std::string layername, AttributeMap attributes, std::string pid) { _id = pid; @@ -1119,7 +1123,7 @@ void Boundary3D::smooth_boundary(int passes) { } } -void Boundary3D::detect_outliers(int degrees_incline) { +void Boundary3D::detect_outliers(bool replace_all) { //-- gather all rings std::vector rings; rings.push_back(_p2->outer()); @@ -1130,227 +1134,88 @@ void Boundary3D::detect_outliers(int degrees_incline) { for (Ring2& ring : rings) { ringi++; - // find spikes in roads (due to misclassified lidar points) and fix by averaging between previous and next vertex. - std::vector idx; - std::vector x, y, z, coeffs; - for (int i = 0; i < ring.size(); i++) { - idx.push_back(i); - x.push_back(ring[i].x()); - y.push_back(ring[i].y()); - z.push_back(_p2z[ringi][i]); - } - - std::vector xtmp = x, ytmp = y, ztmp = z; - // itterate only if >6 points in the ring or the LS will not work - int niter = _p2z[ringi].size() - 6; - std::vector indices; - std::vector residualRanges; - std::vector gaps; - std::vector < std::vector > residualCoeffs; - for (int i = 0; i < niter; i++) { - // Fit the model - //std::vector coeffs = polyfitqr(xtmp, ytmp, 2); - //std::vector correctedvalues = polyvalqr(coeffs, xtmp); - //std::vector residuals; - - //for (int j = 0; j < correctedvalues.size(); j++) { - // residuals.push_back(ytmp[j] - correctedvalues[j]); - //} - std::vector correctedvalues; - std::vector coeffs = polyfit3d(xtmp, ytmp, ztmp, correctedvalues); - std::vector residuals; - - for (int j = 0; j < correctedvalues.size(); j++) { - residuals.push_back(ztmp[j] - correctedvalues[j]); + if (ring.size() > 6) { + // find spikes in roads (due to misclassified lidar points) and fix by averaging between previous and next vertex. + std::vector idx; + std::vector x, y, z, coeffs; + for (int i = 0; i < ring.size(); i++) { + idx.push_back(i); + x.push_back(ring[i].x()); + y.push_back(ring[i].y()); + z.push_back(_p2z[ringi][i]); } - //if (_id == "L0004.3232f41ea7fb409aa7b64ba27318f4dd") { // road outlier detection - if (_id == "L0002.e1f202d3ec3345178b79c5c898ea9c0c") { //crossing - std::cout << ""; - // std::cout << "coeffs:" << std::endl; - // for (int i = 0; i < 3; i++) { - // std::cout << coeffs[i] << std::endl; - // } - // std::cout << std::endl; - - // std::cout << "fitted values" << std::endl; - // for (int i = 0; i < correctedvalues.size(); i++) { - // std::cout << correctedvalues[i] << std::endl; - // } - // std::cout << std::endl; - - //std::cout << "heights\tfitted values\tresiduals" << std::endl; - //for (int i = 0; i < fitted.size(); i++) { - // std::cout << z1[i] << "\t" << fitted[i] << "\t\t" << res[i] << std::endl; - //} - } + std::vector xtmp = x, ytmp = y, ztmp = z; + int niter = _p2z[ringi].size() - 6; + std::vector indices; + double se = 0; + for (int i = 0; i < niter; i++) { + // Fit the model + std::vector correctedvalues; + coeffs = polyfit3d(xtmp, ytmp, ztmp, correctedvalues); + std::vector residuals, absResiduals; + + double sum = 0; + for (int j = 0; j < correctedvalues.size(); j++) { + absResiduals.push_back(abs(correctedvalues[j] - ztmp[j])); + if (i == 0) { + double z = correctedvalues[j] - ztmp[j]; + residuals.push_back(z); + sum += z; + } + } + if (i == 0) { + double m = sum / residuals.size(); - // Calculate the maximum residual - auto min = std::min_element(residuals.begin(), residuals.end(), abs_compare); - auto max = std::max_element(residuals.begin(), residuals.end(), abs_compare); - double res_range = abs(*min) + abs(*max); - int imax = max - residuals.begin(); - int vtx = idx[imax]; - //double height = y[vtx]; - //double iresidual = *max; - - //std::cout << "vertex: " << vtx << ", abs res: " << res_range << std::endl; - - // store the index of the vertex marked as an outlier - indices.push_back(vtx); - // store size of the gap with the previous residual, do before storing residual_range - if (!residualRanges.empty()) { - gaps.push_back(abs(res_range - residualRanges.back())); - } - // store the residual range of the outlier - residualRanges.push_back(res_range); - // store the coefficients for later use - residualCoeffs.push_back(coeffs); - - // remove the outlier from idx, xtmp, xtmp and xtmp arrays for next iteration - idx.erase(idx.begin() + imax); - xtmp.erase(xtmp.begin() + imax); - ytmp.erase(ytmp.begin() + imax); - ztmp.erase(ztmp.begin() + imax); - - // stop if residuals are <1 (mm precision) - if (res_range < 1) { - break; - } - } + double sq_sum = 0; + std::for_each(residuals.begin(), residuals.end(), [&](const double d) { + sq_sum += (d - m) * (d - m); + }); - // find the largest gap - auto maxgap = std::max_element(gaps.begin(), gaps.end()); - // skip if gaps is empty - if (maxgap != gaps.end()) { - int maxgapi = maxgap - gaps.begin(); + // Calculate standard deviation and 2 sigma + double stdev = sqrt(sq_sum / residuals.size()); + se = 2 * stdev; + } - //std::cout << "max gap i: " << maxgapi << ", max gap: " << *maxgap << std::endl; + // Calculate the maximum residual + auto max = std::max_element(absResiduals.begin(), absResiduals.end()); + // remove outlier if larger then 2*standard deviation + if (*max > se) { + //double res_range = abs(*min) + abs(*max); + int imax = max - absResiduals.begin(); + int vtx = idx[imax]; + //std::cout << "vertex: " << vtx << ", max res: " << *max << std::endl; + + // store the index of the vertex marked as an outlier + indices.push_back(vtx); + + // remove the outlier from idx, xtmp, xtmp and xtmp arrays for next iteration + idx.erase(idx.begin() + imax); + xtmp.erase(xtmp.begin() + imax); + ytmp.erase(ytmp.begin() + imax); + ztmp.erase(ztmp.begin() + imax); + } + else { + break; + } + } - // get the new values based on the coeffs of the equation at max abs residual +1 + // get the new values based on the coeffs of the lase equation mathalgo::matrix oXYMatrix = combineXY(x, y); - std::vector correctedvalues = polyval3d(residualCoeffs[maxgapi+1], oXYMatrix); - - //std::cout << _id << std::endl; - // replace the old values with the new values upto the maximum gap (not including since gap is this to next) - for (int i = 0; i <= maxgapi; i++) { - int idx = indices[i]; - //std::cout << "old: " << y[idx] << ", new: " << correctedvalues[idx] << std::endl; - _p2z[ringi][idx] = correctedvalues[idx]; + std::vector correctedvalues = polyval3d(coeffs, oXYMatrix); + if (replace_all) { + for (int i = 0; i < ring.size(); i++) { + _p2z[ringi][i] = correctedvalues[i]; + } + } + else { + for (int i : indices) { + _p2z[ringi][i] = correctedvalues[i]; + } } } } - - - ////-- gather all rings - //std::vector rings; - //rings.push_back(_p2->outer()); - //for (Ring2& iring : _p2->inners()) - // rings.push_back(iring); - - //int ringi = -1; - //for (Ring2& ring : rings) { - // ringi++; - // std::vector ringz = _p2z[ringi]; - // std::vector inclines(_p2z[ringi].size()); - // std::vector changez(_p2z[ringi].size(), 0); - // float PI = 3.14159265; - - // for (int i = 0; i < ring.size(); i++) { - // int i0 = i - 1; - // int i2 = i + 1; - // if (i == 0) { - // i0 = ring.size() - 1; - // } - // if (i2 == ring.size()) { - // i2 = 0; - // } - // float len1z = (ringz[i] - ringz[i0]) / 100.0; - // float len2z = (ringz[i2] - ringz[i]) / 100.0; - // float incline = atan2(len2z, distance(ring[i], ring[i2])) - atan2(len1z, distance(ring[i0], ring[i])); - // if (incline <= -PI) { - // incline = 2 * PI + incline; - // } - // if (incline > PI) { - // incline = incline - 2 * PI; - // } - // incline = incline * 180 / PI; - // //if (incline > 0) we have a peak down, otherwise we have a peak up - // if (abs(incline) > degrees_incline) { - // if (abs(len2z) > abs(len1z)) { - // if (incline > 0) { - // // going from 'flat' surface up - // changez[i] = 1; - // } - // else { - // // going from 'flat' surface down - // changez[i] = -1; - // } - // } - // } - // } - // // find the index of the first peak up (value == 1) - // int start_idx = std::find(changez.begin(), changez.end(), 1) - changez.begin(); - // if (start_idx == changez.size()) { - // // else find the index of the first peak down (value == -1) - // start_idx = std::find(changez.begin(), changez.end(), -1) - changez.begin(); - // if (start_idx == changez.size()) { - // start_idx = ring.size(); - // } - // } - - // if (start_idx != ring.size()) { - // // Detect and fix outliers starting from the first peak up detected previously - // for (int j = 0; j < ring.size(); ++j) { - // //make round-trip through vector starting at start_idx - // int i = (j + start_idx + ring.size()) % ring.size(); - // int i0 = i - 1; - // int i2 = i + 1; - // if (i == 0) { - // i0 = ring.size() - 1; - // } - // if (i2 == ring.size()) { - // i2 = 0; - // } - // float len1z = (ringz[i] - ringz[i0]) / 100.0; - // float len2z = (ringz[i2] - ringz[i]) / 100.0; - // float incline = atan2(len2z, distance(ring[i], ring[i2])) - atan2(len1z, distance(ring[i0], ring[i])); - // if (incline <= -PI) { - // incline = 2 * PI + incline; - // } - // if (incline > PI) { - // incline = incline - 2 * PI; - // } - // incline = incline * 180 / PI; - // //if (incline > 0) we have a peak down, otherwise we have a peak up - // if (abs(incline) > degrees_incline) { - // //find the outlier by sorting and comparing distance - // std::vector heights = { ringz[i0], ringz[i], ringz[i2] }; - // std::sort(heights.begin(), heights.end()); - // int h = heights[0]; - // if (abs(heights[2] - heights[1]) > abs(heights[0] - heights[1])) { - // h = heights[2]; - // } - - // if (ringz[i0] == h) { - // //put to height of closest vertex for now - // _p2z[ringi][i0] = ringz[i]; - // ringz[i0] = ringz[i]; - // } - // else if (ringz[i] == h) { - // _p2z[ringi][i] = (ringz[i0] + ringz[i2]) / 2; - // ringz[i] = (ringz[i0] + ringz[i2]) / 2; - // } - // else if (ringz[i2] == h) { - // //put to height of closest vertex for now - // _p2z[ringi][i2] = ringz[i]; - // ringz[i2] = ringz[i]; - // } - // } - // } - // } - //} } //------------------------------- diff --git a/src/TopoFeature.h b/src/TopoFeature.h index 2733fd72..31985332 100644 --- a/src/TopoFeature.h +++ b/src/TopoFeature.h @@ -137,7 +137,7 @@ class Boundary3D: public TopoFeature { virtual bool lift() = 0; virtual void get_citygml(std::wostream& of) = 0; virtual void get_cityjson(nlohmann::json& j, std::unordered_map &dPts) = 0; - void detect_outliers(int degrees_incline); + void detect_outliers(bool replace_all); protected: int _simplification; void smooth_boundary(int passes = 1); From 4b1147091d441ba9ee42332ca9a3c4cf266aeef6 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Wed, 8 Aug 2018 11:23:35 +0200 Subject: [PATCH 31/45] fix bug redefined bool for error in reading polygon files --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index ada98223..d9050825 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -464,7 +464,7 @@ int main(int argc, const char * argv[]) { //-- add the polygons to the map3d if (bPolyData) { - bool bPolyData = map3d.add_polygons_files(polygonFiles); + bPolyData = map3d.add_polygons_files(polygonFiles); } if (!bPolyData) { std::cerr << "ERROR: Missing polygon data, cannot 3dfy the dataset. Aborting.\n"; From 19af402c6caa961248ea7b7204af79c81edfbebf Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Thu, 9 Aug 2018 10:52:23 +0200 Subject: [PATCH 32/45] clean polyfit code --- thirdparty/matrix.hpp | 4 +- thirdparty/polyfit.hpp | 127 ----------------------------------------- 2 files changed, 1 insertion(+), 130 deletions(-) diff --git a/thirdparty/matrix.hpp b/thirdparty/matrix.hpp index 4e851bc0..10a84791 100644 --- a/thirdparty/matrix.hpp +++ b/thirdparty/matrix.hpp @@ -7,8 +7,6 @@ namespace mathalgo { - using namespace std; - template class matrix { @@ -105,7 +103,7 @@ namespace mathalgo return m_nCols; } - inline vector data() + inline std::vector data() { return m_oData; } diff --git a/thirdparty/polyfit.hpp b/thirdparty/polyfit.hpp index 7e259202..69b8ae3c 100644 --- a/thirdparty/polyfit.hpp +++ b/thirdparty/polyfit.hpp @@ -1,124 +1,5 @@ -#pragma once - -#ifdef BOOST_UBLAS_TYPE_CHECK -# undef BOOST_UBLAS_TYPE_CHECK -#endif -#define BOOST_UBLAS_TYPE_CHECK 0 -#ifndef _USE_MATH_DEFINES -# define _USE_MATH_DEFINES -#endif - -#include -#include #include #include - -/* - Finds the coefficients of a polynomial p(x) of degree n that fits the data, - p(x(i)) to y(i), in a least squares sense. The result p is a row vector of - length n+1 containing the polynomial coefficients in incremental powers. - - param: - oX x axis values - oY y axis values - nDegree polynomial degree including the constant - - return: - coefficients of a polynomial starting at the constant coefficient and - ending with the coefficient of power to nDegree. C++0x-compatible - compilers make returning locally created vectors very efficient. - -*/ -template -std::vector polyfit( const std::vector& oX, const std::vector& oY, int nDegree ) -{ - using namespace boost::numeric::ublas; - - if ( oX.size() != oY.size() ) - throw std::invalid_argument( "X and Y vector sizes do not match" ); - - // more intuative this way - nDegree++; - - size_t nCount = oX.size(); - matrix oXMatrix( nCount, nDegree ); - matrix oYMatrix( nCount, 1 ); - - // copy y matrix - for ( size_t i = 0; i < nCount; i++ ) - { - oYMatrix(i, 0) = oY[i]; - } - - // create the X matrix - for ( size_t nRow = 0; nRow < nCount; nRow++ ) - { - T nVal = 1.0f; - for ( int nCol = 0; nCol < nDegree; nCol++ ) - { - oXMatrix(nRow, nCol) = nVal; - nVal *= oX[nRow]; - } - } - - // transpose X matrix - matrix oXtMatrix( trans(oXMatrix) ); - // multiply transposed X matrix with X matrix - matrix oXtXMatrix( prec_prod(oXtMatrix, oXMatrix) ); - // multiply transposed X matrix with Y matrix - matrix oXtYMatrix( prec_prod(oXtMatrix, oYMatrix) ); - - // lu decomposition - permutation_matrix pert(oXtXMatrix.size1()); - const std::size_t singular = lu_factorize(oXtXMatrix, pert); - // must be singular - BOOST_ASSERT( singular == 0 ); - - // backsubstitution - lu_substitute(oXtXMatrix, pert, oXtYMatrix); - - // copy the result to coeff - return std::vector( oXtYMatrix.data().begin(), oXtYMatrix.data().end() ); -} - -/* - Calculates the value of a polynomial of degree n evaluated at x. The input - argument pCoeff is a vector of length n+1 whose elements are the coefficients - in incremental powers of the polynomial to be evaluated. - - param: - oCoeff polynomial coefficients generated by polyfit() function - oX x axis values - - return: - Fitted Y values. C++0x-compatible compilers make returning locally - created vectors very efficient. -*/ -template -std::vector polyval( const std::vector& oCoeff, const std::vector& oX ) -{ - size_t nCount = oX.size(); - size_t nDegree = oCoeff.size(); - std::vector oY( nCount ); - - for ( size_t i = 0; i < nCount; i++ ) - { - T nY = 0; - T nXT = 1; - T nX = oX[i]; - for ( size_t j = 0; j < nDegree; j++ ) - { - // multiply current x by a coefficient - nY += oCoeff[j] * nXT; - // power up the X - nXT *= nX; - } - oY[i] = nY; - } - - return oY; -} - #include "matrix.hpp" #include "GivensQR.hpp" /* @@ -234,14 +115,6 @@ mathalgo::matrix combineXY(std::vector& oX, std::vector& oY) { oXYMatrix(nRow, 3) = oX[nRow] * oY[nRow]; oXYMatrix(nRow, 4) = std::pow(oX[nRow], 2); oXYMatrix(nRow, 5) = std::pow(oY[nRow], 2); - - //int nCol = 0; - //for (size_t i = 0; i < nDegree; i++) { - // for (size_t j = 0; j < nDegree; j++) { - // oXYMatrix(nRow, i*nDegree+j) = std::pow(oX[nRow], i) * std::pow(oY[nRow], j); - // nCol++; - // } - //} } return oXYMatrix; } From 441755163aab22c811dee4656b31434984a74af1 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Thu, 9 Aug 2018 16:48:30 +0200 Subject: [PATCH 33/45] final outlier_detection changes --- src/TopoFeature.cpp | 20 ++++------ thirdparty/matrix.hpp | 90 ++++++++++++++++++++++++++++++++++++++++++ thirdparty/polyfit.hpp | 51 ++++++++++-------------- 3 files changed, 118 insertions(+), 43 deletions(-) diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index 9f852def..e01e556c 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -29,10 +29,6 @@ #include "TopoFeature.h" #include "io.h" #include "polyfit.hpp" -#include -#include - -using namespace boost::accumulators; TopoFeature::TopoFeature(char *wkt, std::string layername, AttributeMap attributes, std::string pid) { _id = pid; @@ -1158,15 +1154,16 @@ void Boundary3D::detect_outliers(bool replace_all) { double sum = 0; for (int j = 0; j < correctedvalues.size(); j++) { - absResiduals.push_back(abs(correctedvalues[j] - ztmp[j])); + absResiduals.push_back(abs(ztmp[j] - correctedvalues[j])); if (i == 0) { - double z = correctedvalues[j] - ztmp[j]; + double z = ztmp[j] - correctedvalues[j]; residuals.push_back(z); sum += z; } } if (i == 0) { - double m = sum / residuals.size(); + int n = residuals.size(); + double m = sum / n; double sq_sum = 0; std::for_each(residuals.begin(), residuals.end(), [&](const double d) { @@ -1174,18 +1171,16 @@ void Boundary3D::detect_outliers(bool replace_all) { }); // Calculate standard deviation and 2 sigma - double stdev = sqrt(sq_sum / residuals.size()); - se = 2 * stdev; + double stdev = sqrt(sq_sum / (n - 1)); + se = 1.96 * stdev; } // Calculate the maximum residual auto max = std::max_element(absResiduals.begin(), absResiduals.end()); // remove outlier if larger then 2*standard deviation if (*max > se) { - //double res_range = abs(*min) + abs(*max); int imax = max - absResiduals.begin(); int vtx = idx[imax]; - //std::cout << "vertex: " << vtx << ", max res: " << *max << std::endl; // store the index of the vertex marked as an outlier indices.push_back(vtx); @@ -1202,8 +1197,7 @@ void Boundary3D::detect_outliers(bool replace_all) { } // get the new values based on the coeffs of the lase equation - mathalgo::matrix oXYMatrix = combineXY(x, y); - std::vector correctedvalues = polyval3d(coeffs, oXYMatrix); + std::vector correctedvalues = polyval3d(x, y, coeffs); if (replace_all) { for (int i = 0; i < ring.size(); i++) { _p2z[ringi][i] = correctedvalues[i]; diff --git a/thirdparty/matrix.hpp b/thirdparty/matrix.hpp index 10a84791..226f86d8 100644 --- a/thirdparty/matrix.hpp +++ b/thirdparty/matrix.hpp @@ -33,6 +33,96 @@ namespace mathalgo return oResult; } + inline matrix minor(int i) { + matrix oResult(m_nRows, m_nCols); + int n = m_nRows; + int h = 0, k = 0; + for (int l = 1; l < n; l++) { + for (int j = 0; j < n; j++) { + if (j == i) + continue; + oResult(h,k) = (*this)(l,j); + k++; + if (k == (n - 1)) { + h++; + k = 0; + } + } + } + return oResult; + } + + inline T determinant(int n) { + if (m_nCols != m_nRows) { + throw domain_error("matrix dimensions are not the same"); + } + matrix b(m_nRows, m_nCols); + T sum = 0; + if (n == 1) + return (*this)(0,0); + else if (n == 2) + return ((*this)(0, 0) * (*this)(1, 1) - (*this)(0, 1) * (*this)(1, 0)); + else + for (int i = 0; i= m_nRows || nCol >= m_nCols ) diff --git a/thirdparty/polyfit.hpp b/thirdparty/polyfit.hpp index 69b8ae3c..ab5465d1 100644 --- a/thirdparty/polyfit.hpp +++ b/thirdparty/polyfit.hpp @@ -126,50 +126,41 @@ std::vector polyfit3d(std::vector& oX, std::vector& oY, std::vector& throw std::invalid_argument("X and Y or X and Z vector sizes do not match"); size_t nCount = oX.size(); - mathalgo::matrix oXYMatrix(combineXY(oX, oY)); - mathalgo::matrix oZMatrix(nCount, 1); + mathalgo::matrix A(combineXY(oX, oY)); + mathalgo::matrix X(nCount, 1); // copy z matrix for (size_t i = 0; i < nCount; i++) { - oZMatrix(i, 0) = oZ[i]; + X(i, 0) = oZ[i]; } + // Y=(AT*A)-1*AT*X // transpose X matrix - mathalgo::matrix oXYtMatrix(oXYMatrix.transpose()); + mathalgo::matrix AT(A.transpose()); // multiply transposed X matrix with X matrix - mathalgo::matrix oXYtXYMatrix(oXYtMatrix * oXYMatrix); + mathalgo::matrix ATA(AT * A); // multiply transposed X matrix with Y matrix - mathalgo::matrix oXYtZMatrix(oXYtMatrix * oZMatrix); + mathalgo::matrix ATX(AT * X); mathalgo::Givens oGivens; - oGivens.Decompose(oXYtXYMatrix); - mathalgo::matrix oCoeff = oGivens.Solve(oXYtZMatrix); + oGivens.Decompose(ATA); + mathalgo::matrix Y = oGivens.Solve(ATX); - calculated = polyval3d(oCoeff.data(), oXYMatrix); + mathalgo::matrix AY(A * Y.transpose()); + + calculated = AY.data(); // copy the result to coeff - return oCoeff.data(); + return Y.data(); } template -std::vector polyval3d(std::vector& oCoeff, mathalgo::matrix& oXY) { - size_t nCount = oXY.rows(); - size_t nDegree = oCoeff.size(); - mathalgo::matrix oCoeffMatrix(oCoeff.size(), 1); - - std::vector oZ; - - // copy coeffs matrix - for (size_t i = 0; i < nDegree; i++) { - oCoeffMatrix(i, 0) = oCoeff[i]; - } - - for (size_t i = 0; i < oXY.rows(); i++) { - // transpose Y matrix - mathalgo::matrix oYtMatrix(oXY.row(i)); - // multiply transposed Y matrix with Z matrix - mathalgo::matrix oYtZMatrix(oYtMatrix * oCoeffMatrix); - oZ.push_back(oYtZMatrix(0, 0)); +std::vector polyval3d(std::vector& x, std::vector& y, std::vector& coeff) { + mathalgo::matrix A = combineXY(x, y); + mathalgo::matrix Y(coeff.size(), 1); + // build coeff matrix + for (size_t i = 0; i < coeff.size(); i++) { + Y(i, 0) = coeff[i]; } - - return oZ; + mathalgo::matrix AY(A * Y); + return AY.data(); } \ No newline at end of file From ad299976634f710958cef9c0c2e53962b18d863f Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Fri, 10 Aug 2018 11:34:13 +0200 Subject: [PATCH 34/45] TEST: add points of class 1 only within road polygon --- src/Road.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Road.cpp b/src/Road.cpp index af3cdf4d..b9352430 100644 --- a/src/Road.cpp +++ b/src/Road.cpp @@ -51,7 +51,15 @@ std::string Road::get_mtl() { } bool Road::add_elevation_point(Point2 &p, double z, float radius, int lasclass) { - Boundary3D::add_elevation_point(p, z, radius, lasclass); + if (lasclass == 1) { + if (point_in_polygon(p, *(_p2))) { + Boundary3D::add_elevation_point(p, z, radius, lasclass); + } + } + else { + Boundary3D::add_elevation_point(p, z, radius, lasclass); + } + return true; } From 53606871c494a7ca32e7bed7a4a4f63217445380 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Fri, 10 Aug 2018 11:34:56 +0200 Subject: [PATCH 35/45] change variable names in detect_outliers --- src/TopoFeature.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index e01e556c..25b66456 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -1119,7 +1119,7 @@ void Boundary3D::smooth_boundary(int passes) { } } -void Boundary3D::detect_outliers(bool replace_all) { +void Boundary3D::detect_outliers(bool replace_all){ //-- gather all rings std::vector rings; rings.push_back(_p2->outer()); @@ -1143,6 +1143,7 @@ void Boundary3D::detect_outliers(bool replace_all) { } std::vector xtmp = x, ytmp = y, ztmp = z; + int niter = _p2z[ringi].size() - 6; std::vector indices; double se = 0; @@ -1163,11 +1164,11 @@ void Boundary3D::detect_outliers(bool replace_all) { } if (i == 0) { int n = residuals.size(); - double m = sum / n; + double mean = sum / n; double sq_sum = 0; - std::for_each(residuals.begin(), residuals.end(), [&](const double d) { - sq_sum += (d - m) * (d - m); + std::for_each(residuals.begin(), residuals.end(), [&](const double res) { + sq_sum += (res - mean) * (res - mean); }); // Calculate standard deviation and 2 sigma @@ -1175,6 +1176,9 @@ void Boundary3D::detect_outliers(bool replace_all) { se = 1.96 * stdev; } + if (_id == "O0121.13b76aa29e794ec299a4dca4797c2144\n") { + std::cout << ""; + } // Calculate the maximum residual auto max = std::max_element(absResiduals.begin(), absResiduals.end()); // remove outlier if larger then 2*standard deviation From 3d428a60f2a502d759644a72e9ef0d431595177d Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Fri, 10 Aug 2018 16:13:47 +0200 Subject: [PATCH 36/45] add settings for road.filter_outliers, road.flatten and bridge.flatten --- src/Bridge.cpp | 8 +++++++- src/Bridge.h | 4 +++- src/Map3d.cpp | 21 +++++++++++++++------ src/Map3d.h | 8 ++++++-- src/Road.cpp | 12 ++++++++---- src/Road.h | 5 +++-- src/TopoFeature.cpp | 7 ++----- src/main.cpp | 40 +++++++++++++++++++++++++++++++++++----- 8 files changed, 79 insertions(+), 26 deletions(-) diff --git a/src/Bridge.cpp b/src/Bridge.cpp index aff9f303..2ad1c32c 100644 --- a/src/Bridge.cpp +++ b/src/Bridge.cpp @@ -30,10 +30,12 @@ #include "io.h" float Bridge::_heightref; +bool Bridge::_flatten; -Bridge::Bridge(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref) +Bridge::Bridge(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref, bool flatten) : Boundary3D(wkt, layername, attributes, pid) { _heightref = heightref; + _flatten = flatten; } TopoClass Bridge::get_class() { @@ -60,6 +62,10 @@ bool Bridge::lift() { return true; } +bool Bridge::get_flatten() { + return _flatten; +} + void Bridge::get_cityjson(nlohmann::json& j, std::unordered_map &dPts) { nlohmann::json f; f["type"] = "Bridge"; diff --git a/src/Bridge.h b/src/Bridge.h index 84afa7ef..394c3e3e 100644 --- a/src/Bridge.h +++ b/src/Bridge.h @@ -33,7 +33,7 @@ class Bridge: public Boundary3D { public: - Bridge(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref); + Bridge(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref, bool flatten); bool lift(); bool add_elevation_point(Point2 &p, double z, float radius, int lasclass); @@ -44,8 +44,10 @@ class Bridge: public Boundary3D { bool get_shape(OGRLayer* layer, bool writeAttributes, AttributeMap extraAttributes = AttributeMap()); TopoClass get_class(); bool is_hard(); + bool get_flatten(); private: static float _heightref; + static bool _flatten; }; #endif /* Bridge_h */ diff --git a/src/Map3d.cpp b/src/Map3d.cpp index 5e5b69ad..adc95065 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -50,7 +50,8 @@ Map3d::Map3d() { _forest_innerbuffer = 0.0; _water_heightref = 0.1; _road_heightref = 0.5; - _road_threshold_outliers = 30; + _road_filter_outliers = true; + _road_flatten = true; _separation_heightref = 0.8; _bridge_heightref = 0.5; _radius_vertex_elevation = 1.0; @@ -134,8 +135,12 @@ void Map3d::set_road_heightref(float h) { _road_heightref = h; } -void Map3d::set_road_threshold_outliers(int t) { - _road_threshold_outliers = t; +void Map3d::set_road_filter_outliers(bool filter) { + _road_filter_outliers = filter; +} + +void Map3d::set_road_flatten(bool flatten) { + _road_flatten = flatten; } void Map3d::set_separation_heightref(float h) { @@ -146,6 +151,10 @@ void Map3d::set_bridge_heightref(float h) { _bridge_heightref = h; } +void Map3d::set_bridge_flatten(bool flatten) { + _bridge_flatten = flatten; +} + void Map3d::set_requested_extent(double xmin, double ymin, double xmax, double ymax) { _requestedExtent = Box2(Point2(xmin, ymin), Point2(xmax, ymax)); } @@ -952,7 +961,7 @@ void Map3d::extract_feature(OGRFeature *f, std::string layername, const char *id _lsFeatures.push_back(p3); } else if (layertype == "Road") { - Road* p3 = new Road(wkt, layername, attributes, id, this->_road_heightref, this->_road_threshold_outliers); + Road* p3 = new Road(wkt, layername, attributes, id, this->_road_heightref, this->_road_filter_outliers, this->_road_flatten); _lsFeatures.push_back(p3); } else if (layertype == "Separation") { @@ -960,7 +969,7 @@ void Map3d::extract_feature(OGRFeature *f, std::string layername, const char *id _lsFeatures.push_back(p3); } else if (layertype == "Bridge/Overpass") { - Bridge* p3 = new Bridge(wkt, layername, attributes, id, this->_bridge_heightref); + Bridge* p3 = new Bridge(wkt, layername, attributes, id, this->_bridge_heightref, _bridge_flatten); _lsFeatures.push_back(p3); } //-- flag all polygons at (niveau != 0) or remove if not handling multiple height levels @@ -1395,7 +1404,7 @@ void Map3d::stitch_bridges() { if (f->get_class() == BRIDGE && f->get_top_level() == false) { // Make bridge face flat Bridge* b = dynamic_cast(f); - b->detect_outliers(true); + b->detect_outliers(b->get_flatten()); //-- 1. store all touching top level (adjacent + incident) std::vector* lstouching = f->get_adjacent_features(); diff --git a/src/Map3d.h b/src/Map3d.h index ba8973ef..ecfaac70 100644 --- a/src/Map3d.h +++ b/src/Map3d.h @@ -94,9 +94,11 @@ class Map3d { void set_forest_innerbuffer(float innerbuffer); void set_water_heightref(float heightref); void set_road_heightref(float heightref); - void set_road_threshold_outliers(int t); + void set_road_filter_outliers(bool filter); + void set_road_flatten(bool flatten); void set_separation_heightref(float heightref); void set_bridge_heightref(float heightref); + void set_bridge_flatten(bool flatten); void set_radius_vertex_elevation(float radius); void set_building_radius_vertex_elevation(float radius); void set_threshold_jump_edges(float threshold); @@ -121,9 +123,11 @@ class Map3d { float _forest_innerbuffer; float _water_heightref; float _road_heightref; - int _road_threshold_outliers; + bool _road_filter_outliers; + bool _road_flatten; float _separation_heightref; float _bridge_heightref; + bool _bridge_flatten; float _radius_vertex_elevation; float _building_radius_vertex_elevation; int _threshold_jump_edges; //-- in cm/integer diff --git a/src/Road.cpp b/src/Road.cpp index af3cdf4d..52118e22 100644 --- a/src/Road.cpp +++ b/src/Road.cpp @@ -30,12 +30,14 @@ #include "io.h" float Road::_heightref; -int Road::_threshold_outliers; +bool Road::_filter_outliers; +bool Road::_flatten; -Road::Road(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref, int threshold_outliers) +Road::Road(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref, bool filter_outliers, bool flatten) : Boundary3D(wkt, layername, attributes, pid) { _heightref = heightref; - _threshold_outliers = threshold_outliers; + _filter_outliers = filter_outliers; + _flatten = flatten; } TopoClass Road::get_class() { @@ -57,7 +59,9 @@ bool Road::add_elevation_point(Point2 &p, double z, float radius, int lasclass) bool Road::lift() { lift_each_boundary_vertices(_heightref); - detect_outliers(true); + if (_filter_outliers || _flatten) { + detect_outliers(_flatten); + } return true; } diff --git a/src/Road.h b/src/Road.h index e07b2f4d..6fef77c2 100644 --- a/src/Road.h +++ b/src/Road.h @@ -33,7 +33,7 @@ class Road: public Boundary3D { public: - Road(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref, int outlier_threshold); + Road(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref, bool filter_outliers, bool flatten); bool lift(); bool add_elevation_point(Point2 &p, double z, float radius, int lasclass); void get_citygml(std::wostream& of); @@ -45,7 +45,8 @@ class Road: public Boundary3D { bool is_hard(); private: static float _heightref; - static int _threshold_outliers; + static bool _filter_outliers; + static bool _flatten; }; #endif /* Road_h */ diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index 25b66456..772319ff 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -1119,7 +1119,7 @@ void Boundary3D::smooth_boundary(int passes) { } } -void Boundary3D::detect_outliers(bool replace_all){ +void Boundary3D::detect_outliers(bool flatten){ //-- gather all rings std::vector rings; rings.push_back(_p2->outer()); @@ -1176,9 +1176,6 @@ void Boundary3D::detect_outliers(bool replace_all){ se = 1.96 * stdev; } - if (_id == "O0121.13b76aa29e794ec299a4dca4797c2144\n") { - std::cout << ""; - } // Calculate the maximum residual auto max = std::max_element(absResiduals.begin(), absResiduals.end()); // remove outlier if larger then 2*standard deviation @@ -1202,7 +1199,7 @@ void Boundary3D::detect_outliers(bool replace_all){ // get the new values based on the coeffs of the lase equation std::vector correctedvalues = polyval3d(x, y, coeffs); - if (replace_all) { + if (flatten) { for (int i = 0; i < ring.size(); i++) { _p2z[ringi][i] = correctedvalues[i]; } diff --git a/src/main.cpp b/src/main.cpp index d9050825..96483952 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -260,8 +260,17 @@ int main(int argc, const char * argv[]) { std::string height = n["Road"]["height"].as(); map3d.set_road_heightref(std::stof(height.substr(height.find_first_of("-") + 1)) / 100); } - if (n["Road"]["threshold_outliers"]) { - map3d.set_road_threshold_outliers(n["Road"]["threshold_outliers"].as()); + if (n["Road"]["filter_outliers"]) { + if (n["Road"]["filter_outliers"].as() == "true") + map3d.set_road_filter_outliers(true); + else + map3d.set_road_filter_outliers(false); + } + if (n["Road"]["flatten"]) { + if (n["Road"]["flatten"].as() == "true") + map3d.set_road_flatten(true); + else + map3d.set_road_flatten(false); } YAML::Node tmp = n["Road"]["use_LAS_classes"]; for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) @@ -281,6 +290,12 @@ int main(int argc, const char * argv[]) { std::string height = n["Bridge/Overpass"]["height"].as(); map3d.set_bridge_heightref(std::stof(height.substr(height.find_first_of("-") + 1)) / 100); } + if (n["Bridge/Overpass"]["flatten"]) { + if (n["Bridge/Overpass"]["flatten"].as() == "true") + map3d.set_bridge_flatten(true); + else + map3d.set_bridge_flatten(false); + } YAML::Node tmp = n["Bridge/Overpass"]["use_LAS_classes"]; for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) map3d.add_allowed_las_class(LAS_BRIDGE, it2->as()); @@ -890,10 +905,18 @@ bool validate_yaml(const char* arg, std::set& allowedFeatures) { std::cerr << "\tOption 'Road.height' invalid; must be 'percentile-XX'.\n"; } } - if (n["Road"]["threshold_outliers"]) { - if (is_string_integer(n["Road"]["threshold_outliers"].as()) == false) { + if (n["Road"]["filter_outliers"]) { + std::string s = n["Road"]["filter_outliers"].as(); + if ((s != "true") && (s != "false")) { + wentgood = false; + std::cerr << "\tOption 'Road.filter_outliers' invalid; must be 'true' or 'false'.\n"; + } + } + if (n["Road"]["flatten"]) { + std::string s = n["Road"]["flatten"].as(); + if ((s != "true") && (s != "false")) { wentgood = false; - std::cerr << "\tOption 'Road.threshold_outliers' invalid; must be an integer.\n"; + std::cerr << "\tOption 'Road.flatten' invalid; must be 'true' or 'false'.\n"; } } if (n["Road"]["use_LAS_classes"]) { @@ -943,6 +966,13 @@ bool validate_yaml(const char* arg, std::set& allowedFeatures) { } } } + if (n["Bridge/Overpass"]["flatten"]) { + std::string s = n["Bridge/Overpass"]["flatten"].as(); + if ((s != "true") && (s != "false")) { + wentgood = false; + std::cerr << "\tOption 'Bridge/Overpass.flatten' invalid; must be 'true' or 'false'.\n"; + } + } } } From 4fb8c628b1d097d982bb62d1edb85f130579a1b8 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Mon, 13 Aug 2018 13:26:33 +0200 Subject: [PATCH 37/45] add option use_las_classes_within for each topo class --- src/Bridge.cpp | 6 +-- src/Bridge.h | 2 +- src/Building.cpp | 19 ++++---- src/Building.h | 2 +- src/Forest.cpp | 4 +- src/Forest.h | 2 +- src/Map3d.cpp | 34 +++++++++++++- src/Map3d.h | 2 + src/Road.cpp | 13 +---- src/Road.h | 2 +- src/Separation.cpp | 5 +- src/Separation.h | 2 +- src/Terrain.cpp | 4 +- src/Terrain.h | 2 +- src/TopoFeature.cpp | 29 +++++++----- src/TopoFeature.h | 8 ++-- src/Water.cpp | 10 +--- src/Water.h | 2 +- src/main.cpp | 112 ++++++++++++++++++++++++++++++++++++++++++++ 19 files changed, 199 insertions(+), 61 deletions(-) diff --git a/src/Bridge.cpp b/src/Bridge.cpp index 2ad1c32c..84339dae 100644 --- a/src/Bridge.cpp +++ b/src/Bridge.cpp @@ -50,9 +50,9 @@ std::string Bridge::get_mtl() { return "usemtl Bridge"; } -bool Bridge::add_elevation_point(Point2 &p, double z, float radius, int lasclass) { - if (point_in_polygon(p, *(_p2))) { - Boundary3D::add_elevation_point(p, z, radius, lasclass); +bool Bridge::add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within) { + if (!within || (within && point_in_polygon(p, *(_p2)))) { + return Boundary3D::add_elevation_point(p, z, radius, lasclass, within); } return true; } diff --git a/src/Bridge.h b/src/Bridge.h index 394c3e3e..83bc348d 100644 --- a/src/Bridge.h +++ b/src/Bridge.h @@ -36,7 +36,7 @@ class Bridge: public Boundary3D { Bridge(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref, bool flatten); bool lift(); - bool add_elevation_point(Point2 &p, double z, float radius, int lasclass); + bool add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within); void get_citygml(std::wostream& of); void get_citygml_imgeo(std::wostream& of); void get_cityjson(nlohmann::json& j, std::unordered_map &dPts); diff --git a/src/Building.cpp b/src/Building.cpp index fba12bc7..132ec256 100644 --- a/src/Building.cpp +++ b/src/Building.cpp @@ -107,14 +107,17 @@ bool Building::lift() { return true; } -bool Building::add_elevation_point(Point2 &p, double z, float radius, int lasclass) { - if (within_range(p, *(_p2), radius)) { - int zcm = int(z * 100); - if ( (_las_classes_roof.empty() == true) || (_las_classes_roof.count(lasclass) > 0) ) { - _zvaluesinside.push_back(zcm); - } - if ( (_las_classes_ground.empty() == true) || (_las_classes_ground.count(lasclass) > 0) ) { - _zvaluesground.push_back(zcm); +bool Building::add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within) { + // if within then a point must lay within the polygon, otherwise add + if (!within || (within && point_in_polygon(p, *(_p2)))) { + if (within_range(p, *(_p2), radius)) { + int zcm = int(z * 100); + if ((_las_classes_roof.empty() == true) || (_las_classes_roof.count(lasclass) > 0)) { + _zvaluesinside.push_back(zcm); + } + if ((_las_classes_ground.empty() == true) || (_las_classes_ground.count(lasclass) > 0)) { + _zvaluesground.push_back(zcm); + } } } return true; diff --git a/src/Building.h b/src/Building.h index cbbe3f03..ac90ba56 100644 --- a/src/Building.h +++ b/src/Building.h @@ -35,7 +35,7 @@ class Building: public Flat { public: Building(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref_top, float heightref_base, bool building_triangulate, bool building_include_floor, bool building_inner_walls); bool lift(); - bool add_elevation_point(Point2 &p, double z, float radius, int lasclass); + bool add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within); void construct_building_walls(const NodeColumn& nc); void get_obj(std::unordered_map< std::string, unsigned long > &dPts, int lod, std::string mtl, std::string &fs); void get_citygml(std::wostream& of); diff --git a/src/Forest.cpp b/src/Forest.cpp index 6736d2c6..3ccdd48f 100644 --- a/src/Forest.cpp +++ b/src/Forest.cpp @@ -44,8 +44,8 @@ std::string Forest::get_mtl() { return "usemtl Forest"; } -bool Forest::add_elevation_point(Point2 &p, double z, float radius, int lasclass) { - return TIN::add_elevation_point(p, z, radius, lasclass); +bool Forest::add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within) { + return TIN::add_elevation_point(p, z, radius, lasclass, within); } bool Forest::lift() { diff --git a/src/Forest.h b/src/Forest.h index ce220586..bbd9ec9d 100644 --- a/src/Forest.h +++ b/src/Forest.h @@ -35,7 +35,7 @@ class Forest: public TIN { public: Forest(char *wkt, std::string layername, AttributeMap attributes, std::string pid, int simplification, double simplification_tinsimp, float innerbuffer); bool lift(); - bool add_elevation_point(Point2 &p, double z, float radius, int lasclass); + bool add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within); void get_citygml(std::wostream& of); void get_citygml_imgeo(std::wostream& of); void get_cityjson(nlohmann::json& j, std::unordered_map &dPts); diff --git a/src/Map3d.cpp b/src/Map3d.cpp index adc95065..151f1ce2 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -638,6 +638,7 @@ void Map3d::add_elevation_point(liblas::Point const& laspt) { int c = laspt.GetClassification().GetClass(); bool bInsert = false; + bool bWithin = false; if (f->get_class() == BUILDING) { bInsert = true; radius = _building_radius_vertex_elevation; @@ -646,36 +647,60 @@ void Map3d::add_elevation_point(liblas::Point const& laspt) { if (_las_classes_allowed[LAS_TERRAIN].empty() || _las_classes_allowed[LAS_TERRAIN].count(c) > 0) { bInsert = true; } + if (_las_classes_allowed_within[LAS_TERRAIN].count(c) > 0) { + bInsert = true; + bWithin = true; + } } else if (f->get_class() == FOREST) { if (_las_classes_allowed[LAS_FOREST].empty() || _las_classes_allowed[LAS_FOREST].count(c) > 0) { bInsert = true; } + if (_las_classes_allowed_within[LAS_FOREST].count(c) > 0) { + bInsert = true; + bWithin = true; + } } else if (f->get_class() == ROAD) { if (_las_classes_allowed[LAS_ROAD].empty() || _las_classes_allowed[LAS_ROAD].count(c) > 0) { bInsert = true; } + if (_las_classes_allowed_within[LAS_ROAD].count(c) > 0) { + bInsert = true; + bWithin = true; + } } else if (f->get_class() == WATER) { if (_las_classes_allowed[LAS_WATER].empty() || _las_classes_allowed[LAS_WATER].count(c) > 0) { bInsert = true; } + if (_las_classes_allowed_within[LAS_WATER].count(c) > 0) { + bInsert = true; + bWithin = true; + } } else if (f->get_class() == SEPARATION) { if (_las_classes_allowed[LAS_SEPARATION].empty() || _las_classes_allowed[LAS_SEPARATION].count(c) > 0) { bInsert = true; } + if (_las_classes_allowed_within[LAS_SEPARATION].count(c) > 0) { + bInsert = true; + bWithin = true; + } } else if (f->get_class() == BRIDGE) { if (_las_classes_allowed[LAS_BRIDGE].empty() || _las_classes_allowed[LAS_BRIDGE].count(c) > 0) { bInsert = true; } + if (_las_classes_allowed_within[LAS_BRIDGE].count(c) > 0) { + bInsert = true; + bWithin = true; + } } if (bInsert == true) { //-- only insert if in the allowed LAS classes Point2 p(laspt.GetX(), laspt.GetY()); - f->add_elevation_point(p, laspt.GetZ(), radius, c); + f->add_elevation_point(p, laspt.GetZ(), radius, c, bWithin); } } } @@ -1402,7 +1427,7 @@ void Map3d::stitch_bridges() { std::vector ringis, pis; for (auto& f : _lsFeatures) { if (f->get_class() == BRIDGE && f->get_top_level() == false) { - // Make bridge face flat + // Make bridge face flattened Bridge* b = dynamic_cast(f); b->detect_outliers(b->get_flatten()); @@ -1860,3 +1885,8 @@ int Map3d::interpolate_height(TopoFeature* f, const Point2 &p, int prevringi, in void Map3d::add_allowed_las_class(AllowedLASTopo c, int i) { _las_classes_allowed[c].insert(i); } + +void Map3d::add_allowed_las_class_within(AllowedLASTopo c, int i) { + _las_classes_allowed_within[c].insert(i); +} + diff --git a/src/Map3d.h b/src/Map3d.h index ecfaac70..63953c4a 100644 --- a/src/Map3d.h +++ b/src/Map3d.h @@ -105,6 +105,7 @@ class Map3d { void set_requested_extent(double xmin, double ymin, double xmax, double ymax); void add_allowed_las_class(AllowedLASTopo c, int i); + void add_allowed_las_class_within(AllowedLASTopo c, int i); bool save_building_variables(); int interpolate_height(TopoFeature* f, const Point2 &p, int prevringi, int prevpi, int nextringi, int nextpi); @@ -136,6 +137,7 @@ class Map3d { //-- storing the LAS allowed for each TopoFeature std::array,NUM_ALLOWEDLASTOPO> _las_classes_allowed; + std::array,NUM_ALLOWEDLASTOPO> _las_classes_allowed_within; NodeColumn _nc; NodeColumn _nc_building_walls; diff --git a/src/Road.cpp b/src/Road.cpp index 4059a43c..97adfc83 100644 --- a/src/Road.cpp +++ b/src/Road.cpp @@ -52,17 +52,8 @@ std::string Road::get_mtl() { return "usemtl Road"; } -bool Road::add_elevation_point(Point2 &p, double z, float radius, int lasclass) { - if (lasclass == 1) { - if (point_in_polygon(p, *(_p2))) { - Boundary3D::add_elevation_point(p, z, radius, lasclass); - } - } - else { - Boundary3D::add_elevation_point(p, z, radius, lasclass); - } - - return true; +bool Road::add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within) { + return Boundary3D::add_elevation_point(p, z, radius, lasclass, within); } bool Road::lift() { diff --git a/src/Road.h b/src/Road.h index 6fef77c2..509f1749 100644 --- a/src/Road.h +++ b/src/Road.h @@ -35,7 +35,7 @@ class Road: public Boundary3D { public: Road(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref, bool filter_outliers, bool flatten); bool lift(); - bool add_elevation_point(Point2 &p, double z, float radius, int lasclass); + bool add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within); void get_citygml(std::wostream& of); void get_citygml_imgeo(std::wostream& of); void get_cityjson(nlohmann::json& j, std::unordered_map &dPts); diff --git a/src/Separation.cpp b/src/Separation.cpp index a60362f9..d6e610b5 100644 --- a/src/Separation.cpp +++ b/src/Separation.cpp @@ -48,9 +48,8 @@ std::string Separation::get_mtl() { return "usemtl Separation"; } -bool Separation::add_elevation_point(Point2 &p, double z, float radius, int lasclass) { - Boundary3D::add_elevation_point(p, z, radius, lasclass); - return true; +bool Separation::add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within) { + return Boundary3D::add_elevation_point(p, z, radius, lasclass, within); } bool Separation::lift() { diff --git a/src/Separation.h b/src/Separation.h index 61966341..8207afee 100644 --- a/src/Separation.h +++ b/src/Separation.h @@ -35,7 +35,7 @@ class Separation: public Boundary3D { public: Separation(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref); bool lift(); - bool add_elevation_point(Point2 &p, double z, float radius, int lasclass); + bool add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within); void get_citygml(std::wostream& of); void get_citygml_imgeo(std::wostream& of); void get_cityjson(nlohmann::json& j, std::unordered_map &dPts); diff --git a/src/Terrain.cpp b/src/Terrain.cpp index 2ff9286b..522ee634 100644 --- a/src/Terrain.cpp +++ b/src/Terrain.cpp @@ -45,8 +45,8 @@ std::string Terrain::get_mtl() { return "usemtl Terrain"; } -bool Terrain::add_elevation_point(Point2 &p, double z, float radius, int lasclass) { - return TIN::add_elevation_point(p, z, radius, lasclass); +bool Terrain::add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within) { + return TIN::add_elevation_point(p, z, radius, lasclass, within); } bool Terrain::lift() { diff --git a/src/Terrain.h b/src/Terrain.h index 6c01db0e..e995f739 100644 --- a/src/Terrain.h +++ b/src/Terrain.h @@ -35,7 +35,7 @@ class Terrain: public TIN { public: Terrain(char *wkt, std::string layername, AttributeMap attributes, std::string pid, int simplification, double simplification_tinsimp, float innerbuffer); bool lift(); - bool add_elevation_point(Point2 &p, double z, float radius, int lasclass); + bool add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within); void get_citygml(std::wostream& of); void get_cityjson(nlohmann::json& j, std::unordered_map &dPts); void get_citygml_imgeo(std::wostream& of); diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index 772319ff..c9876c91 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -1062,11 +1062,14 @@ int Flat::get_number_vertices() { return (int(_vertices.size()) + int(_vertices_vw.size())); } -bool Flat::add_elevation_point(Point2 &p, double z, float radius, int lasclass) { - if (within_range(p, *(_p2), radius)) { - int zcm = int(z * 100); - //-- 1. assign to polygon since within the threshold value (buffering of polygon) - _zvaluesinside.push_back(zcm); +bool Flat::add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within) { + // if within then a point must lay within the polygon, otherwise add + if (!within || (within && point_in_polygon(p, *(_p2)))) { + if (within_range(p, *(_p2), radius)) { + int zcm = int(z * 100); + //-- 1. assign to polygon since within the threshold value (buffering of polygon) + _zvaluesinside.push_back(zcm); + } } return true; } @@ -1096,9 +1099,11 @@ int Boundary3D::get_number_vertices() { return (int(_vertices.size()) + int(_vertices_vw.size())); } -bool Boundary3D::add_elevation_point(Point2 &p, double z, float radius, int lasclass) { - // no need for checking for point-in-polygon since only points in range of the vertices are added - assign_elevation_to_vertex(p, z, radius); +bool Boundary3D::add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within) { + // if within then a point must lay within the polygon, otherwise add + if (!within || (within && point_in_polygon(p, *(_p2)))) { + assign_elevation_to_vertex(p, z, radius); + } return true; } @@ -1227,10 +1232,12 @@ int TIN::get_number_vertices() { return (int(_vertices.size()) + int(_vertices_vw.size())); } -bool TIN::add_elevation_point(Point2 &p, double z, float radius, int lasclass) { +bool TIN::add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within) { bool toadd = false; - // no need for checking for point-in-polygon since only points in range of the vertices are added - assign_elevation_to_vertex(p, z, radius); + // if within then a point must lay within the polygon, otherwise add + if (!within || (within && point_in_polygon(p, *(_p2)))) { + assign_elevation_to_vertex(p, z, radius); + } if (_simplification <= 1) toadd = true; else { diff --git a/src/TopoFeature.h b/src/TopoFeature.h index 31985332..fa922ead 100644 --- a/src/TopoFeature.h +++ b/src/TopoFeature.h @@ -41,7 +41,7 @@ class TopoFeature { virtual bool lift() = 0; virtual bool buildCDT(); - virtual bool add_elevation_point(Point2 &p, double z, float radius, int lasclass) = 0; + virtual bool add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within) = 0; virtual int get_number_vertices() = 0; virtual TopoClass get_class() = 0; virtual bool is_hard() = 0; @@ -113,7 +113,7 @@ class Flat: public TopoFeature { public: Flat(char *wkt, std::string layername, AttributeMap attributes, std::string pid); int get_number_vertices(); - bool add_elevation_point(Point2 &p, double z, float radius, int lasclass); + bool add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within); int get_height(); virtual TopoClass get_class() = 0; virtual bool is_hard() = 0; @@ -131,7 +131,7 @@ class Boundary3D: public TopoFeature { public: Boundary3D(char *wkt, std::string layername, AttributeMap attributes, std::string pid); int get_number_vertices(); - bool add_elevation_point(Point2 &p, double z, float radius, int lasclass); + bool add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within); virtual TopoClass get_class() = 0; virtual bool is_hard() = 0; virtual bool lift() = 0; @@ -149,7 +149,7 @@ class TIN: public TopoFeature { public: TIN(char *wkt, std::string layername, AttributeMap attributes, std::string pid, int simplification = 0, double simplification_tinsimp = 0, float innerbuffer = 0); int get_number_vertices(); - bool add_elevation_point(Point2 &p, double z, float radius, int lasclass); + bool add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within); virtual TopoClass get_class() = 0; virtual bool is_hard() = 0; virtual bool lift() = 0; diff --git a/src/Water.cpp b/src/Water.cpp index f91c28c4..65616a9c 100644 --- a/src/Water.cpp +++ b/src/Water.cpp @@ -48,14 +48,8 @@ bool Water::is_hard() { return true; } -bool Water::add_elevation_point(Point2 &p, double z, float radius, int lasclass) { - // Add elevation points within the polygon - if (point_in_polygon(p, *(_p2))) { - int zcm = int(z * 100); - //-- 1. assign to polygon since within the threshold value (buffering of polygon) - _zvaluesinside.push_back(zcm); - } - return true; +bool Water::add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within) { + return Flat::add_elevation_point(p, z, radius, lasclass, within); } bool Water::lift() { diff --git a/src/Water.h b/src/Water.h index ed4ae928..0ef21099 100644 --- a/src/Water.h +++ b/src/Water.h @@ -35,7 +35,7 @@ class Water: public Flat { public: Water(char *wkt, std::string layername, AttributeMap attributes, std::string pid, float heightref); bool lift(); - bool add_elevation_point(Point2 &p, double z, float radius, int lasclass); + bool add_elevation_point(Point2 &p, double z, float radius, int lasclass, bool within); void get_citygml(std::wostream& of); void get_cityjson(nlohmann::json& j, std::unordered_map &dPts); void get_citygml_imgeo(std::wostream& of); diff --git a/src/main.cpp b/src/main.cpp index 96483952..d0a1d98e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -192,6 +192,11 @@ int main(int argc, const char * argv[]) { YAML::Node tmp = n["Building"]["roof"]["use_LAS_classes"]; for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) map3d.add_allowed_las_class(LAS_BUILDING_ROOF, it2->as()); + if (n["Building"]["roof"]["use_LAS_classes_within"]) { + tmp = n["Building"]["roof"]["use_LAS_classes_within"]; + for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) + map3d.add_allowed_las_class_within(LAS_BUILDING_ROOF, it2->as()); + } } if (n["Building"]["ground"]) { if (n["Building"]["ground"]["height"]) { @@ -201,6 +206,11 @@ int main(int argc, const char * argv[]) { YAML::Node tmp = n["Building"]["ground"]["use_LAS_classes"]; for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) map3d.add_allowed_las_class(LAS_BUILDING_GROUND, it2->as()); + if (n["Building"]["ground"]["use_LAS_classes_within"]) { + tmp = n["Building"]["ground"]["use_LAS_classes_within"]; + for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) + map3d.add_allowed_las_class_within(LAS_BUILDING_GROUND, it2->as()); + } } if (n["Building"]["lod"]) { map3d.set_building_lod(n["Building"]["lod"].as()); @@ -234,6 +244,11 @@ int main(int argc, const char * argv[]) { YAML::Node tmp = n["Terrain"]["use_LAS_classes"]; for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) map3d.add_allowed_las_class(LAS_TERRAIN, it2->as()); + if (n["Terrain"]["use_LAS_classes_within"]) { + tmp = n["Terrain"]["use_LAS_classes_within"]; + for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) + map3d.add_allowed_las_class_within(LAS_TERRAIN, it2->as()); + } } if (n["Forest"]) { if (n["Forest"]["simplification"]) @@ -245,6 +260,11 @@ int main(int argc, const char * argv[]) { YAML::Node tmp = n["Forest"]["use_LAS_classes"]; for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) map3d.add_allowed_las_class(LAS_FOREST, it2->as()); + if (n["Forest"]["use_LAS_classes_within"]) { + tmp = n["Forest"]["use_LAS_classes_within"]; + for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) + map3d.add_allowed_las_class_within(LAS_FOREST, it2->as()); + } } if (n["Water"]) { if (n["Water"]["height"]) { @@ -254,6 +274,11 @@ int main(int argc, const char * argv[]) { YAML::Node tmp = n["Water"]["use_LAS_classes"]; for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) map3d.add_allowed_las_class(LAS_WATER, it2->as()); + if (n["Water"]["use_LAS_classes_within"]) { + tmp = n["Water"]["use_LAS_classes_within"]; + for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) + map3d.add_allowed_las_class_within(LAS_WATER, it2->as()); + } } if (n["Road"]) { if (n["Road"]["height"]) { @@ -275,6 +300,11 @@ int main(int argc, const char * argv[]) { YAML::Node tmp = n["Road"]["use_LAS_classes"]; for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) map3d.add_allowed_las_class(LAS_ROAD, it2->as()); + if (n["Road"]["use_LAS_classes_within"]) { + tmp = n["Road"]["use_LAS_classes_within"]; + for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) + map3d.add_allowed_las_class_within(LAS_ROAD, it2->as()); + } } if (n["Separation"]) { if (n["Separation"]["height"]) { @@ -284,6 +314,11 @@ int main(int argc, const char * argv[]) { YAML::Node tmp = n["Separation"]["use_LAS_classes"]; for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) map3d.add_allowed_las_class(LAS_SEPARATION, it2->as()); + if (n["Separation"]["use_LAS_classes_within"]) { + tmp = n["Separation"]["use_LAS_classes_within"]; + for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) + map3d.add_allowed_las_class_within(LAS_SEPARATION, it2->as()); + } } if (n["Bridge/Overpass"]) { if (n["Bridge/Overpass"]["height"]) { @@ -299,6 +334,11 @@ int main(int argc, const char * argv[]) { YAML::Node tmp = n["Bridge/Overpass"]["use_LAS_classes"]; for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) map3d.add_allowed_las_class(LAS_BRIDGE, it2->as()); + if (n["Bridge/Overpass"]["use_LAS_classes_within"]) { + tmp = n["Bridge/Overpass"]["use_LAS_classes_within"]; + for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) + map3d.add_allowed_las_class_within(LAS_BRIDGE, it2->as()); + } } } @@ -759,6 +799,15 @@ bool validate_yaml(const char* arg, std::set& allowedFeatures) { } } } + if (n["Building"]["roof"]["use_LAS_classes_within"]) { + YAML::Node tmp = n["Building"]["roof"]["use_LAS_classes_within"]; + for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) { + if (is_string_integer(it2->as()) == false) { + wentgood = false; + std::cerr << "\tOption 'Building.roof.use_LAS_classes_within' invalid; must be an integer.\n"; + } + } + } } if (n["Building"]["ground"]) { if (n["Building"]["ground"]["height"]) { @@ -778,6 +827,15 @@ bool validate_yaml(const char* arg, std::set& allowedFeatures) { } } } + if (n["Building"]["ground"]["use_LAS_classes_within"]) { + YAML::Node tmp = n["Building"]["ground"]["use_LAS_classes_within"]; + for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) { + if (is_string_integer(it2->as()) == false) { + wentgood = false; + std::cerr << "\tOption 'Building.ground.use_LAS_classes_within' invalid; must be an integer.\n"; + } + } + } } if (n["Building"]["lod"]) { if (is_string_integer(n["Building"]["lod"].as(), 0, 1) == false) { @@ -840,6 +898,15 @@ bool validate_yaml(const char* arg, std::set& allowedFeatures) { std::cerr << "\tOption 'Terrain.use_LAS_classes' invalid; must be an integer.\n"; } } + } + if (n["Terrain"]["use_LAS_classes_within"]) { + YAML::Node tmp = n["Terrain"]["use_LAS_classes_within"]; + for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) { + if (is_string_integer(it2->as()) == false) { + wentgood = false; + std::cerr << "\tOption 'Terrain.use_LAS_classes_within' invalid; must be an integer.\n"; + } + } } } if (n["Forest"]) { @@ -875,6 +942,15 @@ bool validate_yaml(const char* arg, std::set& allowedFeatures) { std::cerr << "\tOption 'Forest.use_LAS_classes' invalid; must be an integer.\n"; } } + } + if (n["Forest"]["use_LAS_classes_within"]) { + YAML::Node tmp = n["Forest"]["use_LAS_classes_within"]; + for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) { + if (is_string_integer(it2->as()) == false) { + wentgood = false; + std::cerr << "\tOption 'Forest.use_LAS_classes_within' invalid; must be an integer.\n"; + } + } } } if (n["Water"]) { @@ -894,6 +970,15 @@ bool validate_yaml(const char* arg, std::set& allowedFeatures) { std::cerr << "\tOption 'Water.use_LAS_classes' invalid; must be an integer.\n"; } } + } + if (n["Water"]["use_LAS_classes_within"]) { + YAML::Node tmp = n["Water"]["use_LAS_classes_within"]; + for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) { + if (is_string_integer(it2->as()) == false) { + wentgood = false; + std::cerr << "\tOption 'Water.use_LAS_classes_within' invalid; must be an integer.\n"; + } + } } } if (n["Road"]) { @@ -928,6 +1013,15 @@ bool validate_yaml(const char* arg, std::set& allowedFeatures) { } } } + if (n["Road"]["use_LAS_classes_within"]) { + YAML::Node tmp = n["Road"]["use_LAS_classes_within"]; + for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) { + if (is_string_integer(it2->as()) == false) { + wentgood = false; + std::cerr << "\tOption 'Road.use_LAS_classes_within' invalid; must be an integer.\n"; + } + } + } } if (n["Separation"]) { if (n["Separation"]["height"]) { @@ -947,6 +1041,15 @@ bool validate_yaml(const char* arg, std::set& allowedFeatures) { } } } + if (n["Separation"]["use_LAS_classes_within"]) { + YAML::Node tmp = n["Separation"]["use_LAS_classes_within"]; + for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) { + if (is_string_integer(it2->as()) == false) { + wentgood = false; + std::cerr << "\tOption 'Separation.use_LAS_classes_within' invalid; must be an integer.\n"; + } + } + } } if (n["Bridge/Overpass"]) { if (n["Bridge/Overpass"]["height"]) { @@ -966,6 +1069,15 @@ bool validate_yaml(const char* arg, std::set& allowedFeatures) { } } } + if (n["Bridge/Overpass"]["use_LAS_classes_within"]) { + YAML::Node tmp = n["Bridge/Overpass"]["use_LAS_classes_within"]; + for (auto it2 = tmp.begin(); it2 != tmp.end(); ++it2) { + if (is_string_integer(it2->as()) == false) { + wentgood = false; + std::cerr << "\tOption 'Bridge/Overpass.use_LAS_classes_within' invalid; must be an integer.\n"; + } + } + } if (n["Bridge/Overpass"]["flatten"]) { std::string s = n["Bridge/Overpass"]["flatten"].as(); if ((s != "true") && (s != "false")) { From 35710ca5ab69ca33bd9cb5e5785f0ec5f859f41b Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Tue, 14 Aug 2018 16:48:27 +0200 Subject: [PATCH 38/45] fix building roof at -99.99 if no points within building, use ground pts --- src/Building.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Building.cpp b/src/Building.cpp index 132ec256..039543d2 100644 --- a/src/Building.cpp +++ b/src/Building.cpp @@ -102,6 +102,10 @@ bool Building::lift() { else { _height_base = -9999; } + if (_zvaluesinside.empty() && _zvaluesground.empty() == false) { + // if no points inside the building use the ground points to set height + _zvaluesinside = _zvaluesground; + } //-- for the roof Flat::lift_percentile(_heightref_top); return true; From 5f8e997f07d4ee71d8227bca39e89b97cdac3f3c Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Wed, 15 Aug 2018 15:27:20 +0200 Subject: [PATCH 39/45] test --- src/Map3d.cpp | 29 ++++++++++++++++++++--------- src/TopoFeature.cpp | 7 ++++++- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/Map3d.cpp b/src/Map3d.cpp index 151f1ce2..4927d8ec 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -1171,13 +1171,20 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st ringi, pi)); for (auto& fadj : star) { - if (std::get<0>(fadj)->get_class() != BRIDGE) { + if (std::get<0>(fadj)->get_class() != BRIDGE && std::get<0>(fadj)->get_class() != BUILDING) { zstar.push_back(std::make_tuple( std::get<0>(fadj)->get_vertex_elevation(std::get<1>(fadj), std::get<2>(fadj)), std::get<0>(fadj), std::get<1>(fadj), std::get<2>(fadj))); } + else if (std::get<0>(fadj)->get_class() == BUILDING) { + zstar.push_back(std::make_tuple( + dynamic_cast(std::get<0>(fadj))->get_height_base(), + std::get<0>(fadj), + std::get<1>(fadj), + std::get<2>(fadj))); + } // This it for adjacent objects at the corners of the bridge where the adjacent features need extra VW at the height jump. else { f->add_vertical_wall(); @@ -1346,27 +1353,31 @@ void Map3d::stitch_jumpedge(TopoFeature* f1, int ringi1, int pi1, TopoFeature* f } } else if (f1->get_class() == BUILDING) { + int f1base = dynamic_cast(f1)->get_height_base(); if (f2->get_class() != WATER) { - f2->set_vertex_elevation(ringi2, pi2, dynamic_cast(f1)->get_height_base()); + f2->set_vertex_elevation(ringi2, pi2, f1base); } else { - //- keep water flat, add the water height to the nc - _nc_building_walls[key_bucket].push_back(f2z); + //- keep water flat, add the water height and the building base height to the nc + _nc[key_bucket].push_back(f2z); + _nc[key_bucket].push_back(f1base); } //- expect a building to always be heighest adjacent feature - _nc_building_walls[key_bucket].push_back(dynamic_cast(f1)->get_height_base()); + _nc_building_walls[key_bucket].push_back(f1base); _nc_building_walls[key_bucket].push_back(f1z); } else { //-- f2 is Building + int f2base = dynamic_cast(f2)->get_height_base(); if (f1->get_class() != WATER) { - f1->set_vertex_elevation(ringi1, pi1, dynamic_cast(f2)->get_height_base()); + f1->set_vertex_elevation(ringi1, pi1, f2base); } else { - //- keep water flat, add the water height to the nc - _nc_building_walls[key_bucket].push_back(f1z); + //- keep water flat, add the water height and the building base height to the nc + _nc[key_bucket].push_back(f1z); + _nc[key_bucket].push_back(f2base); } //- expect a building to always be heighest adjacent feature - _nc_building_walls[key_bucket].push_back(dynamic_cast(f2)->get_height_base()); + _nc_building_walls[key_bucket].push_back(f2base); _nc_building_walls[key_bucket].push_back(f2z); } } diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index c9876c91..8d772168 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -472,6 +472,9 @@ void TopoFeature::construct_vertical_walls(const NodeColumn& nc) { for (Ring2& ring : therings) { ringi++; for (int ai = 0; ai < ring.size(); ai++) { + if (_id == "b71ed2cc7-0db1-11e6-b093-bd9bfd5080ba" && ai == 34) { + std::cout << ""; + } //-- Point a a = ring[ai]; //-- find Point b @@ -519,7 +522,9 @@ void TopoFeature::construct_vertical_walls(const NodeColumn& nc) { continue; //-- Make exeption for bridges, they have vw's from bottom up, swap. Also skip if adjacent feature is bridge, vw is then created by bridge - if (this->get_class() == BRIDGE && this->get_top_level()) { + //-- Make exeption for buildings adjacent to water, they have vw's from bottom up. + if (this->get_class() == BRIDGE && this->get_top_level() || + this->get_class() == WATER && fadj->get_class() == BUILDING) { //-- find the height of the vertex in the node column std::vector::const_iterator sait, eait, sbit, ebit; sait = std::find(anc.begin(), anc.end(), az); From aab5230f170360b4258fabf1aed852e65c9b6426 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Wed, 15 Aug 2018 16:14:46 +0200 Subject: [PATCH 40/45] add vw to water if building is adjacent --- src/Map3d.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Map3d.cpp b/src/Map3d.cpp index 4927d8ec..75e7837c 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -1163,6 +1163,9 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st } //-- degree of vertex >= 3: more complex cases else if (star.size() > 1) { + if (f->get_id() == "b71ed2cc7-0db1-11e6-b093-bd9bfd5080ba" && pi == 34) { + std::cout << ""; + } //-- collect all elevations std::vector< std::tuple< int, TopoFeature*, int, int > > zstar; zstar.push_back(std::make_tuple( @@ -1229,11 +1232,15 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st _nc_building_walls[key_bucket].push_back(hfloor); } - int hroof = std::get<0>(zstar[i]); + int hroof = dynamic_cast(std::get<1>(zstar[i]))->get_height(); if (std::find(_nc_building_walls[key_bucket].begin(), _nc_building_walls[key_bucket].end(), hroof) == _nc_building_walls[key_bucket].end()) { _nc_building_walls[key_bucket].push_back(hroof); } } + // add vw to water since it might be lower then the building floor + if (water != -1) { + std::get<1>(zstar[water])->add_vertical_wall(); + } int baseheight = dynamic_cast(std::get<1>(zstar[building]))->get_height_base(); for (auto& each : zstar) { if (std::get<1>(each)->get_class() != BUILDING && std::get<1>(each)->get_class() != WATER) { From d26c5b9f44f21834923031f14d335b54f6bd81ca Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Wed, 15 Aug 2018 16:20:06 +0200 Subject: [PATCH 41/45] fix condition vw for water&&building --- src/TopoFeature.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index 8d772168..e2ae9f03 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -523,8 +523,8 @@ void TopoFeature::construct_vertical_walls(const NodeColumn& nc) { //-- Make exeption for bridges, they have vw's from bottom up, swap. Also skip if adjacent feature is bridge, vw is then created by bridge //-- Make exeption for buildings adjacent to water, they have vw's from bottom up. - if (this->get_class() == BRIDGE && this->get_top_level() || - this->get_class() == WATER && fadj->get_class() == BUILDING) { + if ((this->get_class() == BRIDGE && this->get_top_level()) || + (this->get_class() == WATER && fadj->get_class() == BUILDING)) { //-- find the height of the vertex in the node column std::vector::const_iterator sait, eait, sbit, ebit; sait = std::find(anc.begin(), anc.end(), az); From d27a29af4c242476bdbebf81b0980dec6dbfd2fe Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Wed, 15 Aug 2018 16:44:06 +0200 Subject: [PATCH 42/45] fix building height reset to ground --- src/Map3d.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Map3d.cpp b/src/Map3d.cpp index 75e7837c..0db65ce5 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -1163,9 +1163,6 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st } //-- degree of vertex >= 3: more complex cases else if (star.size() > 1) { - if (f->get_id() == "b71ed2cc7-0db1-11e6-b093-bd9bfd5080ba" && pi == 34) { - std::cout << ""; - } //-- collect all elevations std::vector< std::tuple< int, TopoFeature*, int, int > > zstar; zstar.push_back(std::make_tuple( @@ -1329,7 +1326,9 @@ void Map3d::stitch_one_vertex(TopoFeature* f, int ringi, int pi, std::vector< st int tmph = -99999; for (auto& each : zstar) { int h = std::get<0>(each); - std::get<1>(each)->set_vertex_elevation(std::get<2>(each), std::get<3>(each), h); + if (std::get<1>(each)->get_class() != BUILDING) { + std::get<1>(each)->set_vertex_elevation(std::get<2>(each), std::get<3>(each), h); + } if (h != tmph) { //-- not to repeat the same height _nc[key_bucket].push_back(h); tmph = h; From 38abc24f92fb6fe1e979765a89e8b3e059e9ecb5 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Wed, 15 Aug 2018 17:42:29 +0200 Subject: [PATCH 43/45] fix wrong iteration of vw at water up to building --- src/TopoFeature.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index e2ae9f03..0ba3758e 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -472,9 +472,6 @@ void TopoFeature::construct_vertical_walls(const NodeColumn& nc) { for (Ring2& ring : therings) { ringi++; for (int ai = 0; ai < ring.size(); ai++) { - if (_id == "b71ed2cc7-0db1-11e6-b093-bd9bfd5080ba" && ai == 34) { - std::cout << ""; - } //-- Point a a = ring[ai]; //-- find Point b @@ -532,6 +529,17 @@ void TopoFeature::construct_vertical_walls(const NodeColumn& nc) { sbit = std::find(bnc.begin(), bnc.end(), bz); ebit = std::find(bnc.begin(), bnc.end(), fadj_bz); + if (this->get_class() == WATER && fadj->get_class() == BUILDING) { + if (eait == anc.end()) { + eait--; + fadj_az = *eait; + } + if (ebit == bnc.end()) { + ebit--; + fadj_bz = *ebit; + } + } + //-- iterate to triangulate while ((sbit != ebit) && (sbit != bnc.end()) && ((sbit + 1) != bnc.end())) { Point3 p; From 212c68ea31baf54fea681f785d24eeea10c6ad98 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Thu, 16 Aug 2018 11:14:06 +0200 Subject: [PATCH 44/45] write building floors for gdal multipolygon output --- src/Building.cpp | 92 ++++++++++++++++++++++++++++++++++++++++++++- src/TopoFeature.cpp | 6 +-- src/TopoFeature.h | 2 +- 3 files changed, 92 insertions(+), 8 deletions(-) diff --git a/src/Building.cpp b/src/Building.cpp index 039543d2..c21c861d 100644 --- a/src/Building.cpp +++ b/src/Building.cpp @@ -561,5 +561,93 @@ void Building::get_imgeo_nummeraanduiding(std::wostream& of) { } bool Building::get_shape(OGRLayer* layer, bool writeAttributes, AttributeMap extraAttributes) { - return TopoFeature::get_multipolygon_features(layer, "Building", writeAttributes, extraAttributes, true, this->get_height_base(), this->get_height()); -} + OGRFeatureDefn *featureDefn = layer->GetLayerDefn(); + OGRFeature *feature = OGRFeature::CreateFeature(featureDefn); + OGRMultiPolygon multipolygon = OGRMultiPolygon(); + Point3 p; + + //-- add all triangles to the layer + for (auto& t : _triangles) { + OGRPolygon polygon = OGRPolygon(); + OGRLinearRing ring = OGRLinearRing(); + + p = _vertices[t.v0].first; + ring.addPoint(p.get<0>(), p.get<1>(), p.get<2>()); + p = _vertices[t.v1].first; + ring.addPoint(p.get<0>(), p.get<1>(), p.get<2>()); + p = _vertices[t.v2].first; + ring.addPoint(p.get<0>(), p.get<1>(), p.get<2>()); + + ring.closeRings(); + polygon.addRing(&ring); + multipolygon.addGeometry(&polygon); + } + + //-- add all vertical wall triangles to the layer + for (auto& t : _triangles_vw) { + OGRPolygon polygon = OGRPolygon(); + OGRLinearRing ring = OGRLinearRing(); + + p = _vertices_vw[t.v0].first; + ring.addPoint(p.get<0>(), p.get<1>(), p.get<2>()); + p = _vertices_vw[t.v1].first; + ring.addPoint(p.get<0>(), p.get<1>(), p.get<2>()); + p = _vertices_vw[t.v2].first; + ring.addPoint(p.get<0>(), p.get<1>(), p.get<2>()); + + ring.closeRings(); + polygon.addRing(&ring); + multipolygon.addGeometry(&polygon); + } + + //-- add all floor triangles to the layer + if (_building_include_floor) { + float z = z_to_float(this->get_height_base()); + for (auto& t : _triangles) { + OGRPolygon polygon = OGRPolygon(); + OGRLinearRing ring = OGRLinearRing(); + + // reverse orientation for floor polygon, v0-v2-v1 instead of v0-v1-v2. + p = _vertices[t.v0].first; + ring.addPoint(p.get<0>(), p.get<1>(), z); + p = _vertices[t.v2].first; + ring.addPoint(p.get<0>(), p.get<1>(), z); + p = _vertices[t.v1].first; + ring.addPoint(p.get<0>(), p.get<1>(), z); + + ring.closeRings(); + polygon.addRing(&ring); + multipolygon.addGeometry(&polygon); + } + } + + feature->SetGeometry(&multipolygon); + // perform extra character encoding for gdal. + const char* idcpl = CPLRecode(this->get_id().c_str(), "", CPL_ENC_UTF8); + feature->SetField("3df_id", idcpl); + // perform extra character encoding for gdal. + const char* classcpl = CPLRecode("Building", "", CPL_ENC_UTF8); + feature->SetField("3df_class", classcpl); + feature->SetField("baseheight", z_to_float(this->get_height_base())); + feature->SetField("roofheight", z_to_float(this->get_height())); + if (writeAttributes) { + for (auto attr : _attributes) { + if (!(attr.second.first == OFTDateTime && attr.second.second == "0000/00/00 00:00:00")) { + // perform extra character encoding for gdal. + const char* attrcpl = CPLRecode(attr.second.second.c_str(), "", CPL_ENC_UTF8); + feature->SetField(attr.first.c_str(), attrcpl); + } + } + for (auto attr : extraAttributes) { + // perform extra character encoding for gdal. + const char* attrcpl = CPLRecode(attr.second.second.c_str(), "", CPL_ENC_UTF8); + feature->SetField(attr.first.c_str(), attrcpl); + } + } + if (layer->CreateFeature(feature) != OGRERR_NONE) { + std::cerr << "Failed to create feature " << this->get_id() << ".\n"; + return false; + } + OGRFeature::DestroyFeature(feature); + return true; +} \ No newline at end of file diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index 0ba3758e..ae12b623 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -289,7 +289,7 @@ void TopoFeature::get_citygml_attributes(std::wostream& of, AttributeMap attribu } } -bool TopoFeature::get_multipolygon_features(OGRLayer* layer, std::string className, bool writeAttributes, AttributeMap extraAttributes, bool writeHeights, int height_base, int height) { +bool TopoFeature::get_multipolygon_features(OGRLayer* layer, std::string className, bool writeAttributes, AttributeMap extraAttributes) { OGRFeatureDefn *featureDefn = layer->GetLayerDefn(); OGRFeature *feature = OGRFeature::CreateFeature(featureDefn); OGRMultiPolygon multipolygon = OGRMultiPolygon(); @@ -336,10 +336,6 @@ bool TopoFeature::get_multipolygon_features(OGRLayer* layer, std::string classNa // perform extra character encoding for gdal. const char* classcpl = CPLRecode(className.c_str(), "", CPL_ENC_UTF8); feature->SetField("3df_class", classcpl); - if (writeHeights) { - feature->SetField("baseheight", z_to_float(height_base)); - feature->SetField("roofheight", z_to_float(height)); - } if (writeAttributes) { for (auto attr : _attributes) { if (!(attr.second.first == OFTDateTime && attr.second.second == "0000/00/00 00:00:00")) { diff --git a/src/TopoFeature.h b/src/TopoFeature.h index fa922ead..c7237aa2 100644 --- a/src/TopoFeature.h +++ b/src/TopoFeature.h @@ -71,7 +71,7 @@ class TopoFeature { bool has_vertical_walls(); void add_vertical_wall(); bool get_top_level(); - bool get_multipolygon_features(OGRLayer* layer, std::string className, bool writeAttributes, AttributeMap extraAttributes = AttributeMap(), bool writeHeights = false, int height_base = 0, int height = 0); + bool get_multipolygon_features(OGRLayer* layer, std::string className, bool writeAttributes, AttributeMap extraAttributes = AttributeMap()); void get_obj(std::unordered_map< std::string, unsigned long > &dPts, std::string mtl, std::string &fs); AttributeMap get_attributes(); void get_imgeo_object_info(std::wostream& of, std::string id); From 13cc19f5b9d44f81e31016b597e473ceed87a4f3 Mon Sep 17 00:00:00 2001 From: Tom Commandeur Date: Thu, 16 Aug 2018 11:17:36 +0200 Subject: [PATCH 45/45] make version 1.0! --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index d0a1d98e..258aef98 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -37,7 +37,7 @@ #include #include -std::string VERSION = "1.0-RC2"; +std::string VERSION = "1.0"; bool validate_yaml(const char* arg, std::set& allowedFeatures); int main(int argc, const char * argv[]);