diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 61fcd5ba..6ef47bae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,33 +16,33 @@ jobs: run: | sudo apt-get update sudo apt-get install libgmp-dev libmpfr-dev libboost-all-dev libeigen3-dev libomp-dev libgdal-dev - - name: Download CGAL + - name: Download CGAL 6.0 run: | - wget https://github.com/CGAL/cgal/releases/download/v5.6.1/CGAL-5.6.1-library.tar.xz -P ${{ github.workspace }} + wget https://github.com/CGAL/cgal/releases/download/v6.0/CGAL-6.0-library.tar.xz -P ${{ github.workspace }} cd ${{ github.workspace }} - tar -xvf CGAL-5.6.1-library.tar.xz + tar -xvf CGAL-6.0-library.tar.xz - name: Build run: | mkdir build && cd build - cmake .. -DCGAL_DIR=${{ github.workspace }}/CGAL-5.6.1 && make -j4 + cmake .. -DCGAL_DIR=${{ github.workspace }}/CGAL-6.0 && make -j4 - build_linux_2004: - runs-on: ubuntu-20.04 + build_linux_2204: + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - name: Install dependencies run: | sudo apt-get update sudo apt-get install libgmp-dev libmpfr-dev libboost-all-dev libeigen3-dev libomp-dev libgdal-dev - - name: Download CGAL + - name: Download CGAL 6.0 run: | - wget https://github.com/CGAL/cgal/releases/download/v5.6.1/CGAL-5.6.1-library.tar.xz -P ${{ github.workspace }} + wget https://github.com/CGAL/cgal/releases/download/v6.0/CGAL-6.0-library.tar.xz -P ${{ github.workspace }} cd ${{ github.workspace }} - tar -xvf CGAL-5.6.1-library.tar.xz + tar -xvf CGAL-6.0-library.tar.xz - name: Build run: | mkdir build && cd build - cmake .. -DCGAL_DIR=${{ github.workspace }}/CGAL-5.6.1 && make -j4 + cmake .. -DCGAL_DIR=${{ github.workspace }}/CGAL-6.0 && make -j4 build_macos: runs-on: macos-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a8ad507..6a6ac414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## [Unreleased] +### Changed +- Updated to CGAL 6 + ## [0.5.0] - 2024-07-29 - breaking changes ### Added - LoD2.2 and LoD1.3 reconstruction diff --git a/CMakeLists.txt b/CMakeLists.txt index 79470ed2..f9e605d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,10 @@ set(CMAKE_BUILD_TYPE "Release") # cmake_policy(SET CMP0003 NEW) #endif() +if(POLICY CMP0167) + cmake_policy(SET CMP0167 NEW) +endif() + if (MSVC) add_definitions(-DNOMINMAX) add_definitions("/EHsc") diff --git a/docker/city4cfd-build-base.dockerfile b/docker/city4cfd-build-base.dockerfile index feaa4fbe..1dd6bc38 100644 --- a/docker/city4cfd-build-base.dockerfile +++ b/docker/city4cfd-build-base.dockerfile @@ -7,11 +7,26 @@ LABEL org.opencontainers.image.description="Base image for building City4CFD" LABEL org.opencontainers.image.licenses="AGPL-3.0" LABEL org.opencontainers.image.url="https://github.com/tudelft3d/city4cfd" +# Install required packages and clean up RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ cmake \ libboost-all-dev \ - libcgal-dev \ + libgmp-dev \ + libmpfr-dev \ libeigen3-dev \ libomp-dev \ - libgdal-dev + libgdal-dev \ + wget \ + ca-certificates \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + && rm -rf /tmp/* + +# Download and extract CGAL +RUN wget https://github.com/CGAL/cgal/releases/download/v6.0/CGAL-6.0-library.tar.xz \ + && tar -xf CGAL-6.0-library.tar.xz -C /opt \ + && rm CGAL-6.0-library.tar.xz + +# Set environment variable for CGAL +ENV CGAL_DIR=/opt/CGAL-6.0 diff --git a/src/Building.cpp b/src/Building.cpp index 974b048a..0fcd8395 100644 --- a/src/Building.cpp +++ b/src/Building.cpp @@ -296,10 +296,10 @@ void Building::get_cityjson_info(nlohmann::json& b) const { void Building::get_cityjson_semantics(nlohmann::json& g) const { // Temp for checking CGAL mesh properties Face_property semantics; - bool foundProperty; - boost::tie(semantics, foundProperty) = m_mesh.property_map("f:semantics"); - // auto semantics = m_mesh.property_map("f:semantics"); - if (!foundProperty) throw city4cfd_error("Semantic property map not found!"); + auto semanticsMap = m_mesh.property_map("f:semantics"); + if (semanticsMap.has_value()) { + semantics = semanticsMap.value(); + } else throw city4cfd_error("Semantic property map not found!"); std::unordered_map surfaceId; surfaceId["RoofSurface"] = 0; g["semantics"]["surfaces"][0]["type"] = "RoofSurface"; diff --git a/src/Config.cpp b/src/Config.cpp index 279d5fc5..27d5289f 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -141,7 +141,7 @@ void Config::set_config(nlohmann::json& j) { //- Domain boundaries Config::get().set_region(domainBndConfig, "domain_bnd", j); // Define domain type if using BPG - if (domainBndConfig.type() == typeid(bool)) { + if (std::holds_alternative(domainBndConfig)) { if (j.contains("flow_direction")) flowDirection = Vector_2(j["flow_direction"][0], j["flow_direction"][1]); @@ -165,13 +165,13 @@ void Config::set_config(nlohmann::json& j) { } // Set domain side and top - if (domainBndConfig.type() == typeid(Polygon_2)) { - Polygon_2 poly = boost::get(domainBndConfig); + if (std::holds_alternative(domainBndConfig)) { + Polygon_2 poly = std::get(domainBndConfig); numSides = poly.size(); for (int i = 0; i < poly.size(); ++i) { outputSurfaces.emplace_back("Side_" + std::to_string(i % poly.size())); } - } else if (domainBndConfig.type() == typeid(double) || bpgDomainType != RECTANGLE) { + } else if (std::holds_alternative(domainBndConfig) || bpgDomainType != RECTANGLE) { outputSurfaces.emplace_back("Sides"); } else if (bpgDomainType == RECTANGLE) { // Expand output surfaces with front and back numSides = 4; @@ -327,7 +327,7 @@ void Config::set_config(nlohmann::json& j) { } //-- influRegion and domainBndConfig flow control -void Config::set_region(boost::variant& regionType, +void Config::set_region(std::variant& regionType, const std::string regionName, nlohmann::json& j) { if (j[regionName].is_string()) { // Search for GeoJSON polygon diff --git a/src/Config.h b/src/Config.h index 6d933f5b..8bfd8655 100644 --- a/src/Config.h +++ b/src/Config.h @@ -50,7 +50,7 @@ class Config { public: void validate(nlohmann::json& j); void set_config(nlohmann::json& j); - void set_region(boost::variant& regionType, + void set_region(std::variant& regionType, const std::string regionName, nlohmann::json& j); static void write_to_log(const std::string& msg); @@ -65,7 +65,7 @@ class Config { //-- Domain setup Point_2 pointOfInterest; double topHeight = 0.; - boost::variant domainBndConfig; + std::variant domainBndConfig; DomainType bpgDomainType; bool bpgBlockageRatioFlag = false; double bpgBlockageRatio = 0.03; @@ -134,7 +134,7 @@ class Config { //-- Struct for reconstruction regions (part of Buildings) struct ReconRegion{ - boost::variant influRegionConfig; + std::variant influRegionConfig; std::string lod; double bpgInfluExtra = 0.; bool importAdvantage = true; diff --git a/src/Map3d.cpp b/src/Map3d.cpp index 45e32c1d..6629f829 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -198,7 +198,7 @@ void Map3d::set_features() { for (auto& reconRegion : Config::get().reconRegions) { m_reconRegions.emplace_back(reconRegion); } - if (Config::get().domainBndConfig.type() == typeid(bool)) m_bndBPG = true; + if (std::holds_alternative(Config::get().domainBndConfig)) m_bndBPG = true; //-- Apply area filtering if (Config::get().minArea > 0.) { @@ -256,7 +256,7 @@ void Map3d::set_influ_region() { //-- Set the reconstruction (influence) regions --// double maxDim = -1.; // this works if there's one point of interest for (int i = 0; i < m_reconRegions.size(); ++i) { - if (m_reconRegions[i].m_reconSettings->influRegionConfig.type() == typeid(bool)) {// bool defines BPG request + if (std::holds_alternative(m_reconRegions[i].m_reconSettings->influRegionConfig)) {// bool defines BPG request std::cout << "INFO: Reconstruction region "<< i << " not defined in config. " << "Calculating with BPG." << std::endl; if (maxDim < 0.) @@ -264,7 +264,7 @@ void Map3d::set_influ_region() { else m_reconRegions[i].calc_influ_region_bpg(maxDim); } else - boost::apply_visitor(m_reconRegions[i], Config::get().reconRegions[i]->influRegionConfig); + std::visit(m_reconRegions[i], Config::get().reconRegions[i]->influRegionConfig); } // Check if regions get larger with increasing index for (int i = 0; i < m_reconRegions.size(); ++i) { @@ -303,7 +303,7 @@ void Map3d::set_bnd() { m_domainBnd.calc_bnd_bpg(m_reconRegions.back().get_bounding_region(), m_buildingsPtr); } else { //-- Define boundary region with values set in config - boost::apply_visitor(m_domainBnd, Config::get().domainBndConfig); + std::visit(m_domainBnd, Config::get().domainBndConfig); } this->bnd_sanity_check(); // Check if outer bnd is larger than the influ region diff --git a/src/PointCloud.cpp b/src/PointCloud.cpp index e1cb3439..01eb4d57 100644 --- a/src/PointCloud.cpp +++ b/src/PointCloud.cpp @@ -33,7 +33,6 @@ #include "Building.h" #include "Quadtree/Quadtree.h" -#include #include #include #include @@ -241,7 +240,7 @@ void PointCloud::buffer_flat_edges(const PolyFeaturesPtr& avgFeatures, typedef CGAL::Polygon_offset_builder_traits_2 OffsetBuilderTraits; typedef CGAL::Polygon_offset_builder_2 OffsetBuilder; - typedef boost::shared_ptr ContourPtr; + typedef std::shared_ptr ContourPtr; typedef std::vector ContourSequence; // get info using the original point cloud // std::vector offsets{0.001, 0.2}; @@ -252,7 +251,7 @@ void PointCloud::buffer_flat_edges(const PolyFeaturesPtr& avgFeatures, //for (auto& f: avgFeatures) { // MSVC doesn't like range loop with OMP auto& poly = avgFeatures[i]->get_poly().outer_boundary(); // set the frame - boost::optional margin = CGAL::compute_outer_frame_margin(poly.begin(), poly.end(), offsets.back()); + auto margin = CGAL::compute_outer_frame_margin(poly.begin(), poly.end(), offsets.back()); CGAL::Bbox_2 bbox = CGAL::bbox_2(poly.begin(), poly.end()); // Compute the boundaries of the frame double fxmin = bbox.xmin() - *margin; @@ -271,7 +270,7 @@ void PointCloud::buffer_flat_edges(const PolyFeaturesPtr& avgFeatures, poly.reverse_orientation(); ssb.enter_contour(poly.begin(), poly.end()); // Construct the skeleton - boost::shared_ptr ss = ssb.construct_skeleton(); + auto ss = ssb.construct_skeleton(); // Proceed only if the skeleton was correctly constructed. if (ss) { for (auto& offset: offsets) { diff --git a/src/PolyFeature.cpp b/src/PolyFeature.cpp index 402035f7..8a233066 100644 --- a/src/PolyFeature.cpp +++ b/src/PolyFeature.cpp @@ -220,7 +220,7 @@ bool PolyFeature::flatten_polygon_inner_points(const Point_set_3& pointCloud, bool& isNextToBuilding) { typedef CGAL::Straight_skeleton_2 Ss; - typedef boost::shared_ptr> PolygonPtrWH; + typedef std::shared_ptr> PolygonPtrWH; typedef std::vector PolygonPtrVectorWH; #ifdef CITY4CFD_POLYFEATURE_VERBOSE @@ -234,7 +234,7 @@ bool PolyFeature::flatten_polygon_inner_points(const Point_set_3& pointCloud, std::vector indices; std::vector originalHeights; - auto buildingPt = pointCloud.property_map>("building_point").first; + auto buildingPt = pointCloud.property_map>("building_point").value(); //-- Take tree subset bounded by the polygon std::vector subsetPts; Polygon_2 bbox = geomutils::calc_bbox_poly(m_poly.rings().front()); diff --git a/src/ReconstructedBuilding.cpp b/src/ReconstructedBuilding.cpp index 7bdff1c5..e5c0a8c0 100644 --- a/src/ReconstructedBuilding.cpp +++ b/src/ReconstructedBuilding.cpp @@ -339,10 +339,10 @@ void ReconstructedBuilding::get_cityjson_info(nlohmann::json& b) const { void ReconstructedBuilding::get_cityjson_semantics(nlohmann::json& g) const { // Temp for checking CGAL mesh properties Face_property semantics; - bool foundProperty; - boost::tie(semantics, foundProperty) = m_mesh.property_map("f:semantics"); - // auto semantics = m_mesh.property_map("f:semantics"); - if (!foundProperty) throw city4cfd_error("Semantic property map not found!"); + auto semanticsMap = m_mesh.property_map("f:semantics"); + if (semanticsMap.has_value()) { + semantics = semanticsMap.value(); + } else throw city4cfd_error("Semantic property map not found!"); std::unordered_map surfaceId; surfaceId["RoofSurface"] = 0; g["semantics"]["surfaces"][0]["type"] = "RoofSurface"; diff --git a/src/Terrain.cpp b/src/Terrain.cpp index b14ee94b..7c19a14f 100644 --- a/src/Terrain.cpp +++ b/src/Terrain.cpp @@ -35,12 +35,12 @@ Terrain::Terrain() : TopoFeature(0), m_cdt(), m_surfaceLayersTerrain(), m_constrainedPolys(), m_vertexFaceMap(), m_extraConstrainedEdges(), - m_searchTree(Config::get().searchtree_bucket_size) {} + m_searchTree() {} Terrain::Terrain(int pid) : TopoFeature(pid), m_cdt(), m_surfaceLayersTerrain(), m_constrainedPolys(), m_vertexFaceMap(), m_extraConstrainedEdges(), - m_searchTree(Config::get().searchtree_bucket_size) {} + m_searchTree() {} Terrain::~Terrain() = default; diff --git a/src/io.cpp b/src/io.cpp index 0e725c9e..7319c1a9 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -86,6 +86,7 @@ bool IO::read_point_cloud(std::string& file, Point_set_3& pc) { for (auto it = pc.points().begin(); it != pc.points().end(); ++it) { transformPC.insert(it->transform(translate)); } + transformPC.add_normal_map(); //todo temp workaround due to CGAL bug pc = transformPC; return true; } diff --git a/thirdparty/roofer/src/reconstruction/ArrangementBase.hpp b/thirdparty/roofer/src/reconstruction/ArrangementBase.hpp index bc37f693..ecaf916f 100644 --- a/thirdparty/roofer/src/reconstruction/ArrangementBase.hpp +++ b/thirdparty/roofer/src/reconstruction/ArrangementBase.hpp @@ -96,7 +96,10 @@ class Face_index_observer : public CGAL::Arr_observer n_faces (0), in_footprint(is_footprint), plane_id(pid), elevation(elevation), plane(plane) { CGAL_precondition (arr.is_empty()); - arr.unbounded_face()->data().is_finite=false; + for (auto uf = arr.unbounded_faces_begin(); + uf != arr.unbounded_faces_end(); ++uf) { + uf->data().is_finite = false; + } n_faces++; }; virtual void after_split_face (Face_handle old_face, @@ -122,7 +125,10 @@ class Face_split_observer : public CGAL::Arr_observer n_faces (0) { CGAL_precondition (arr.is_empty()); - arr.unbounded_face()->data().in_footprint=false; + for (auto uf = arr.unbounded_faces_begin(); + uf != arr.unbounded_faces_end(); ++uf) { + uf->data().is_finite = false; + } n_faces++; } virtual void after_split_face (Face_handle old_face, diff --git a/thirdparty/roofer/src/reconstruction/ArrangementBuilder.cpp b/thirdparty/roofer/src/reconstruction/ArrangementBuilder.cpp index 70ab2f6c..36c2b09c 100644 --- a/thirdparty/roofer/src/reconstruction/ArrangementBuilder.cpp +++ b/thirdparty/roofer/src/reconstruction/ArrangementBuilder.cpp @@ -21,7 +21,7 @@ namespace roofer::detection { // arr_common_face(edge->source(), edge->target(), common_face); auto result = CGAL::intersection(Segment_2(e_source, e_target), segment); if (result) { - if (auto p = boost::get(&*result)) { + if (auto p = std::get_if(&*result)) { // std::cout << "\tp " << *p << std::endl; // std::cout << "\tp (geo) " << CGAL::to_double(p->x())+CGAL::to_double(geo_p.x()) << ", " << CGAL::to_double(p->y())+CGAL::to_double(geo_p.y()) << std::endl; // check if point of intersection is practically at the same coordinates of one of the edge's end points diff --git a/thirdparty/roofer/src/reconstruction/ArrangementExtruder.cpp b/thirdparty/roofer/src/reconstruction/ArrangementExtruder.cpp index 117f3b53..67581a85 100644 --- a/thirdparty/roofer/src/reconstruction/ArrangementExtruder.cpp +++ b/thirdparty/roofer/src/reconstruction/ArrangementExtruder.cpp @@ -296,7 +296,7 @@ namespace roofer::detection { auto l_a = EPECK::Line_3(EPECK::Point_3(p1.x(), p1.y(), h1a), EPECK::Point_3(p2.x(), p2.y(), h2a)); auto l_b = EPECK::Line_3(EPECK::Point_3(p1.x(), p1.y(), h1b), EPECK::Point_3(p2.x(), p2.y(), h2b)); auto result = CGAL::intersection(l_a, l_b); - auto px = boost::get(&*result); + auto px = std::get_if(&*result); // TODO: check if distance from px to p1 and p2 is longer than snap_tolerance? @@ -334,7 +334,7 @@ namespace roofer::detection { auto l_a = EPECK::Line_3(EPECK::Point_3(p1.x(), p1.y(), h1a), EPECK::Point_3(p2.x(), p2.y(), h2a)); auto l_b = EPECK::Line_3(EPECK::Point_3(p1.x(), p1.y(), h1b), EPECK::Point_3(p2.x(), p2.y(), h2b)); auto result = CGAL::intersection(l_a, l_b); - auto px = boost::get(&*result); + auto px = std::get_if(&*result); // TODO: check if distance from px to p1 and p2 is longer than snap_tolerance? diff --git a/thirdparty/roofer/src/reconstruction/ArrangementSnapper.cpp b/thirdparty/roofer/src/reconstruction/ArrangementSnapper.cpp index 3f963891..749cf10a 100644 --- a/thirdparty/roofer/src/reconstruction/ArrangementSnapper.cpp +++ b/thirdparty/roofer/src/reconstruction/ArrangementSnapper.cpp @@ -210,7 +210,7 @@ namespace arragementsnapper { auto obj = walk_pl.locate( Walk_pl::Arrangement_2::Point_2(p.x(), p.y()) ); // std::cout << "The point (" << p << ") is located "; - if (auto f = boost::get(&obj)) { // located inside a face + if (auto f = std::get_if(&obj)) { // located inside a face // std::cout << "inside " // << (((*f)->is_unbounded()) ? "the unbounded" : "a bounded") // << " face." << std::endl; @@ -476,7 +476,7 @@ namespace arragementsnapper { auto obj = walk_pl.locate( Walk_pl::Arrangement_2::Point_2(p.x(), p.y()) ); - if (auto f = boost::get(&obj)) { // located inside a face + if (auto f = std::get_if(&obj)) { // located inside a face // arrFace->data() = (*f)->data(); canidate_faces[arr.non_const_handle(*f)] += cdt.triangle(fit).area(); } diff --git a/thirdparty/roofer/src/reconstruction/LineRegulariserBase.hpp b/thirdparty/roofer/src/reconstruction/LineRegulariserBase.hpp index 67f4a2c9..0ed4fe06 100644 --- a/thirdparty/roofer/src/reconstruction/LineRegulariserBase.hpp +++ b/thirdparty/roofer/src/reconstruction/LineRegulariserBase.hpp @@ -233,7 +233,7 @@ namespace linereg { Segment_2 s(a_2d.target(), b_2d.source()); auto result = CGAL::intersection(l_a, l_b); if (result) { - if (auto p = boost::get(&*result)) { + if (auto p = std::get_if(&*result)) { if (CGAL::squared_distance(*p, s) < snap_threshold) { double z = -plane.a()/plane.c() * p->x() - plane.b()/plane.c()*p->y() - plane.d()/plane.c(); ring_pts.push_back( Point_3(p->x(), p->y(), z) ); diff --git a/thirdparty/roofer/src/reconstruction/PlaneIntersector.cpp b/thirdparty/roofer/src/reconstruction/PlaneIntersector.cpp index 1a7e6f84..4fc2e32b 100644 --- a/thirdparty/roofer/src/reconstruction/PlaneIntersector.cpp +++ b/thirdparty/roofer/src/reconstruction/PlaneIntersector.cpp @@ -1,6 +1,7 @@ #include "PlaneIntersector.hpp" #include +#include namespace roofer::detection { @@ -72,7 +73,7 @@ namespace roofer::detection { auto result = CGAL::intersection(plane_hi, plane_lo); if (result) { // bound the line to extend of one plane's inliers - if (auto l = boost::get(&*result)) { + if (auto l = std::get_if(&*result)) { Point pmin_lo, pmax_lo; Point pmin_hi, pmax_hi; double dmin_lo, dmax_lo; diff --git a/thirdparty/val3dity/CMakeLists.txt b/thirdparty/val3dity/CMakeLists.txt index b26269e3..f1af3aa4 100644 --- a/thirdparty/val3dity/CMakeLists.txt +++ b/thirdparty/val3dity/CMakeLists.txt @@ -6,9 +6,12 @@ endif(COMMAND cmake_policy) cmake_minimum_required (VERSION 3.16) project( val3dity ) +if(POLICY CMP0167) + cmake_policy(SET CMP0167 NEW) +endif() -add_definitions(-std=c++14) +add_definitions(-std=c++17) set( CMAKE_BUILD_TYPE "Release") set( CMAKE_CXX_FLAGS "-O2" ) @@ -25,9 +28,7 @@ endif(MSVC) # CGAL find_package( CGAL QUIET COMPONENTS ) -if ( CGAL_FOUND ) - include( ${CGAL_USE_FILE} ) -else() +if (NOT CGAL_FOUND ) message(SEND_ERROR "val3dity requires the CGAL library") return() endif()