diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml new file mode 100644 index 00000000..a0faa974 --- /dev/null +++ b/.github/workflows/build-docker.yml @@ -0,0 +1,15 @@ +name: Build Docker + +on: + push: + workflow_dispatch: + +jobs: + build_docker: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Docker registry login + run: echo "${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}" | docker login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin + - name: Build the Docker image + run: docker build --build-arg JOBS=2 . --file docker/city4cfd.dockerfile --tag tudelft3d/city4cfd:latest diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c1e63ab7..c82d6606 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,8 +1,8 @@ -name: CI +name: build on: push: - pull_request: + pull_request: workflow_dispatch: schedule: - cron: '0 6 1 * *' # Run on the first day of every month at 06:00 UTC @@ -48,11 +48,21 @@ jobs: mkdir build && cd build cmake .. && make -j4 - build_docker: - runs-on: ubuntu-latest + build_win64: + runs-on: windows-latest steps: - uses: actions/checkout@v3 - - name: Docker registry login - run: echo "${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}" | docker login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin - - name: Build the Docker image - run: docker build --build-arg JOBS=2 . --file docker/city4cfd.dockerfile --tag tudelft3d/city4cfd:latest + - name: vcpkg build + uses: johnwason/vcpkg-action@v5 + id: vcpkg + with: + pkgs: boost-program-options boost-geometry boost-locale boost-chrono boost-system boost-filesystem eigen3 cgal gdal + triplet: x64-windows-release + token: ${{ github.token }} + extra-args: --clean-after-build + - name: Build + run: | + mkdir Release + cd Release + cmake .. ${{ steps.vcpkg.outputs.vcpkg-cmake-config }} + cmake --build . --parallel 4 --config Release diff --git a/.github/workflows/release-win64-exe.yml b/.github/workflows/release-win64-exe.yml new file mode 100644 index 00000000..901fb24d --- /dev/null +++ b/.github/workflows/release-win64-exe.yml @@ -0,0 +1,54 @@ +# This is a basic workflow to help you get started with Actions + +name: Release Windows Executable + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the master branch +on: + release: + types: [created] + pull_request: + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + release_win64: + if: false + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + - name: vcpkg build + uses: johnwason/vcpkg-action@v5 + id: vcpkg + with: + pkgs: boost-program-options boost-geometry boost-locale boost-chrono boost-system boost-filesystem eigen3 cgal gdal + triplet: x64-windows-release + token: ${{ github.token }} + extra-args: --clean-after-build + - name: Build + run: | + mkdir Release + cd Release + cmake .. ${{ steps.vcpkg.outputs.vcpkg-cmake-config }} + cmake --build . --parallel 4 --config Release + - name: Package binary files + run: | + mkdir city4cfd-win64 + xcopy Release\Release\* city4cfd-win64\ + mkdir city4cfd-win64\tools\city4cfd_las2las + xcopy Release\tools\LASTools\Release\* city4cfd-win64\tools\city4cfd_las2las + mkdir city4cfd-win64\tools\prepare_point_cloud + xcopy Release\tools\prepare_point_cloud\Release\* city4cfd-win64\tools\prepare_point_cloud\ + xcopy vcpkg\installed\x64-windows-release\share\proj\proj.db city4cfd-win64\ + tar -zcvf city4cfd-win64.zip city4cfd-win64\* + - name: Upload binary files as artifact + uses: actions/upload-artifact@master + with: + name: city4cfd-win64 + path: city4cfd-win64.zip + - name: Add binaries as an asset to the release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + files: | + city4cfd-win64.zip diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cc57692..5955fc7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.4.4] - 2024-01-13 +### Changed +- Improved fallbacks in case building import fails in late stages +- Improved polygon flattening when buildings are adjacent +### Fixed +- Minor bugfixes + ## [0.4.3] - 2023-08-25 ### Fixed - Issue with GDAL on Ubuntu 20.04 diff --git a/CMakeLists.txt b/CMakeLists.txt index 525242e7..2a5ac86a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,15 +4,29 @@ project(city4cfd) #set( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true ) -set(CMAKE_CXX_FLAGS "-O3") +set(CMAKE_CXX_FLAGS "-O2") set(CMAKE_BUILD_TYPE "Release") -#if (COMMAND cmake_policy) -# cmake_policy(SET CMP0003 NEW) -#endif() +if (COMMAND cmake_policy) + cmake_policy(SET CMP0003 NEW) +endif() + +if (MSVC) + add_definitions(-DNOMINMAX) + add_definitions("/EHsc") +endif (MSVC) # BOOST find_package(Boost 1.66 REQUIRED COMPONENTS filesystem locale) +if (WIN32) + FIND_PACKAGE(Boost) + if (Boost_FOUND) + INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIR}) + ADD_DEFINITIONS( "-DHAS_BOOST" ) + set(Boost_USE_STATIC_LIBS ON) + set(Boost_ARCHITECTURE "-x64") + endif() +endif (WIN32) # CGAL find_package(CGAL REQUIRED QUIET COMPONENTS) diff --git a/README.md b/README.md index f5c6420c..065ba925 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ -[![docs](https://img.shields.io/badge/docs-Wiki-brightgreen?style=flat-square)](https://github.com/tudelft3d/City4CFD/wiki) +# City4CFD + +[![build](https://img.shields.io/github/actions/workflow/status/tudelft3d/City4CFD/build.yml?branch=main&style=flat-square)](https://github.com/tudelft3d/City4CFD/actions/workflows/build.yml) +[![docs](https://img.shields.io/badge/docs-Wiki-yellow?style=flat-square)](https://github.com/tudelft3d/City4CFD/wiki) [![GitHub license](https://img.shields.io/github/license/tudelft3d/City4CFD?style=flat-square)](https://github.com/tudelft3d/City4CFD/blob/master/LICENSE) [![DOI:10.3389/fbuil.2022.899332](http://img.shields.io/badge/DOI-10.3389/fbuil.2022.899332-B62030.svg?style=flat-square)](https://doi.org/10.3389/fbuil.2022.899332) - -# City4CFD - ![welcome_figure](https://github.com/tudelft3d/City4CFD/blob/main/docs/images/workflow.png) -City4CFD--*City for CFD*--is a tool that aims to automatically reconstruct 3D city geometries tailored for microscale urban flow simulations. +City4CFD--*City for CFD*--is a cross-platform tool that aims to automatically reconstruct 3D city geometries tailored for microscale urban flow simulations. It can create automatically a terrain from a point cloud and imprint different surfaces (e.g. green areas, water, roads). @@ -34,7 +34,7 @@ City4CFD is developed by the [3D Geoinformation Research Group](https://3d.bk.tu **Output** is in the following formats: OBJ, STL, and CityJSON. The ID of each polygon is preserved, and there is a 1-to-1 mapping between the input and the output. ## Installation -You can directly compile City4CFD on your system using cmake, run it through a Docker container, or install using Homebrew in the case of macOS. +You can directly compile City4CFD on your system using cmake, run it through a Docker container, install using Homebrew in the case of macOS, or use a precompiled binary on Windows. ### Build from source The following libraries are required to build the project: @@ -77,6 +77,9 @@ Mac users can install City4CFD through Homebrew: brew install tudelft3d/software/city4cfd ``` +### Windows +Windows users can use a precompiled binary in [Releases](https://github.com/tudelft3d/City4CFD/releases). + ## Getting started The folder *examples* contains example datasets you can run for your first reconstruction. You can run your first reconstruction from the `/examples/TUD_Campus` folder by typing: diff --git a/src/Boundary.cpp b/src/Boundary.cpp index 8bcd5807..bf559489 100644 --- a/src/Boundary.cpp +++ b/src/Boundary.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/BoundingRegion.cpp b/src/BoundingRegion.cpp index 9eaafd3b..3ecff697 100644 --- a/src/BoundingRegion.cpp +++ b/src/BoundingRegion.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/BoundingRegion.h b/src/BoundingRegion.h index 3326487f..64e64fe3 100644 --- a/src/BoundingRegion.h +++ b/src/BoundingRegion.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/Building.cpp b/src/Building.cpp index f2dd75fa..fa6903dd 100644 --- a/src/Building.cpp +++ b/src/Building.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -42,30 +42,16 @@ Building::Building() : PolyFeature(1), _elevation(-global::largnum), _height(-global::largnum), _ptsPtr(std::make_shared()), _hasFailed(false) {} -Building::Building(const int internalID) - : PolyFeature(1, internalID), _elevation(-global::largnum), _height(-global::largnum), - _ptsPtr(std::make_shared()), _hasFailed(false) {} - Building::Building(const nlohmann::json& poly) : PolyFeature(poly, true, 1), _elevation(-global::largnum), _height(-global::largnum), _ptsPtr(std::make_shared()), _hasFailed(false) {} // 'true' here to check for polygon simplicity -Building::Building(const nlohmann::json& poly, const int internalID) - : PolyFeature(poly, true, 1, internalID), _elevation(-global::largnum), _height(-global::largnum), - _ptsPtr(std::make_shared()), _hasFailed(false) {} - // 'true' here to check for polygon simplicity - Building::Building(const Polygon_with_attr& poly) : PolyFeature(poly, true, 1), _elevation(-global::largnum), _height(-global::largnum), _ptsPtr(std::make_shared()), _hasFailed(false) {} // 'true' here to check for polygon simplicity -Building::Building(const Polygon_with_attr& poly, const int internalID) - : PolyFeature(poly, true, 1, internalID), _elevation(-global::largnum), _height(-global::largnum), - _ptsPtr(std::make_shared()), _hasFailed(false) {} - // 'true' here to check for polygon simplicity - Building::~Building() = default; void Building::insert_point(const Point_3& pt) { @@ -275,6 +261,10 @@ double Building::sq_max_dim() { return *(std::max_element(dims.begin(), dims.end())); } +PointSet3Ptr Building::get_points() const { + return _ptsPtr; +} + void Building::get_cityjson_info(nlohmann::json& b) const { b["type"] = "Building"; // b["attributes"]; diff --git a/src/Building.h b/src/Building.h index d0a1b1c9..4ce35961 100644 --- a/src/Building.h +++ b/src/Building.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -34,11 +34,8 @@ class Building : public PolyFeature { public: Building(); - Building(const int internalID); Building(const nlohmann::json& poly); - Building(const nlohmann::json& poly, const int internalID); Building(const Polygon_with_attr& poly); - Building(const Polygon_with_attr& poly, const int internalID); ~Building(); static void alpha_wrap(const BuildingsPtr& buildings, Mesh& newMesh); @@ -59,6 +56,7 @@ class Building : public PolyFeature { bool has_self_intersections() const; void set_to_zero_terrain(); double sq_max_dim(); + PointSet3Ptr get_points() const; virtual void get_cityjson_info(nlohmann::json& b) const override; virtual void get_cityjson_semantics(nlohmann::json& g) const override; diff --git a/src/CGALTypes.h b/src/CGALTypes.h index df5d350d..c395fbd5 100644 --- a/src/CGALTypes.h +++ b/src/CGALTypes.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -62,6 +62,13 @@ typedef CGAL::Polygon_2 Polygon_3; //- CGAL's Polygon_with_holes container expanded struct Polygon_with_holes_2 { + Polygon_with_holes_2() = default; + + Polygon_with_holes_2(const CGAL::Polygon_with_holes_2& cgalPoly) { + _rings.push_back(cgalPoly.outer_boundary()); + for (auto& hole : cgalPoly.holes()) _rings.push_back(hole); + } + std::vector _rings; std::vector& rings() {return _rings;} @@ -92,12 +99,20 @@ struct Polygon_with_holes_2 { } return cgalPoly; } - const CGAL::Polygon_with_holes_2 get_exact() const { + + const CGAL::Polygon_2 get_exact_outer_boundary() const { Converter to_exact; - CGAL::Polygon_with_holes_2 cgalPoly; + CGAL::Polygon_2 cgalOuterPoly; for (auto& pt : _rings.front()) { - cgalPoly.outer_boundary().push_back(to_exact(pt)); + cgalOuterPoly.push_back(to_exact(pt)); } + return cgalOuterPoly; + } + + const CGAL::Polygon_with_holes_2 get_exact() const { + Converter to_exact; + CGAL::Polygon_with_holes_2 cgalPoly; + cgalPoly.outer_boundary() = get_exact_outer_boundary(); for (auto hole = holes_begin(); hole != holes_end(); ++hole) { CGAL::Polygon_2 holePoly; for (auto& pt : *hole) { diff --git a/src/Config.cpp b/src/Config.cpp index b6d07c97..e9ae1a4f 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/Config.h b/src/Config.h index 90bc65f1..463ac438 100644 --- a/src/Config.h +++ b/src/Config.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/ImportedBuilding.cpp b/src/ImportedBuilding.cpp index 6a4190e9..2450f260 100644 --- a/src/ImportedBuilding.cpp +++ b/src/ImportedBuilding.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -40,14 +40,14 @@ int ImportedBuilding::noBottom = 0; -ImportedBuilding::ImportedBuilding(std::unique_ptr& buildingJson, PointSet3Ptr& importedBuildingPts, const int internalID) - : Building(internalID), _buildingJson(std::move(buildingJson)), - _footprintIdxList(), _parentBuildingID(), _ptMap(), - _appendToBuilding(false), _lodIdx(-1), _footprintPtsIdxList(), _trueHeight(Config::get().importTrueHeight) { +ImportedBuilding::ImportedBuilding(std::unique_ptr& buildingJson, PointSet3Ptr& importedBuildingPts) + : Building(), _buildingJson(std::move(buildingJson)), + _footprintIdxList(), _ptMap(), _appendToBuilding(false), + _lodIdx(-1), _footprintPtsIdxList(), _trueHeight(Config::get().importTrueHeight) { _f_imported = true; // the flag is here to avoid shorten polygons later. todo to fix - //-- Get parent building ID - _parentBuildingID = (*_buildingJson)["parents"].front(); + //-- ID is the partent building ID + _id = (*_buildingJson)["parents"].front(); //-- Define LoD std::map lodGeomLst; @@ -116,23 +116,33 @@ ImportedBuilding::ImportedBuilding(std::unique_ptr& buildingJson // int polyNo = 0; for (int& footprintIdx : _footprintIdxList) { //-- Construct footprint polygon from ground surface - nlohmann::json coordBnd = geometry["boundaries"].front()[footprintIdx].front(); - CGAL::Polygon_2 facePoly; - for (const int& ptIdx: coordBnd) { - facePoly.push_back(ePoint_2(_ptMap.at(ptIdx).x(), _ptMap.at(ptIdx).y())); - footprintElevations.push_back(_ptMap.at(ptIdx).z()); - pointConnectivity[IO::gen_key_bucket(Point_2(_ptMap.at(ptIdx).x(), _ptMap.at(ptIdx).y()))] = ptIdx; - } - if (!facePoly.is_simple()) { - Config::write_to_log("Failed to import building: " + this->get_parent_building_id() - + " Reason: Footprint polygon is not simple."); - this->deactivate(); - return; + CGAL::Polygon_with_holes_2 facePolyWH; + bool first = true; + for (auto& coordBnd : geometry["boundaries"].front()[footprintIdx]) { + CGAL::Polygon_2 facePoly; + for (const int& ptIdx: coordBnd) { + facePoly.push_back(ePoint_2(_ptMap.at(ptIdx).x(), _ptMap.at(ptIdx).y())); + footprintElevations.push_back(_ptMap.at(ptIdx).z()); + pointConnectivity[IO::gen_key_bucket(Point_2(_ptMap.at(ptIdx).x(), _ptMap.at(ptIdx).y()))] = ptIdx; + } + if (!facePoly.is_simple()) { + Config::write_to_log("Failed to import building: " + this->get_id() + + " Reason: Footprint polygon is not simple."); + this->deactivate(); + return; + } + geomutils::pop_back_if_equal_to_front(facePoly); + + if (first) { + if (facePoly.is_clockwise_oriented()) facePoly.reverse_orientation(); + first = false; + facePolyWH.outer_boundary() = facePoly; + } else { + if (facePoly.is_counterclockwise_oriented()) facePoly.reverse_orientation(); + facePolyWH.add_hole(facePoly); + } } - geomutils::pop_back_if_equal_to_front(facePoly); - if (facePoly.is_clockwise_oriented()) facePoly.reverse_orientation(); - - polySet.join(facePoly); + polySet.join(facePolyWH); } //-- Polyset to polygon data structure this->polyset_to_polygon(polySet); @@ -141,12 +151,13 @@ ImportedBuilding::ImportedBuilding(std::unique_ptr& buildingJson this->set_footprint_mesh_connectivity(pointConnectivity); } -ImportedBuilding::ImportedBuilding(Mesh& mesh, const int internalID) - : Building(internalID), _buildingJson(std::make_unique()), - _footprintIdxList(), _parentBuildingID(), _appendToBuilding(false), _ptMap(), +ImportedBuilding::ImportedBuilding(Mesh& mesh) + : Building(), _buildingJson(std::make_unique()), + _footprintIdxList(), _appendToBuilding(false), _ptMap(), _lodIdx(-1), _footprintPtsIdxList(), _trueHeight(Config::get().importTrueHeight) { _f_imported = true; // the flag is here to avoid shorten polygons later. todo to fix + _id = std::to_string(_polyInternalID); //-- Get the polygon from the building bottom // group faces pointing down CGAL::Vector_3 downVector(0, 0, -1); @@ -339,6 +350,14 @@ void ImportedBuilding::reconstruct() { PMP::polygon_soup_to_polygon_mesh(points, polygons, _mesh); PMP::triangulate_faces(_mesh); + if (this->get_height() < Config::get().minHeight) { + // Store points to ptsPtr so that it might be used for LoD1 reconstruction + for (auto& pt : _ptMap) _ptsPtr->insert(pt.second); + throw std::runtime_error("Importing failed. It could be that the height is" + "\n lower than minimum, or the mesh connectivity is broken." + "\n Trying to reconstruct LoD1.2 from building points"); + } + /* Mesh wrap; const double relative_alpha = 300.; @@ -409,10 +428,6 @@ const nlohmann::json& ImportedBuilding::get_building_json() const { return *_buildingJson; } -const std::string& ImportedBuilding::get_parent_building_id() const { - return _parentBuildingID; -} - const int ImportedBuilding::get_lod_idx() const { return _lodIdx; } @@ -423,7 +438,7 @@ const bool ImportedBuilding::is_appending() const { void ImportedBuilding::check_simplicity(Polygon_2& ring) { if (!ring.is_simple()) { - Config::write_to_log("Failed to import building: " + this->get_parent_building_id() + Config::write_to_log("Failed to import building: " + this->get_id() + " Reason: Footprint polygon is not simple."); this->deactivate(); return; @@ -432,7 +447,7 @@ void ImportedBuilding::check_simplicity(Polygon_2& ring) { void ImportedBuilding::polyset_to_polygon(const CGAL::Polygon_set_2& polySet) { // polySet.remove_redundant_edges(); - std::list> res; + std::vector> res; polySet.polygons_with_holes(std::back_inserter(res)); Converter to_inexact; diff --git a/src/ImportedBuilding.h b/src/ImportedBuilding.h index c30ee3b2..ce2f1e67 100644 --- a/src/ImportedBuilding.h +++ b/src/ImportedBuilding.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -38,8 +38,8 @@ class ImportedBuilding : public Building { ImportedBuilding() = delete; ImportedBuilding(std::unique_ptr& buildingJson, - PointSet3Ptr& importedBuildingPts, const int internalID); - ImportedBuilding(Mesh& mesh, const int internalID); + PointSet3Ptr& importedBuildingPts); + ImportedBuilding(Mesh& mesh); ~ImportedBuilding(); virtual double get_elevation() override; @@ -49,7 +49,6 @@ class ImportedBuilding : public Building { void append_nonground_part(const std::shared_ptr& other); const nlohmann::json& get_building_json() const; - const std::string& get_parent_building_id() const; const int get_lod_idx() const; const bool is_appending() const; @@ -61,7 +60,6 @@ class ImportedBuilding : public Building { std::unique_ptr _buildingJson; std::vector _footprintIdxList; std::vector> _footprintPtsIdxList; - std::string _parentBuildingID; bool _appendToBuilding; bool _trueHeight; int _lodIdx; diff --git a/src/LoD12.cpp b/src/LoD12.cpp index 2b6316b6..3210359d 100644 --- a/src/LoD12.cpp +++ b/src/LoD12.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/LoD12.h b/src/LoD12.h index ede982f8..8f25e16c 100644 --- a/src/LoD12.h +++ b/src/LoD12.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/Map3d.cpp b/src/Map3d.cpp index 20267adf..5f4280d8 100644 --- a/src/Map3d.cpp +++ b/src/Map3d.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -106,9 +106,8 @@ void Map3d::set_features() { //-- Add features - order in _allFeaturesPtr defines the advantage in marking terrain polygons //- Buildings - int internalID = 0; for (auto& poly : _polygonsBuildings) { - auto building = std::make_shared(*poly, internalID++); + auto building = std::make_shared(*poly); _reconstructedBuildingsPtr.push_back(building); _buildingsPtr.push_back(building); _allFeaturesPtr.push_back(building); @@ -119,10 +118,8 @@ void Map3d::set_features() { std::cout << "Importing CityJSON geometries" << std::endl; std::vector> appendingBuildings; - internalID = 0; for (auto& importedBuilding: _importedBuildingsJSON) { - auto explicitCityJSONGeom = std::make_shared(importedBuilding, _importedBuildingsPts, - internalID++); + auto explicitCityJSONGeom = std::make_shared(importedBuilding, _importedBuildingsPts); if (!explicitCityJSONGeom->is_appending()) { _importedBuildingsPtr.push_back(explicitCityJSONGeom); _buildingsPtr.push_back(explicitCityJSONGeom); @@ -134,7 +131,7 @@ void Map3d::set_features() { //- Check for building parts that do not have footprint and append to another instance of the same building for (auto& b: appendingBuildings) { for (auto& importedBuilding: _importedBuildingsPtr) { - if (b->get_parent_building_id() == importedBuilding->get_parent_building_id()) { + if (b->get_id() == importedBuilding->get_id()) { importedBuilding->append_nonground_part(b); break; } @@ -145,7 +142,7 @@ void Map3d::set_features() { } else if (!_importedBuildingsOther.empty()) { std::cout << "Importing geometries" << std::endl; for (auto& mesh : _importedBuildingsOther) { - auto explicitOBJGeom = std::make_shared(mesh, internalID++); + auto explicitOBJGeom = std::make_shared(mesh); _importedBuildingsPtr.push_back(explicitOBJGeom); _buildingsPtr.push_back(explicitOBJGeom); _allFeaturesPtr.push_back(explicitOBJGeom); @@ -316,8 +313,21 @@ void Map3d::reconstruct_terrain() { if (_terrainPtr->get_cdt().number_of_vertices() == 0) { std::cout << "\nReconstructing terrain" << std::endl; _terrainPtr->prep_constraints(_allFeaturesPtr, _pointCloud.get_terrain()); - if (!Config::get().flattenSurfaces.empty()) - _pointCloud.flatten_polygon_pts(_allFeaturesPtr, _terrainPtr->get_extra_constrained_edges()); + // Handle flattening + if (!Config::get().flattenSurfaces.empty()) { + std::vector> additionalPolys; + _pointCloud.flatten_polygon_pts(_allFeaturesPtr, _terrainPtr->get_extra_constrained_edges(), additionalPolys); + if (!additionalPolys.empty()) { + for (auto& polyToAdd : additionalPolys) { + Polygon_with_attr newPolyToAdd; + newPolyToAdd.polygon = polyToAdd.first; + auto surfacePoly + = std::make_shared(newPolyToAdd, polyToAdd.second); + _surfaceLayersPtr.push_back(surfacePoly); + _allFeaturesPtr.push_back(surfacePoly); + } + } + } _terrainPtr->set_cdt(_pointCloud.get_terrain()); _terrainPtr->constrain_features(); } @@ -333,34 +343,55 @@ void Map3d::reconstruct_buildings() { << ". If I cannot find a geometry with that LoD, I will reconstruct in the highest LoD available" << std::endl; } - int failed = 0; - #pragma omp parallel for - for (auto& f : _buildingsPtr) { - if (!f->is_active()) continue; - try { - f->reconstruct(); - //-- In case of hybrid boolean/constraining reconstruction - if (Config::get().clip && !Config::get().handleSelfIntersect && f->has_self_intersections()) { - f->set_clip_flag(false); - f->reconstruct(); - } - } catch (std::exception& e) { - #pragma omp atomic - ++failed; - // add information to log file - Config::write_to_log("Building ID: " + f->get_id() + " Failed to reconstruct. Reason: " + e.what()); - // mark for geojson output - f->mark_as_failed(); - } + # pragma omp parallel for + for (int i = 0; i < _buildingsPtr.size(); ++i) { + //for (auto& f : _buildingsPtr) { // MSVC doesn't like range loops with OMP + auto& b = _buildingsPtr[i]; + if (b->is_active()) this->reconstruct_one_building(b); } - this->clear_inactives(); - std::cout << " Number of successfully reconstructed buildings: " << _buildingsPtr.size() << std::endl; + this->clear_inactives(); // in case of imported-reconstructed fallback + // Gather failed reconstructions + int failed = 0; + for (auto& b : _buildingsPtr) if (b->has_failed_to_reconstruct()) ++failed; + std::cout << " Number of successfully reconstructed buildings: " << _buildingsPtr.size() - failed << std::endl; Config::get().logSummary << "Building reconstruction summary: successfully reconstructed buildings: " << _buildingsPtr.size() - failed << std::endl; Config::get().logSummary << " num of failed reconstructions: " << failed << std::endl; } +void Map3d::reconstruct_one_building(std::shared_ptr& building) { + try { + building->reconstruct(); + //-- In case of hybrid boolean/constraining reconstruction + if (Config::get().clip && !Config::get().handleSelfIntersect && building->has_self_intersections()) { + building->set_clip_flag(false); + building->reconstruct(); + } + } catch (std::exception& e) { + // add information to log file + Config::write_to_log("Building ID: " + building->get_id() + " Failed to reconstruct. Reason: " + e.what()); + // fallback for failed reconstruction of imported buildings + if (building->is_imported()) { + building->deactivate(); // deactivate this and use reconstructed instead + // try to recover by reconstructing LoD1.2 from geometry pts + auto importToReconstructBuild = + std::make_shared(std::static_pointer_cast(building)); + #pragma omp critical + { + _reconstructedBuildingsPtr.push_back(importToReconstructBuild); + _allFeaturesPtr.push_back(importToReconstructBuild); + _buildingsPtr.push_back(importToReconstructBuild); + } + std::shared_ptr buildToReconstruct = importToReconstructBuild; + this->reconstruct_one_building(buildToReconstruct); + } else { + // mark for geojson output + building->mark_as_failed(); + } + } +} + void Map3d::reconstruct_boundaries() { std::cout << "\nReconstructing boundaries" << std::endl; if (_boundariesPtr.size() > 2) { // Means more than one side @@ -413,8 +444,20 @@ void Map3d::clip_buildings() { //-- Prepare terrain with subset std::cout << "\nReconstructing terrain" << std::endl; _terrainPtr->prep_constraints(_allFeaturesPtr, _pointCloud.get_terrain()); - if (!Config::get().flattenSurfaces.empty()) - _pointCloud.flatten_polygon_pts(_allFeaturesPtr, _terrainPtr->get_extra_constrained_edges()); + // Handle flattening + if (!Config::get().flattenSurfaces.empty()) { + std::vector> additionalPolys; + _pointCloud.flatten_polygon_pts(_allFeaturesPtr, _terrainPtr->get_extra_constrained_edges(), additionalPolys); + if (!additionalPolys.empty()) { + for (auto& polyToAdd : additionalPolys) { + Polygon_with_attr newPolyToAdd; + newPolyToAdd.polygon = polyToAdd.first; + auto surfacePoly = std::make_shared(newPolyToAdd, polyToAdd.second); + _surfaceLayersPtr.push_back(surfacePoly); + _allFeaturesPtr.push_back(surfacePoly); + } + } + } _terrainPtr->set_cdt(_pointCloud.get_terrain()); _terrainPtr->constrain_features(); _terrainPtr->prepare_subset(); @@ -548,14 +591,14 @@ void Map3d::clear_inactives() { for (auto& importedBuilding: _importedBuildingsPtr) { if (!importedBuilding->is_active()) { auto it = std::find(inactiveBuildingIdxs.begin(), inactiveBuildingIdxs.end(), - importedBuilding->get_parent_building_id()); + importedBuilding->get_id()); if (it == inactiveBuildingIdxs.end()) - inactiveBuildingIdxs.push_back(importedBuilding->get_parent_building_id()); + inactiveBuildingIdxs.push_back(importedBuilding->get_id()); } } for (unsigned long i = 0; i < _importedBuildingsPtr.size();) { auto it = std::find(inactiveBuildingIdxs.begin(), inactiveBuildingIdxs.end(), - _importedBuildingsPtr[i]->get_parent_building_id()); + _importedBuildingsPtr[i]->get_id()); if (it == inactiveBuildingIdxs.end()) ++i; else { _importedBuildingsPtr[i]->deactivate(); @@ -627,4 +670,4 @@ void Map3d::set_footprint_elevation(T& features) { //- Explicit template instantiation template void Map3d::set_footprint_elevation (BuildingsPtr& feature); template void Map3d::set_footprint_elevation(SurfaceLayersPtr& feature); -template void Map3d::set_footprint_elevation (PolyFeaturesPtr& feature); \ No newline at end of file +template void Map3d::set_footprint_elevation (PolyFeaturesPtr& feature); diff --git a/src/Map3d.h b/src/Map3d.h index 17c4828d..607a7e14 100644 --- a/src/Map3d.h +++ b/src/Map3d.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -80,6 +80,7 @@ class Map3d { void remove_extra_terrain_pts(); void reconstruct_terrain(); void reconstruct_buildings(); + void reconstruct_one_building(std::shared_ptr& building); void reconstruct_boundaries(); void reconstruct_with_flat_terrain(); void solve_building_conflicts(); diff --git a/src/PointCloud.cpp b/src/PointCloud.cpp index 1005cd9a..c7728823 100644 --- a/src/PointCloud.cpp +++ b/src/PointCloud.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -110,7 +110,6 @@ void PointCloud::smooth_terrain() { for (auto& pt : mesh.points()) { _pointCloudTerrain.insert(pt); } - _pointCloudTerrain.add_property_map ("is_building_point", false); } void PointCloud::remove_points_in_polygon(const BuildingsPtr& features) { @@ -155,7 +154,6 @@ void PointCloud::create_flat_terrain(const PolyFeaturesPtr& lsFeatures) { _pointCloudTerrain.insert(Point_3(pt.x(), pt.y(), 0.0)); } } - _pointCloudTerrain.add_property_map ("is_building_point", false); } void PointCloud::set_flat_terrain() { @@ -164,11 +162,11 @@ void PointCloud::set_flat_terrain() { flatPC.insert(Point_3(pt.x(), pt.y(), 0.)); } _pointCloudTerrain = flatPC; - _pointCloudTerrain.add_property_map ("is_building_point", false); } void PointCloud::flatten_polygon_pts(const PolyFeaturesPtr& lsFeatures, - std::vector& constrainedEdges) { + std::vector& constrainedEdges, + std::vector>& newPolys) { std::cout << "\n Flattening surfaces" << std::endl; std::map flattenedPts; @@ -199,10 +197,12 @@ void PointCloud::flatten_polygon_pts(const PolyFeaturesPtr& lsFeatures, auto ita = Config::get().flattenSurfaces.find(f->get_output_layer_id()); if (ita != Config::get().flattenSurfaces.end()) { // flatten points - if(f->flatten_polygon_inner_points(_pointCloudTerrain, flattenedPts, searchTree, pointCloudConnectivity)) { + bool isNextToBuilding = false; + if(f->flatten_polygon_inner_points(_pointCloudTerrain, flattenedPts, searchTree, pointCloudConnectivity, + constrainedEdges, newPolys, isNextToBuilding)) { // add to list if constructing vertical borders if (std::find(Config::get().flattenVertBorder.begin(), Config::get().flattenVertBorder.end(), - f->get_output_layer_id()) != Config::get().flattenVertBorder.end()) { + f->get_output_layer_id()) != Config::get().flattenVertBorder.end() && !isNextToBuilding) { vertBorders.push_back(f); } } @@ -243,8 +243,9 @@ void PointCloud::buffer_flat_edges(const PolyFeaturesPtr& avgFeatures, std::vector offsets{0.001}; std::vector polyList; #pragma omp parallel for - for (auto& f: avgFeatures) { - auto& poly = f->get_poly().outer_boundary(); + for (int i = 0; i < avgFeatures.size(); ++i) { + //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()); CGAL::Bbox_2 bbox = CGAL::bbox_2(poly.begin(), poly.end()); @@ -331,7 +332,6 @@ void PointCloud::read_point_clouds() { if (!Config::get().ground_xyz.empty()) { std::cout << "Reading ground points" << std::endl; IO::read_point_cloud(Config::get().ground_xyz, _pointCloudTerrain); - _pointCloudTerrain.add_property_map("is_building_point", false); std::cout << " Points read: " << _pointCloudTerrain.size() << std::endl; } else { @@ -364,4 +364,4 @@ const Point_set_3& PointCloud::get_terrain() const { const Point_set_3& PointCloud::get_buildings() const { return _pointCloudBuildings; -} \ No newline at end of file +} diff --git a/src/PointCloud.h b/src/PointCloud.h index d50a518d..5954ecec 100644 --- a/src/PointCloud.h +++ b/src/PointCloud.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -44,7 +44,8 @@ class PointCloud { void set_flat_terrain(); void smooth_terrain(); void remove_points_in_polygon(const BuildingsPtr& features); - void flatten_polygon_pts(const PolyFeaturesPtr& lsFeatures, std::vector& constrainedEdges); + void flatten_polygon_pts(const PolyFeaturesPtr& lsFeatures, std::vector& constrainedEdges, + std::vector>& newPolys); void buffer_flat_edges(const PolyFeaturesPtr& avgFeatures, std::vector& constrainedEdges); void read_point_clouds(); diff --git a/src/PolyFeature.cpp b/src/PolyFeature.cpp index ba3c09f2..c5f850d7 100644 --- a/src/PolyFeature.cpp +++ b/src/PolyFeature.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -25,31 +25,37 @@ Delft University of Technology */ +#ifdef CITY4CFD_VERBOSE + #define CITY4CFD_POLYFEATURE_VERBOSE +#endif + #include "PolyFeature.h" #include "geomutils.h" +#include "Building.h" #include #include #include +#include +#include +#include +#include + #ifndef NDEBUG #include #endif PolyFeature::PolyFeature() - : TopoFeature(), _poly(), _groundElevations(), _polyInternalID(), + : TopoFeature(), _poly(), _groundElevations(), _polyInternalID(new_internal_id()), _groundElevation(-global::largnum), _minBbox() {} PolyFeature::PolyFeature(const int outputLayerID) - : TopoFeature(outputLayerID), _poly(), _groundElevations(), _polyInternalID(), + : TopoFeature(outputLayerID), _poly(), _groundElevations(), _polyInternalID(new_internal_id()), _groundElevation(-global::largnum), _minBbox() {} -PolyFeature::PolyFeature(const int outputLayerID, const int internalID) - : TopoFeature(outputLayerID), _groundElevations(), _polyInternalID(internalID), - _groundElevation(-global::largnum), _minBbox() {} - PolyFeature::PolyFeature(const nlohmann::json& poly, const bool checkSimplicity) - : TopoFeature(), _groundElevations(), _polyInternalID(), + : TopoFeature(), _groundElevations(), _polyInternalID(new_internal_id()), _groundElevation(-global::largnum), _minBbox() { this->parse_json_poly(poly, checkSimplicity); } @@ -63,24 +69,13 @@ PolyFeature::PolyFeature(const nlohmann::json& poly, const bool checkSimplicity, PolyFeature::PolyFeature(const nlohmann::json& poly, const int outputLayerID) : PolyFeature(poly, false, outputLayerID) {} -PolyFeature::PolyFeature(const nlohmann::json& poly, const bool checkSimplicity, - const int outputLayerID, const int internalID) - : PolyFeature(poly, checkSimplicity) { - _polyInternalID = internalID; - _outputLayerID = outputLayerID; - if (_outputLayerID >= _numOfOutputLayers) _numOfOutputLayers = _outputLayerID + 1; -} - -PolyFeature::PolyFeature(const nlohmann::json& poly, const int outputLayerID, const int internalID) - : PolyFeature(poly, false, outputLayerID, internalID) {} - PolyFeature::PolyFeature(const Polygon_with_attr& poly, const bool checkSimplicity) - : TopoFeature(), _groundElevations(), _polyInternalID(), + : TopoFeature(), _groundElevations(), _polyInternalID(new_internal_id()), _groundElevation(-global::largnum), _minBbox() { bool isOuterRing = true; for (auto& ring : poly.polygon.rings()) { Polygon_2 tempPoly; - const double minDist = 0.0001; + const double minDist = 0.0001; // hardcoded Point_2 prev(global::largnum, global::largnum); for (auto& pt: ring.vertices()) { if (CGAL::squared_distance(pt, prev) > minDist) { @@ -89,6 +84,11 @@ PolyFeature::PolyFeature(const Polygon_with_attr& poly, const bool checkSimplici } } geomutils::pop_back_if_equal_to_front(tempPoly); + if (tempPoly.size() < 3) { // Sanity check if it is even a polygon + std::cout << "WARNING: Skipping import of a zero-area polygon" << std::endl; + this->deactivate(); + return; + } if (isOuterRing) { if (tempPoly.is_clockwise_oriented()) tempPoly.reverse_orientation(); isOuterRing = false; @@ -122,19 +122,10 @@ PolyFeature::PolyFeature(const Polygon_with_attr& poly, const bool checkSimplici PolyFeature::PolyFeature(const Polygon_with_attr& poly, const int outputLayerID) : PolyFeature(poly, false, outputLayerID) {} -PolyFeature::PolyFeature(const Polygon_with_attr& poly, const bool checkSimplicity, - const int outputLayerID, const int internalID) - : PolyFeature(poly, checkSimplicity) { - _polyInternalID = internalID; - _outputLayerID = outputLayerID; - if (_outputLayerID >= _numOfOutputLayers) _numOfOutputLayers = _outputLayerID + 1; -} - -PolyFeature::PolyFeature(const Polygon_with_attr& poly, const int outputLayerID, const int internalID) - : PolyFeature(poly, false, outputLayerID, internalID) {} - PolyFeature::~PolyFeature() = default; +int PolyFeature::_numOfPolyFeatures = 0; + void PolyFeature::calc_footprint_elevation_nni(const DT& dt) { typedef std::vector> Point_coordinate_vector; DT::Face_handle fh = nullptr; @@ -201,8 +192,8 @@ void PolyFeature::calc_footprint_elevation_linear(const DT& dt) { double PolyFeature::ground_elevation() { if (_groundElevation < -global::largnum + global::smallnum) { - if (_groundElevations.empty())throw std::runtime_error("Polygon elevations missing!" - " Cannot calculate average"); + if (_groundElevations.empty()) throw std::runtime_error("Polygon elevations missing!" + " Cannot calculate average"); // calculating base elevation as 95 percentile of outer ring _groundElevation = geomutils::percentile(_groundElevations.front(), 0.95); } @@ -222,10 +213,18 @@ double PolyFeature::ground_elevation() { bool PolyFeature::flatten_polygon_inner_points(const Point_set_3& pointCloud, std::map& flattenedPts, const SearchTree& searchTree, - const std::unordered_map& pointCloudConnectivity) const { + const std::unordered_map& pointCloudConnectivity, + std::vector& constrainedEdges, + std::vector>& newPolys, + bool& isNextToBuilding) { + + typedef CGAL::Straight_skeleton_2 Ss; + typedef boost::shared_ptr> PolygonPtrWH; + typedef std::vector PolygonPtrVectorWH; + std::vector indices; std::vector originalHeights; - auto is_building_pt = pointCloud.property_map("is_building_point").first; + auto building_pt = pointCloud.property_map>("building_point").first; //-- Take tree subset bounded by the polygon std::vector subsetPts; Polygon_2 bbox = geomutils::calc_bbox_poly(_poly.rings().front()); @@ -234,23 +233,89 @@ bool PolyFeature::flatten_polygon_inner_points(const Point_set_3& pointCloud, Fuzzy_iso_box pts_range(bbox1, bbox2); searchTree.search(std::back_inserter(subsetPts), pts_range); - //-- Collect points that have not been already flattened + //-- Check if the polygon is overlapping with a building + std::map> overlappingBuildings; //id-building map for (auto& pt3 : subsetPts) { Point_2 pt(pt3.x(), pt3.y()); if (CGAL::bounded_side_2(_poly._rings.front().begin(), _poly._rings.front().end(), pt) != CGAL::ON_UNBOUNDED_SIDE) { auto itIdx = pointCloudConnectivity.find(pt3); - auto pointSetIt = pointCloud.begin(); std::advance(pointSetIt, itIdx->second); - if (is_building_pt[*pointSetIt]) - return false; //todo temp solution when having adjacent buildings - auto it = flattenedPts.find(itIdx->second); - if (it == flattenedPts.end()) { - indices.push_back(itIdx->second); - originalHeights.push_back(pointCloud.point(itIdx->second).z()); + auto currBuilding = building_pt[*pointSetIt]; + if (currBuilding != nullptr) { + int buildingId = currBuilding->get_internal_id(); + auto it = overlappingBuildings.find(buildingId); + if (it == overlappingBuildings.end()) { + overlappingBuildings[buildingId] = currBuilding; + } + } + } + } + if (!overlappingBuildings.empty()) isNextToBuilding = true; + //-- If next intersecting a building, clip poly with building + std::vector flattenCandidatePolys; + if (!overlappingBuildings.empty()) { + // Add clipping polys to set + CGAL::Polygon_set_2 polySet; + Converter to_exact; + for (auto& intersectBuilding : overlappingBuildings) { + polySet.insert(intersectBuilding.second->get_poly().get_exact_outer_boundary()); + } + // Clip with this polygon + polySet.complement(); + polySet.intersection(this->get_poly().get_exact()); + // Store this as the new polygon + std::vector> resPolys; + polySet.polygons_with_holes(std::back_inserter(resPolys)); +#ifdef CITY4CFD_POLYFEATURE_VERBOSE + std::cout << "After flatten polygon clipping, total new polygons: " << resPolys.size() << std::endl; +#endif + for (auto& flattenBndPoly : resPolys) { + flattenCandidatePolys.emplace_back(geomutils::exact_poly_to_poly(flattenBndPoly)); + } + } + std::vector flattenBndPolys; + if (!flattenCandidatePolys.empty()) { + bool isFirst = true; + for (auto& poly : flattenCandidatePolys) { + const double offsetVal = 0.1; // offset value hardcoded + PolygonPtrVectorWH offset_poly = + CGAL::create_interior_skeleton_and_offset_polygons_with_holes_2(offsetVal, + poly.get_cgal_type()); + if (offset_poly.size() == 1) { // make a check whether the offset is successfully created or not + flattenBndPolys.push_back(offset_poly.front()->outer_boundary()); + } else { + std::cout << "Skeleton construction failed!" << std::endl; + return false; + } + if (isFirst) { + _poly = Polygon_with_holes_2(*(offset_poly.front())); + isFirst = false; + } else { + // Some polys are cut -- save the new resulting polys and add them as new features in Map3D + newPolys.emplace_back(Polygon_with_holes_2(*(offset_poly.front())), _outputLayerID); + } + } + } else { + flattenBndPolys.push_back(_poly.outer_boundary()); + } + //-- Collect points that have not been already flattened + for (auto& pt3 : subsetPts) { + for (auto& flattenBndPoly : flattenBndPolys) { + Point_2 pt(pt3.x(), pt3.y()); + if (CGAL::bounded_side_2(flattenBndPoly.begin(), + flattenBndPoly.end(), + pt) != CGAL::ON_UNBOUNDED_SIDE) { + auto itIdx = pointCloudConnectivity.find(pt3); + + auto it = flattenedPts.find(itIdx->second); + if (it == flattenedPts.end()) { + indices.push_back(itIdx->second); + originalHeights.push_back(pointCloud.point(itIdx->second).z()); + } } } } @@ -265,6 +330,17 @@ bool PolyFeature::flatten_polygon_inner_points(const Point_set_3& pointCloud, for (auto& i : indices) { flattenedPts[i] = Point_3(pointCloud.point(i).x(), pointCloud.point(i).y(), avgHeight); } + //-- Add additional new segments to constrain + if (!flattenBndPolys.empty()) { + for (auto& flattenBndPoly : flattenBndPolys) { + for (const auto& newEdge: flattenBndPoly.edges()) { + ePoint_3 pt1(newEdge.point(0).x(), newEdge.point(0).y(), avgHeight); + ePoint_3 pt2(newEdge.point(1).x(), newEdge.point(1).y(), avgHeight); + constrainedEdges.emplace_back(pt1, pt2); + } + } + } + return true; } @@ -301,6 +377,10 @@ void PolyFeature::clear_feature() { _mesh.clear(); } +int PolyFeature::new_internal_id() { + return ++_numOfPolyFeatures; +} + Polygon_with_holes_2& PolyFeature::get_poly() { return _poly; } @@ -309,6 +389,12 @@ const Polygon_with_holes_2& PolyFeature::get_poly() const { return _poly; } +Polygon_with_attr PolyFeature::get_poly_w_attr() const { + Polygon_with_attr poly; + poly.polygon = _poly; + return poly; +} + const std::vector>& PolyFeature::get_ground_elevations() const { return _groundElevations; } diff --git a/src/PolyFeature.h b/src/PolyFeature.h index e6ec815f..2a1d7cc3 100644 --- a/src/PolyFeature.h +++ b/src/PolyFeature.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -34,17 +34,12 @@ class PolyFeature : public TopoFeature { public: PolyFeature(); PolyFeature(const int outputLayerID); - PolyFeature(const int outputLayerID, const int internalID); PolyFeature(const nlohmann::json& poly, const bool checkSimplicity = false); PolyFeature(const nlohmann::json& poly, const bool checkSimplicity, const int outputLayerID); - PolyFeature(const nlohmann::json& poly, const bool checkSimplicity, const int outputLayerID, const int internalID); PolyFeature(const nlohmann::json& poly, const int outputLayerID); - PolyFeature(const nlohmann::json& poly, const int outputLayerID, const int internalID); PolyFeature(const Polygon_with_attr& poly, const bool checkSimplicity = false); PolyFeature(const Polygon_with_attr& poly, const bool checkSimplicity, const int outputLayerID); - PolyFeature(const Polygon_with_attr& poly, const bool checkSimplicity, const int outputLayerID, const int internalID); PolyFeature(const Polygon_with_attr& poly, const int outputLayerID); - PolyFeature(const Polygon_with_attr& poly, const int outputLayerID, const int internalID); virtual ~PolyFeature(); void calc_footprint_elevation_nni(const DT& dt); @@ -54,8 +49,10 @@ class PolyFeature : public TopoFeature { double ground_elevation(); // double slope_height(); bool flatten_polygon_inner_points(const Point_set_3& pointCloud, std::map& flattenedPts, - const SearchTree& searchTree, const std::unordered_map& pointCloudConnectivity) const; + const SearchTree& searchTree, const std::unordered_map& pointCloudConnectivity, + std::vector& constrainedEdges, + std::vector>& newPolys, + bool& isNextToBuilding); void set_zero_borders(); void calc_min_bbox(); void clear_feature(); @@ -68,17 +65,21 @@ class PolyFeature : public TopoFeature { Polygon_with_holes_2& get_poly(); const Polygon_with_holes_2& get_poly() const; + Polygon_with_attr get_poly_w_attr() const; const std::vector>& get_ground_elevations() const; const int get_internal_id() const; MinBbox& get_min_bbox(); protected: + static int _numOfPolyFeatures; + int _polyInternalID; Polygon_with_holes_2 _poly; std::vector> _groundElevations; double _groundElevation; MinBbox _minBbox; + int new_internal_id(); void parse_json_poly(const nlohmann::json& poly, const bool checkSimplicity); }; diff --git a/src/ReconstructedBuilding.cpp b/src/ReconstructedBuilding.cpp index 3b920478..1c87faf7 100644 --- a/src/ReconstructedBuilding.cpp +++ b/src/ReconstructedBuilding.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -29,15 +29,12 @@ #include "geomutils.h" #include "LoD12.h" +#include "ImportedBuilding.h" ReconstructedBuilding::ReconstructedBuilding() : Building(), _attributeHeight(-global::largnum), _attributeHeightAdvantage(Config::get().buildingHeightAttrAdv) {} -ReconstructedBuilding::ReconstructedBuilding(const int internalID) - : Building(internalID), _attributeHeight(-global::largnum), - _attributeHeightAdvantage(Config::get().buildingHeightAttrAdv) {} - ReconstructedBuilding::ReconstructedBuilding(const Mesh& mesh) : ReconstructedBuilding() { _mesh = mesh; @@ -60,13 +57,13 @@ ReconstructedBuilding::ReconstructedBuilding(const nlohmann::json& poly) } */ -ReconstructedBuilding::ReconstructedBuilding(const nlohmann::json& poly, const int internalID) - : Building(poly, internalID), _attributeHeight(-global::largnum), +ReconstructedBuilding::ReconstructedBuilding(const nlohmann::json& poly) + : Building(poly), _attributeHeight(-global::largnum), _attributeHeightAdvantage(Config::get().buildingHeightAttrAdv) { if (!Config::get().buildingUniqueId.empty() && poly["properties"].contains(Config::get().buildingUniqueId)) { _id = poly["properties"][Config::get().buildingUniqueId].dump(); } else { - _id = std::to_string(internalID); + _id = std::to_string(_polyInternalID); } if (poly["properties"].contains(Config::get().buildingHeightAttribute)) { if (poly["properties"][Config::get().buildingHeightAttribute].is_number()) { @@ -84,15 +81,15 @@ ReconstructedBuilding::ReconstructedBuilding(const nlohmann::json& poly, const i } } -ReconstructedBuilding::ReconstructedBuilding(const Polygon_with_attr& poly, const int internalID) - : Building(poly, internalID), _attributeHeight(-global::largnum), +ReconstructedBuilding::ReconstructedBuilding(const Polygon_with_attr& poly) + : Building(poly), _attributeHeight(-global::largnum), _attributeHeightAdvantage(Config::get().buildingHeightAttrAdv) { // Check for the polygon ID attribute auto idIt = poly.attributes.find(Config::get().buildingUniqueId); if (idIt != poly.attributes.end()) { _id = idIt->second; } else { - _id = std::to_string(internalID); + _id = std::to_string(_polyInternalID); } // Check for the building height attribute auto buildingHeightAttrIt = poly.attributes.find(Config::get().buildingHeightAttribute); @@ -109,6 +106,14 @@ ReconstructedBuilding::ReconstructedBuilding(const Polygon_with_attr& poly, cons } } +ReconstructedBuilding::ReconstructedBuilding(const std::shared_ptr& importedBuilding) + : Building(importedBuilding->get_poly_w_attr()), + _attributeHeight(-global::largnum), _attributeHeightAdvantage(Config::get().buildingHeightAttrAdv) { + _ptsPtr = importedBuilding->get_points(); + _groundElevations = importedBuilding->get_ground_elevations(); + _id = importedBuilding->get_id(); +} + ReconstructedBuilding::~ReconstructedBuilding() = default; /* diff --git a/src/ReconstructedBuilding.h b/src/ReconstructedBuilding.h index 3536186b..79fb713f 100644 --- a/src/ReconstructedBuilding.h +++ b/src/ReconstructedBuilding.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -33,11 +33,11 @@ class ReconstructedBuilding : public Building { public: ReconstructedBuilding(); - ReconstructedBuilding(const int internalID); ReconstructedBuilding(const Mesh& mesh); // ReconstructedBuilding(const nlohmann::json& poly); - ReconstructedBuilding(const nlohmann::json& poly, const int internalID); - ReconstructedBuilding(const Polygon_with_attr& poly, const int internalID); + ReconstructedBuilding(const nlohmann::json& poly); + ReconstructedBuilding(const Polygon_with_attr& poly); + ReconstructedBuilding(const std::shared_ptr& importedBuilding); ~ReconstructedBuilding(); virtual double get_elevation() override; diff --git a/src/Sides.cpp b/src/Sides.cpp index 5fe4d81c..63778516 100644 --- a/src/Sides.cpp +++ b/src/Sides.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/Sides.h b/src/Sides.h index 47ad608b..a4691147 100644 --- a/src/Sides.h +++ b/src/Sides.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/SurfaceLayer.cpp b/src/SurfaceLayer.cpp index 7a3f23ce..10b00677 100644 --- a/src/SurfaceLayer.cpp +++ b/src/SurfaceLayer.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/SurfaceLayer.h b/src/SurfaceLayer.h index 2bd5386c..a05d4af0 100644 --- a/src/SurfaceLayer.h +++ b/src/SurfaceLayer.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/Terrain.cpp b/src/Terrain.cpp index cfb0e643..acbf22ea 100644 --- a/src/Terrain.cpp +++ b/src/Terrain.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -30,6 +30,7 @@ #include "geomutils.h" #include "io.h" #include "SurfaceLayer.h" +#include "Building.h" Terrain::Terrain() : TopoFeature(0), _cdt(), _surfaceLayersTerrain(), @@ -64,7 +65,7 @@ void Terrain::set_cdt(const Point_set_3& pointCloud) { void Terrain::prep_constraints(const PolyFeaturesPtr& features, Point_set_3& pointCloud) { std::cout << " Lifting polygon edges to terrain elevation" << std::endl; int countFeatures = 0; - auto is_building_pt = pointCloud.property_map("is_building_point").first; + auto building_pt = pointCloud.add_property_map>("building_point", nullptr).first; for (auto& f : features) { if (!f->is_active()) continue; bool is_building = false; @@ -78,7 +79,7 @@ void Terrain::prep_constraints(const PolyFeaturesPtr& features, Point_set_3& poi for (auto& polyVertex : ring) { pts.push_back(ePoint_3(polyVertex.x(), polyVertex.y(), elevations[polyCount][i])); auto it = pointCloud.insert(Point_3(polyVertex.x(), polyVertex.y(), elevations[polyCount][i++])); - if (is_building) is_building_pt[*it] = true; + if (is_building) building_pt[*it] = std::static_pointer_cast(f); } _constrainedPolys.push_back(pts); ++polyCount; diff --git a/src/Terrain.h b/src/Terrain.h index 4fa309ca..6cfc92a8 100644 --- a/src/Terrain.h +++ b/src/Terrain.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/Top.cpp b/src/Top.cpp index 77329be9..c7c30dbe 100644 --- a/src/Top.cpp +++ b/src/Top.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/Top.h b/src/Top.h index 35cf93ac..42cccabe 100644 --- a/src/Top.h +++ b/src/Top.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/TopoFeature.cpp b/src/TopoFeature.cpp index 3d7751a6..5d5470cc 100644 --- a/src/TopoFeature.cpp +++ b/src/TopoFeature.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/TopoFeature.h b/src/TopoFeature.h index a5a5defd..5b450663 100644 --- a/src/TopoFeature.h +++ b/src/TopoFeature.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/configSchema.inc b/src/configSchema.inc index 4f9cf236..aff0a59b 100644 --- a/src/configSchema.inc +++ b/src/configSchema.inc @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -406,7 +406,7 @@ namespace jsonschema { ], "required": [ "point_of_interest", "influence_region", "domain_bnd", "building_percentile", "edge_max_len", "output_file_name", "output_format", - "output_separately" ] + "output_separately", "lod"] } )"_json; } diff --git a/src/geomutils.cpp b/src/geomutils.cpp index 7491e2fb..03e75feb 100644 --- a/src/geomutils.cpp +++ b/src/geomutils.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -234,6 +234,27 @@ bool geomutils::polygons_in_contact(const Polygon_with_holes_2& firstPoly, const return false; } +Polygon_with_holes_2 geomutils::exact_poly_to_poly(const CGAL::Polygon_with_holes_2& exactPoly) { + Converter to_inexact; + Polygon_with_holes_2 convertedPoly; + Polygon_2 transferKernelPoly; + for (auto& outerPt : exactPoly.outer_boundary()) { + Point_2 polyPt = to_inexact(outerPt); + transferKernelPoly.push_back(polyPt); + } + convertedPoly.rings().push_back(transferKernelPoly); + + for (auto& hole : exactPoly.holes()) { + transferKernelPoly.clear(); + for (auto& pt : hole) { + Point_2 polyPt = to_inexact(pt); + transferKernelPoly.push_back(polyPt); + } + convertedPoly.rings().push_back(transferKernelPoly); + } + return convertedPoly; +} + //-- Templated functions //-- Check if the point is inside a polygon on a 2D projection template diff --git a/src/geomutils.h b/src/geomutils.h index 04eddb9d..29f6bc36 100644 --- a/src/geomutils.h +++ b/src/geomutils.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -44,6 +44,7 @@ namespace geomutils { Point_3 rotate_pt_xy(const Point_3& pt, const double angle, Point_2 centerPt = Point_2(0, 0)); void interpolate_poly_from_pc(const Polygon_2& poly, std::vector& elevations, const Point_set_3& pointCloud); bool polygons_in_contact(const Polygon_with_holes_2& firstPoly, const Polygon_with_holes_2& secondPoly); + Polygon_with_holes_2 exact_poly_to_poly(const CGAL::Polygon_with_holes_2& exactPoly); void remove_self_intersections(Mesh& mesh); //-- Templated functions diff --git a/src/io.cpp b/src/io.cpp index a11279cd..b938fb5c 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/io.h b/src/io.h index 4020d0de..ac2df9e2 100644 --- a/src/io.h +++ b/src/io.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/src/main.cpp b/src/main.cpp index c27d64dc..3b5ac231 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -31,7 +31,7 @@ #include -std::string CITY4CFD_VERSION = "0.4.3"; +std::string CITY4CFD_VERSION = "0.4.4"; void printWelcome() { auto logo{ @@ -57,7 +57,7 @@ void printWelcome() { }; std::cout << logo; - std::cout << "City4CFD Copyright (C) 2021-2023 3D Geoinformation Research Group, TU Delft\n" << std::endl; + std::cout << "City4CFD Copyright (C) 2021-2024 3D Geoinformation Research Group, TU Delft\n" << std::endl; } void printHelp() { diff --git a/src/types.h b/src/types.h index 52628763..69a0330a 100644 --- a/src/types.h +++ b/src/types.h @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -30,6 +30,14 @@ #include "nlohmann/json.hpp" +#ifndef M_PI +#define M_PI 3.14159265358979323846264338327950288 +#endif + +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923132169163975144 +#endif + //-- Typedefs for smart pointers class Building; class Boundary; class TopoFeature; class PolyFeature; class Terrain; class SurfaceLayer; @@ -73,4 +81,4 @@ namespace global { const double smallnum = 1e-7; } -#endif //CITY4CFD_TYPES_H \ No newline at end of file +#endif //CITY4CFD_TYPES_H diff --git a/thirdparty/CGAL/include/CGAL/IO/read_las_points.h b/thirdparty/CGAL/include/CGAL/IO/read_las_points.h index a2701b96..aab96cbf 100644 --- a/thirdparty/CGAL/include/CGAL/IO/read_las_points.h +++ b/thirdparty/CGAL/include/CGAL/IO/read_las_points.h @@ -44,9 +44,9 @@ # pragma GCC diagnostic ignored "-Wstrict-aliasing" #endif -#define USE_AS_DLL +#define COMPILE_AS_DLL 1 #include -#undef USE_AS_DLL +#undef COMPILE_AS_DLL #ifdef __GNUC__ # pragma GCC diagnostic pop diff --git a/thirdparty/CGAL/include/CGAL/IO/write_las_points.h b/thirdparty/CGAL/include/CGAL/IO/write_las_points.h index 0a67f575..2795d7a7 100644 --- a/thirdparty/CGAL/include/CGAL/IO/write_las_points.h +++ b/thirdparty/CGAL/include/CGAL/IO/write_las_points.h @@ -38,11 +38,11 @@ # pragma GCC diagnostic ignored "-Wstrict-aliasing" #endif -#define USE_AS_DLL +#define COMPILE_AS_DLL 1 #include #include #include -#undef USE_AS_DLL +#undef COMPILE_AS_DLL #ifdef __GNUC__ # pragma GCC diagnostic pop diff --git a/thirdparty/CSF/CMakeLists.txt b/thirdparty/CSF/CMakeLists.txt index e0563d5b..2300e32c 100644 --- a/thirdparty/CSF/CMakeLists.txt +++ b/thirdparty/CSF/CMakeLists.txt @@ -3,4 +3,4 @@ project(CSF LANGUAGES CXX) add_subdirectory(src) -set(BUILD_SHARED_LIBS "Build as shared library" OFF) \ No newline at end of file +set(BUILD_SHARED_LIBS "Build as shared library" OFF) diff --git a/thirdparty/CSF/src/CMakeLists.txt b/thirdparty/CSF/src/CMakeLists.txt index 45429ad0..ea3f840b 100644 --- a/thirdparty/CSF/src/CMakeLists.txt +++ b/thirdparty/CSF/src/CMakeLists.txt @@ -22,7 +22,9 @@ set(CSF_HEADERS ) add_library(CSF ${CSF_SOURCES} ${CSF_HEADERS}) -target_compile_options(CSF PRIVATE -Werror -Wall -Wextra -Wno-unused-private-field) +if (!MSVC) + target_compile_options(CSF PRIVATE -Werror -Wall -Wextra -Wno-unused-private-field) +endif() if (OpenMP_CXX_FOUND) target_link_libraries(CSF PUBLIC OpenMP::OpenMP_CXX) diff --git a/thirdparty/LAStools/CMakeLists.txt b/thirdparty/LAStools/CMakeLists.txt index e6a66bea..67ffe79d 100644 --- a/thirdparty/LAStools/CMakeLists.txt +++ b/thirdparty/LAStools/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.9) set(CMAKE_SUPPRESS_REGENERATION true) project("LAStools") @@ -6,12 +6,12 @@ add_definitions(-w) if (!MSVC) add_compile_options(-O3 -Wall -Wno-strict-aliasing) + option(BUILD_SHARED_LIBS "Build LASlib as DLL" OFF) else() - add_definitions(-D_CRT_SECURE_NO_WARNINGS) + add_compile_options(-O2) + add_definitions(-DCOMPILE_AS_DLL -D_CRT_SECURE_NO_WARNINGS) endif() -option(BUILD_SHARED_LIBS "Build LASlib as DLL" OFF) - if (BUILD_SHARED_LIBS AND UNIX AND NOT APPLE) set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib/LASlib") endif() diff --git a/tools/prepare_point_cloud/CMakeLists.txt b/tools/prepare_point_cloud/CMakeLists.txt index 18da6b6d..f901acb4 100644 --- a/tools/prepare_point_cloud/CMakeLists.txt +++ b/tools/prepare_point_cloud/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.9) project(city4cfd_pcprep) -set(CMAKE_CXX_FLAGS "-O3") +set(CMAKE_CXX_FLAGS "-O2") set(CMAKE_BUILD_TYPE "Release") #if (COMMAND cmake_policy) diff --git a/tools/prepare_point_cloud/src/PCconfigSchema.inc b/tools/prepare_point_cloud/src/PCconfigSchema.inc index fa8f588b..406c929a 100644 --- a/tools/prepare_point_cloud/src/PCconfigSchema.inc +++ b/tools/prepare_point_cloud/src/PCconfigSchema.inc @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. diff --git a/tools/prepare_point_cloud/src/city4cfd_pcprep.cpp b/tools/prepare_point_cloud/src/city4cfd_pcprep.cpp index 586abd7e..e9218ac2 100644 --- a/tools/prepare_point_cloud/src/city4cfd_pcprep.cpp +++ b/tools/prepare_point_cloud/src/city4cfd_pcprep.cpp @@ -1,7 +1,7 @@ /* City4CFD - Copyright (c) 2021-2023, 3D Geoinformation Research Group, TU Delft + Copyright (c) 2021-2024, 3D Geoinformation Research Group, TU Delft This file is part of City4CFD. @@ -81,7 +81,7 @@ void printWelcome() { )" }; std::cout << logo; - std::cout << "City4CFD Copyright (C) 2021-2023 3D Geoinformation Research Group, TU Delft\n" << std::endl; + std::cout << "City4CFD Copyright (C) 2021-2024 3D Geoinformation Research Group, TU Delft\n" << std::endl; std::cout << info; }