├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitlab-ci.yml ├── .gitlab └── codechecker_skip.lst ├── .idea ├── .gitignore ├── CloudTools.iml ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── misc.xml └── vcs.xml ├── AHN.Buildings.Aggregate ├── CMakeLists.txt ├── Region.h └── main.cpp ├── AHN.Buildings.MPI ├── CMakeLists.txt └── main.cpp ├── AHN.Buildings.Parallel ├── CMakeLists.txt └── main.cpp ├── AHN.Buildings.Verify ├── CMakeLists.txt ├── Coverage.h └── main.cpp ├── AHN.Buildings ├── BuildingChangeDetection.hpp ├── BuildingExtraction.cpp ├── BuildingExtraction.h ├── CMakeLists.txt ├── Comparison.cpp ├── Comparison.h ├── ContourClassification.cpp ├── ContourClassification.h ├── ContourConvexHullRasterizer.cpp ├── ContourConvexHullRasterizer.h ├── ContourDetection.cpp ├── ContourDetection.h ├── ContourFiltering.cpp ├── ContourFiltering.h ├── ContourSimplification.cpp ├── ContourSimplification.h ├── ContourSplitting.cpp ├── ContourSplitting.h ├── IOMode.cpp ├── IOMode.h ├── Process.cpp ├── Process.h └── main.cpp ├── BUILD.md ├── CMakeLists.txt ├── CMakeSettings.json.sample ├── CONTRIBUTING.md ├── CloudTools.Common ├── CMakeLists.txt ├── Helper.h ├── IO │ ├── IO.cpp │ ├── IO.h │ ├── Reporter.cpp │ ├── Reporter.h │ ├── Result.cpp │ ├── Result.h │ ├── ResultCollection.cpp │ └── ResultCollection.h ├── Operation.cpp └── Operation.h ├── CloudTools.DEM.Difference ├── CMakeLists.txt └── main.cpp ├── CloudTools.DEM.Mask ├── CMakeLists.txt └── main.cpp ├── CloudTools.DEM ├── Algorithms │ ├── HierachicalClustering.hpp │ ├── MatrixTransformation.cpp │ └── MatrixTransformation.h ├── CMakeLists.txt ├── Calculation.cpp ├── Calculation.h ├── ClusterMap.cpp ├── ClusterMap.h ├── Color.cpp ├── Color.h ├── Comparers │ └── Difference.hpp ├── Creation.cpp ├── Creation.h ├── DatasetCalculation.hpp ├── DatasetTransformation.hpp ├── Filters │ ├── ClusterFilter.hpp │ ├── MajorityFilter.hpp │ ├── MorphologyFilter.hpp │ └── NoiseFilter.hpp ├── Helper.cpp ├── Helper.h ├── Metadata.cpp ├── Metadata.h ├── Rasterize.cpp ├── Rasterize.h ├── SweepLineCalculation.hpp ├── SweepLineTransformation.hpp ├── Transformation.cpp ├── Transformation.h └── Window.hpp ├── CloudTools.Vegetation.Verify ├── CMakeLists.txt └── main.cpp ├── CloudTools.Vegetation ├── BuildingFacadeSeedRemoval.hpp ├── CMakeLists.txt ├── CentroidDistance.cpp ├── CentroidDistance.h ├── DistanceCalculation.h ├── EliminateNonTrees.cpp ├── EliminateNonTrees.h ├── HausdorffDistance.cpp ├── HausdorffDistance.h ├── HeightDifference.cpp ├── HeightDifference.h ├── InterpolateNoData.cpp ├── InterpolateNoData.h ├── MorphologyClusterFilter.cpp ├── MorphologyClusterFilter.h ├── NoiseFilter.cpp ├── NoiseFilter.h ├── PostProcess.cpp ├── PostProcess.h ├── PreProcess.cpp ├── PreProcess.h ├── RiverMask.hpp ├── TreeCrownSegmentation.cpp ├── TreeCrownSegmentation.h ├── VolumeDifference.cpp ├── VolumeDifference.h └── main.cpp ├── Folder.DotSettings ├── LICENSE ├── README.md ├── Shell.bat ├── Shell.config.cmd.sample ├── WINDOWS_QUICK_START.md └── doc ├── UML_Buildings_Model.png ├── UML_Operation_Model.png ├── UML_Vegetation_Model.png ├── screenshot_building_small.png └── screenshot_vegetation_small.png /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build project 2 | 3 | on: [push] 4 | 5 | env: 6 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 7 | BUILD_TYPE: Release 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | os: [ubuntu-20.04, ubuntu-22.04] 14 | 15 | runs-on: ${{ matrix.os }} 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | 20 | - name: Update package repository 21 | run: sudo apt-get update 22 | 23 | - name: Install required packages 24 | run: sudo apt-get install libboost-all-dev libgdal-dev libopencv-dev libopenmpi-dev 25 | 26 | - name: Create build environment 27 | run: cmake -E make_directory ${{runner.workspace}}/build 28 | 29 | - name: Configure CMake 30 | working-directory: ${{runner.workspace}}/build 31 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE 32 | 33 | - name: Build 34 | working-directory: ${{runner.workspace}}/build 35 | run: cmake --build . --config $BUILD_TYPE -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - build 3 | - test 4 | 5 | variables: 6 | # No interactive timezone dialog for tzdata 7 | DEBIAN_FRONTEND: noninteractive 8 | 9 | # SAST 10 | include: 11 | - template: Jobs/SAST.gitlab-ci.yml 12 | 13 | # Build 14 | .build: 15 | stage: build 16 | before_script: 17 | - apt-get update -yqq 18 | - apt-get install -yqq build-essential cmake 19 | - apt-get install -yqq libboost-all-dev libgdal-dev libopencv-dev 20 | - apt-get install -yqq libopenmpi-dev 21 | script: 22 | - mkdir -p build 23 | - cd build 24 | - cmake .. 25 | - make 26 | 27 | build_focal: 28 | extends: .build 29 | image: ubuntu:20.04 30 | 31 | build_jammy: 32 | extends: .build 33 | image: ubuntu:22.04 34 | 35 | # Code Quality with CodeChecker 36 | cq_codecheker: 37 | stage: test 38 | image: tmselte/evaluator:cpp-ubuntu-22.04 39 | extends: .build 40 | script: 41 | - mkdir -p build 42 | - cd build 43 | - cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 .. 44 | - cd .. 45 | # Analyze the project 46 | - > 47 | CodeChecker analyze 48 | --analyzers clangsa clang-tidy 49 | --ctu 50 | -o ./reports 51 | ./build/compile_commands.json 52 | # Create the report file 53 | - > 54 | CodeChecker parse 55 | --trim-path-prefix $(pwd) 56 | -e codeclimate 57 | -i .gitlab/codechecker_skip.lst 58 | ./reports > gl-code-quality-report.json || true 59 | # Note: 'CodeChecker parse' would return a non-zero exit code when any issues found 60 | - test -f gl-code-quality-report.json 61 | artifacts: 62 | expose_as: Code Quality Report 63 | reports: 64 | codequality: gl-code-quality-report.json 65 | paths: [gl-code-quality-report.json] 66 | expire_in: 2 weeks 67 | -------------------------------------------------------------------------------- /.gitlab/codechecker_skip.lst: -------------------------------------------------------------------------------- 1 | -/usr/* -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml 3 | 4 | # Thrift plugin 5 | /thriftCompiler.xml 6 | -------------------------------------------------------------------------------- /.idea/CloudTools.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 44 | 45 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /AHN.Buildings.Aggregate/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(../) 2 | 3 | add_executable(ahn_buildings_agg 4 | main.cpp 5 | Region.h) 6 | target_link_libraries(ahn_buildings_agg 7 | dem common) 8 | 9 | install(TARGETS ahn_buildings_agg 10 | DESTINATION ${CMAKE_INSTALL_PREFIX}) 11 | -------------------------------------------------------------------------------- /AHN.Buildings.Aggregate/Region.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace AHN 4 | { 5 | /// 6 | /// Represents an administrative region. 7 | /// 8 | struct Region 9 | { 10 | /// 11 | /// The identifier of the region. 12 | /// 13 | int id; 14 | /// 15 | /// The cumulative altimetry gained. 16 | /// 17 | float gained; 18 | /// 19 | /// The cumulative altimetry lost. 20 | /// 21 | float lost; 22 | /// 23 | /// The cumulative altimetry moved (gained + lost). 24 | /// 25 | float moved; 26 | /// 27 | /// The cumulative altimetry difference (gained - lost). 28 | /// 29 | float difference; 30 | }; 31 | } // AHN 32 | -------------------------------------------------------------------------------- /AHN.Buildings.MPI/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(../) 2 | include_directories(${MPI_CXX_INCLUDE_PATH}) 3 | 4 | link_libraries(${MPI_CXX_LIBRARIES}) 5 | 6 | add_executable(ahn_buildings_mpi 7 | main.cpp) 8 | target_link_libraries(ahn_buildings_mpi 9 | ahn_buildings 10 | dem common) 11 | 12 | install(TARGETS ahn_buildings_mpi 13 | DESTINATION ${CMAKE_INSTALL_PREFIX}) 14 | -------------------------------------------------------------------------------- /AHN.Buildings.Parallel/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(../) 2 | 3 | add_executable(ahn_buildings_par 4 | main.cpp) 5 | target_link_libraries(ahn_buildings_par 6 | ahn_buildings 7 | dem common 8 | Threads::Threads) 9 | 10 | install(TARGETS ahn_buildings_par 11 | DESTINATION ${CMAKE_INSTALL_PREFIX}) 12 | -------------------------------------------------------------------------------- /AHN.Buildings.Verify/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(../) 2 | 3 | add_executable(ahn_buildings_ver 4 | main.cpp 5 | Coverage.h) 6 | target_link_libraries(ahn_buildings_ver 7 | dem common) 8 | 9 | install(TARGETS ahn_buildings_ver 10 | DESTINATION ${CMAKE_INSTALL_PREFIX}) 11 | -------------------------------------------------------------------------------- /AHN.Buildings.Verify/Coverage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | enum Coverage : GByte 6 | { 7 | NoData = 0, 8 | Accept = 1, 9 | Reject = 2 10 | }; 11 | -------------------------------------------------------------------------------- /AHN.Buildings/BuildingChangeDetection.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace AHN 9 | { 10 | namespace Buildings 11 | { 12 | /// 13 | /// Change detection for builings using surface data and building areas. 14 | /// 15 | 16 | class BuildingChangeDetection: public CloudTools::DEM::DatasetTransformation 17 | { 18 | public: 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | /// The AHN-2 dataset of the comparison. 23 | /// The AHN-3 dataset of the comparison. 24 | /// The AHN-2 surface data of the comparison. 25 | /// The AHN-3 surface data of the comparison. 26 | /// The target path of the comparison. 27 | /// he callback method to report progress. 28 | BuildingChangeDetection(GDALDataset* ahn2Dataset, GDALDataset* ahn3Dataset, 29 | GDALDataset* ahn2Surface, GDALDataset* ahn3Surface, 30 | const std::string& targetPath, 31 | CloudTools::Operation::ProgressType progress = nullptr) 32 | : CloudTools::DEM::DatasetTransformation( 33 | std::vector{ ahn2Dataset, ahn3Dataset, ahn2Surface, ahn3Surface }, targetPath, nullptr, progress) 34 | { 35 | initialize(); 36 | } 37 | 38 | 39 | BuildingChangeDetection(const BuildingChangeDetection&) = delete; 40 | BuildingChangeDetection& operator=(const BuildingChangeDetection&) = delete; 41 | 42 | private: 43 | /// 44 | /// Initializes the new instance of the class. 45 | /// 46 | void initialize(); 47 | }; 48 | 49 | void BuildingChangeDetection::initialize() 50 | { 51 | this->nodataValue = 0; 52 | 53 | this->computation = [this](int sizeX, int sizeY) 54 | { 55 | std::map, std::pair> stat; 56 | std::map > pairings2; 57 | std::map > pairings3; 58 | 59 | for (int i = 0; i < sizeX; ++i) 60 | for (int j = 0; j < sizeY; ++j) 61 | { 62 | uint32_t ahn2 = this->sourceData(0, i, j); 63 | uint32_t ahn3 = this->sourceData(1, i, j); 64 | float ahn2H = this->sourceData(2, i, j); 65 | float ahn3H = this->sourceData(3, i, j); 66 | auto pair = std::make_pair(ahn2, ahn3); 67 | 68 | if ((ahn2 > 0 || ahn3 > 0) && ahn2H < 10000 && ahn3H < 10000) // todo: refactor magical constant 69 | { 70 | stat[pair].second += (1. / (stat[pair].first + 1)) * (ahn3H - ahn2H - stat[pair].second); 71 | } 72 | stat[pair].first++; 73 | if (ahn2 > 0) pairings2[ahn2].insert(ahn3); 74 | if (ahn3 > 0) pairings3[ahn3].insert(ahn2); 75 | } 76 | 77 | std::set > type2; // only AHN2 78 | std::set > type3; // only AHN3 79 | std::set > type5; // unchanged AHN2-3 80 | std::set > type8; // changed AHN2-3 81 | std::set > type9; // reconstructed (unchanged) 82 | 83 | for (auto const &entry : stat) 84 | { 85 | uint32_t id2 = entry.first.first; 86 | uint32_t id3 = entry.first.second; 87 | if (id2 > 0 || id3 > 0) 88 | { 89 | for (uint32_t id : pairings2[id2]) 90 | { 91 | auto pair = std::make_pair(id2, id); 92 | const auto &data = stat[pair]; 93 | if (id > 0) 94 | { 95 | if (abs(data.second) <= 2) type5.insert(pair); 96 | else type8.insert(pair); 97 | } 98 | else if (abs(data.second) >= 2) type2.insert(pair); 99 | else if (abs(data.second) <= 2) type9.insert(pair); 100 | } 101 | for (uint32_t id : pairings3[id3]) 102 | { 103 | auto pair = std::make_pair(id, id3); 104 | const auto &data = stat[pair]; 105 | if (id > 0) 106 | { 107 | if (abs(data.second) <= 2) type5.insert(pair); 108 | else type8.insert(pair); 109 | } 110 | else if (abs(data.second) >= 2) type3.insert(pair); 111 | else if (abs(data.second) <= 2) type9.insert(pair); 112 | } 113 | } 114 | } 115 | 116 | for (int i = 0; i < sizeX; ++i) 117 | for (int j = 0; j < sizeY; ++j) 118 | { 119 | uint32_t ahn2 = this->sourceData(0, i, j); 120 | uint32_t ahn3 = this->sourceData(1, i, j); 121 | 122 | std::pair p(ahn2, ahn3); 123 | bool significant = stat[p].first > 150; 124 | int result = this->nodataValue; 125 | if (significant) 126 | { 127 | if (type2.find(p) != type2.end()) result = 2; 128 | else if (type3.find(p) != type3.end()) result = 3; 129 | else if (type5.find(p) != type5.end()) result = 5; 130 | else if (type8.find(p) != type8.end()) result = 8; 131 | else if (type9.find(p) != type9.end()) result = 9; 132 | } 133 | this->setTargetData(i, j, result); 134 | } 135 | }; 136 | } 137 | 138 | } // Buildings 139 | } // AHN -------------------------------------------------------------------------------- /AHN.Buildings/BuildingExtraction.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "BuildingExtraction.h" 4 | 5 | using namespace CloudTools::DEM; 6 | 7 | namespace AHN 8 | { 9 | namespace Buildings 10 | { 11 | BuildingExtraction::BuildingExtraction(GDALDataset* surfaceDataset, GDALDataset* terrainDataset, 12 | const std::string& targetPath, 13 | ProgressType progress) 14 | : SweepLineTransformation(std::vector{surfaceDataset, terrainDataset}, 15 | targetPath, 0, nullptr, progress) 16 | { 17 | this->computation = [this](int x, int y, const std::vector>& sources) 18 | { 19 | const Window& surface = sources[0]; 20 | const Window& terrain = sources[1]; 21 | 22 | if (!terrain.hasData() && surface.hasData()) 23 | return static_cast(255); 24 | return static_cast(this->nodataValue); 25 | }; 26 | this->nodataValue = 0; 27 | } 28 | } // Buildings 29 | } // AHN 30 | -------------------------------------------------------------------------------- /AHN.Buildings/BuildingExtraction.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace AHN 8 | { 9 | namespace Buildings 10 | { 11 | /// 12 | /// Represents a building (object) extractor from surface and terrain DEM datasets. 13 | /// 14 | /// 15 | /// The terrain dataset should be non-interpolated, containing nodata-value at the location of buildings. 16 | /// 17 | class BuildingExtraction : public CloudTools::DEM::SweepLineTransformation 18 | { 19 | public: 20 | /// 21 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 22 | /// 23 | /// The surface dataset of the computation. 24 | /// The non-interpolated terrain dataset of the computation. 25 | /// The target filter map of the computation. 26 | /// The callback method to report progress. 27 | BuildingExtraction(GDALDataset* surfaceDataset, GDALDataset* terrainDataset, 28 | const std::string& targetPath, 29 | ProgressType progress = nullptr); 30 | BuildingExtraction(const BuildingExtraction&) = delete; 31 | BuildingExtraction& operator=(const BuildingExtraction&) = delete; 32 | }; 33 | } // Buildings 34 | } // AHN 35 | -------------------------------------------------------------------------------- /AHN.Buildings/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(../) 2 | 3 | add_library(ahn_buildings 4 | BuildingExtraction.cpp BuildingExtraction.h 5 | BuildingChangeDetection.hpp 6 | ContourDetection.cpp ContourDetection.h 7 | ContourFiltering.cpp ContourFiltering.h 8 | ContourSplitting.cpp ContourSplitting.h 9 | ContourSimplification.cpp ContourSimplification.h 10 | ContourClassification.cpp ContourClassification.h 11 | ContourConvexHullRasterizer.cpp ContourConvexHullRasterizer.h 12 | Comparison.cpp Comparison.h 13 | IOMode.cpp IOMode.h 14 | Process.cpp Process.h) 15 | 16 | add_executable(ahn_buildings_sim 17 | main.cpp) 18 | target_link_libraries(ahn_buildings_sim 19 | ahn_buildings 20 | dem common) 21 | 22 | install(TARGETS ahn_buildings_sim 23 | DESTINATION ${CMAKE_INSTALL_PREFIX}) 24 | -------------------------------------------------------------------------------- /AHN.Buildings/Comparison.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include "Comparison.h" 5 | 6 | using namespace CloudTools::DEM; 7 | 8 | namespace AHN 9 | { 10 | namespace Buildings 11 | { 12 | Comparison::Comparison(GDALDataset* ahn2Dataset, GDALDataset* ahn3Dataset, 13 | const std::string& targetPath, 14 | ProgressType progress) 15 | : SweepLineTransformation(std::vector{ahn2Dataset, ahn3Dataset}, targetPath, 0, nullptr, progress) 16 | { 17 | this->computation = [this](int x, int y, const std::vector>& sources) 18 | { 19 | const Window& ahn2 = sources[0]; 20 | const Window& ahn3 = sources[1]; 21 | 22 | if (!ahn2.hasData() || !ahn3.hasData()) 23 | return static_cast(this->nodataValue); 24 | 25 | float difference = ahn3.data() - ahn2.data(); 26 | if (std::abs(difference) >= this->maximumThreshold || std::abs(difference) <= this->minimumThreshold) 27 | difference = static_cast(this->nodataValue); 28 | return difference; 29 | }; 30 | this->nodataValue = 0; 31 | } 32 | 33 | Comparison::Comparison(GDALDataset* ahn2Dataset, GDALDataset* ahn3Dataset, 34 | GDALDataset* ahn2Filter, GDALDataset* ahn3Filter, 35 | const std::string& targetPath, 36 | ProgressType progress) 37 | : SweepLineTransformation(std::vector{ahn2Dataset, ahn3Dataset, ahn2Filter, ahn3Filter}, 38 | targetPath, 0, nullptr, progress) 39 | { 40 | this->computation = [this](int x, int y, const std::vector>& sources) 41 | { 42 | const Window& ahn2Data = sources[0]; 43 | const Window& ahn3Data = sources[1]; 44 | const Window& ahn2Filter = sources[2]; 45 | const Window& ahn3Filter = sources[3]; 46 | 47 | /* 48 | * Since AHN-3 is incomplete, side tiles are partial, 49 | * resulting in false positive detection of mass building demolition 50 | * when relying only on the filter laysers. 51 | * TODO: this removes demolitions over water (e.g. TU Delft Faculty of Architecture building.) 52 | */ 53 | if (!ahn2Filter.hasData() && !ahn3Filter.hasData() || 54 | !ahn3Data.hasData()) 55 | return static_cast(this->nodataValue); 56 | 57 | float difference = 0.f; 58 | if (ahn2Data.hasData() && ahn3Data.hasData()) 59 | difference = ahn3Data.data() - ahn2Data.data(); 60 | else if (ahn2Data.hasData()) 61 | difference = -ahn2Data.data(); 62 | else if (ahn3Data.hasData()) 63 | difference = ahn3Data.data(); 64 | 65 | if (std::abs(difference) >= this->maximumThreshold || std::abs(difference) <= this->minimumThreshold) 66 | difference = static_cast(this->nodataValue); 67 | return difference; 68 | }; 69 | this->nodataValue = 0; 70 | } 71 | } // Buildings 72 | } // AHN 73 | -------------------------------------------------------------------------------- /AHN.Buildings/Comparison.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace AHN 8 | { 9 | namespace Buildings 10 | { 11 | /// 12 | /// Represents a difference comparison for AHN-2 & AHN-3 datasets. 13 | /// 14 | class Comparison : public CloudTools::DEM::SweepLineTransformation 15 | { 16 | public: 17 | /// 18 | /// Maximum threshold of change. 19 | /// 20 | /// 21 | /// AHN elevation data is defined in meters. 22 | /// 23 | double maximumThreshold = 1000; 24 | /// 25 | /// Minimum threshold of change. 26 | /// 27 | /// 28 | /// AHN elevation data is defined in meters. 29 | /// The theoratical error-threshold of measurements between AHN-2 and AHN-3 is 0.35m. 30 | /// 31 | double minimumThreshold = 0.4; 32 | 33 | public: 34 | /// 35 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 36 | /// 37 | /// The AHN-2 dataset of the comparison. 38 | /// The AHN-3 dataset of the comparison. 39 | /// The target path of the comparison. 40 | /// he callback method to report progress. 41 | Comparison(GDALDataset* ahn2Dataset, GDALDataset* ahn3Dataset, 42 | const std::string& targetPath, 43 | ProgressType progress = nullptr); 44 | 45 | /// 46 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 47 | /// 48 | /// The AHN-2 dataset of the comparison. 49 | /// The AHN-3 dataset of the comparison. 50 | /// The AHN-2 building filter of the comparison. 51 | /// The AHN-3 building filter of the comparison. 52 | /// The target path of the comparison. 53 | /// he callback method to report progress. 54 | Comparison(GDALDataset* ahn2Dataset, GDALDataset* ahn3Dataset, 55 | GDALDataset* ahn2Filter, GDALDataset* ahn3Filter, 56 | const std::string& targetPath, 57 | ProgressType progress = nullptr); 58 | 59 | Comparison(const Comparison&) = delete; 60 | Comparison& operator=(const Comparison&) = delete; 61 | }; 62 | } // Buildings 63 | } // AHN 64 | -------------------------------------------------------------------------------- /AHN.Buildings/ContourClassification.cpp: -------------------------------------------------------------------------------- 1 | #include "ContourClassification.h" 2 | 3 | namespace AHN 4 | { 5 | namespace Buildings 6 | { 7 | void ContourClassification::onPrepare() 8 | { 9 | _buildingContours.reserve(_contours.size()); 10 | } 11 | 12 | void ContourClassification::onExecute() 13 | { 14 | for (auto const &groupedSegments : _contours) 15 | { 16 | auto ce1 = *(groupedSegments.begin()->begin()); 17 | auto ce2 = *(groupedSegments.begin()->rbegin()); 18 | float refAngle = atan((ce1 - ce2).y / (float)(ce1 - ce2).x); 19 | int extended = 0; 20 | std::vector part1; 21 | std::vector part2 = *(groupedSegments.begin()); 22 | int partCnt = groupedSegments.begin()->size(); 23 | int sumCnt = groupedSegments.begin()->size(); 24 | 25 | for (auto it = groupedSegments.begin() + 1; it < groupedSegments.end(); ) 26 | { 27 | if (cv::norm(ce1 - ce2) < closurePixelDifference && it > groupedSegments.begin() + 1) break; 28 | 29 | sumCnt += it->size(); 30 | 31 | auto const &e1 = *(it->begin()); 32 | auto const &e2 = *(it->rbegin()); 33 | float currAngle = atan((e1 - e2).y / (float)(e1 - e2).x); 34 | bool reverse = abs(refAngle - currAngle) < similarAngleThreshold; 35 | float old = refAngle; 36 | refAngle = currAngle; 37 | 38 | if (cv::norm(e1 - ce1) < chainingPixelDifference && (!reverse || extended != 1)) 39 | { 40 | ce1 = e2; 41 | extended = 1; 42 | std::copy(it->begin(), it->end(), std::back_inserter(part1)); 43 | partCnt += it->size(); 44 | ++it; 45 | } 46 | else if (cv::norm(e1 - ce2) < chainingPixelDifference && (!reverse || extended != 2)) 47 | { 48 | ce2 = e2; 49 | extended = 2; 50 | std::copy(it->begin(), it->end(), std::back_inserter(part2)); 51 | partCnt += it->size(); 52 | ++it; 53 | } 54 | else if (cv::norm(e2 - ce1) < chainingPixelDifference && (!reverse || extended != 1)) 55 | { 56 | ce1 = e1; 57 | extended = 1; 58 | std::copy(it->begin(), it->end(), std::back_inserter(part1)); 59 | partCnt += it->size(); 60 | ++it; 61 | } 62 | else if (cv::norm(e2 - ce2) < chainingPixelDifference && (!reverse || extended != 2)) 63 | { 64 | ce2 = e1; 65 | extended = 2; 66 | std::copy(it->begin(), it->end(), std::back_inserter(part2)); 67 | partCnt += it->size(); 68 | ++it; 69 | } 70 | else 71 | { 72 | ++it; 73 | refAngle = old; 74 | } 75 | } 76 | 77 | bool size = sumCnt > minBuildingPixelPerimeter; 78 | if (size) 79 | { 80 | // vegetation filter 81 | std::vector finalContour; 82 | 83 | std::vector flags(contourPartitionNumber, false); 84 | int complexSegmentCnt = 0; 85 | int longSegmentCnt = 0; 86 | 87 | for (auto const &segment : groupedSegments) 88 | { 89 | cv::Point diff = segment.front() - segment.back(); 90 | if (segment.size() > 2) 91 | { 92 | int index = 0; 93 | if (diff.x > 0) 94 | { 95 | float angle = atan(diff.y / (float)diff.x); 96 | index = (angle + CV_PI / 2) * contourPartitionNumber / CV_PI; 97 | } 98 | flags[index] = true; 99 | } 100 | if (abs(diff.x) + abs(diff.y) < segment.size()) complexSegmentCnt++; 101 | if (segment.size() > longSegmentPixelThreshold) longSegmentCnt++; 102 | } 103 | 104 | int cond1 = std::count(flags.begin(), flags.end(), true) >= contourPartitionNumber * condition1Ratio; 105 | int cond2 = complexSegmentCnt > groupedSegments.size() * condition2Ratio; 106 | int cond3 = longSegmentCnt < groupedSegments.size() * condition3Ratio; 107 | int cond4 = sumCnt / groupedSegments.size() < avgSegmentPixels; 108 | 109 | if (cond1 + cond2 + cond3 + cond4 < 2) 110 | { 111 | finalContour.reserve(part1.size() + part2.size()); 112 | std::reverse_copy(part1.begin(), part1.end(), std::back_inserter(finalContour)); 113 | std::copy(part2.begin(), part2.end(), std::back_inserter(finalContour)); 114 | 115 | if (partCnt > sumCnt * minChainingRatio && cv::norm(finalContour.front() - finalContour.back()) < maxChainingPixelError) 116 | { 117 | // successful segment chaining 118 | _buildingContours.push_back(finalContour); 119 | } 120 | else 121 | { 122 | finalContour.clear(); 123 | std::for_each(groupedSegments.begin(), groupedSegments.end(), 124 | [&finalContour](const std::vector &x) 125 | { finalContour.insert(finalContour.end(), x.begin(), x.end()); }); 126 | _buildingContours.push_back(finalContour); 127 | } 128 | } 129 | } 130 | } 131 | _buildingContours.shrink_to_fit(); 132 | } 133 | 134 | std::vector >& ContourClassification::getContours() 135 | { 136 | return _buildingContours; 137 | } 138 | } // Buildings 139 | } // AHN 140 | -------------------------------------------------------------------------------- /AHN.Buildings/ContourClassification.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define _USE_MATH_DEFINES 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | namespace AHN 12 | { 13 | namespace Buildings 14 | { 15 | /// 16 | /// Distinguishing the building contours from any other. 17 | /// 18 | class ContourClassification: public CloudTools::Operation 19 | { 20 | public: 21 | int closurePixelDifference = 5; 22 | float similarAngleThreshold = M_PI / 18; 23 | int chainingPixelDifference = 15; 24 | int minBuildingPixelPerimeter = 80; 25 | int contourPartitionNumber = 8; 26 | int longSegmentPixelThreshold = 15; 27 | float condition1Ratio = 3. / 4; 28 | float condition2Ratio = 1. / 4; 29 | float condition3Ratio = 1. / 4; 30 | float avgSegmentPixels = 15; 31 | float minChainingRatio = 3. / 4; 32 | int maxChainingPixelError = 20; 33 | 34 | /// 35 | /// Initializes a new instance of the class. 36 | /// 37 | /// The input contours. 38 | ContourClassification(const std::vector > >& contours) : _contours(contours) 39 | { } 40 | 41 | /// 42 | /// Retrieves the output contours of the operation. 43 | /// 44 | /// The contours denoting buildings. 45 | std::vector >& getContours(); 46 | 47 | protected: 48 | /// 49 | /// Prepares the output. 50 | /// 51 | void onPrepare() override; 52 | 53 | /// 54 | /// Calculates the output. 55 | /// 56 | void onExecute() override; 57 | 58 | private: 59 | std::vector > > _contours; 60 | std::vector > _buildingContours; 61 | }; 62 | } // Buildings 63 | } // AHN 64 | -------------------------------------------------------------------------------- /AHN.Buildings/ContourConvexHullRasterizer.cpp: -------------------------------------------------------------------------------- 1 | #include "ContourConvexHullRasterizer.h" 2 | 3 | namespace AHN 4 | { 5 | namespace Buildings 6 | { 7 | void ContourConvexHullRasterizer::initialize() 8 | { 9 | this->nodataValue = 0; 10 | 11 | this->computation = [this](int sizeX, int sizeY) 12 | { 13 | cv::Mat contour = cv::Mat::zeros(sizeY, sizeX, CV_8UC3); 14 | std::vector > chulls(_contours.size()); 15 | for (size_t i = 0; i < _contours.size(); i++) 16 | { 17 | convexHull(_contours[i], chulls[i], false); 18 | cv::Scalar color = cv::Scalar((std::rand() + 1) & 255, (std::rand() + 1) & 255, (std::rand() + 1) & 255); 19 | drawContours(contour, chulls, static_cast(i), color, cv::FILLED, cv::LINE_8); 20 | } 21 | 22 | for (int i = 0; i < sizeX; ++i) 23 | for (int j = 0; j < sizeY; ++j) 24 | { 25 | cv::Vec3b rgb = contour.at(j, i); 26 | if (rgb == cv::Vec3b::zeros()) 27 | this->setTargetData(i, j, this->nodataValue); 28 | else 29 | this->setTargetData(i, j, rgb[0] * 1000000 + rgb[1] * 1000 + rgb[2]); 30 | } 31 | }; 32 | } 33 | 34 | } // Buildings 35 | } // AHN 36 | -------------------------------------------------------------------------------- /AHN.Buildings/ContourConvexHullRasterizer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | namespace AHN 12 | { 13 | namespace Buildings 14 | { 15 | /// 16 | /// Converting contours in vector format into a DEM using their convex hulls. 17 | /// 18 | class ContourConvexHullRasterizer : public CloudTools::DEM::DatasetTransformation 19 | { 20 | public: 21 | /// 22 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 23 | /// 24 | /// The reference dataset of the algorithm. 25 | /// The input contours for the transformation. 26 | /// The target file of the algorithm. 27 | /// The callback method to report progress. 28 | ContourConvexHullRasterizer(GDALDataset* sourceDataset, 29 | const std::vector >& contours, 30 | const std::string& targetPath, 31 | CloudTools::Operation::ProgressType progress = nullptr) 32 | : CloudTools::DEM::DatasetTransformation({ sourceDataset }, targetPath, nullptr, progress), _contours(contours) 33 | { 34 | initialize(); 35 | } 36 | 37 | ContourConvexHullRasterizer(const ContourConvexHullRasterizer&) = delete; 38 | ContourConvexHullRasterizer& operator=(const ContourConvexHullRasterizer&) = delete; 39 | 40 | private: 41 | std::vector > _contours; 42 | 43 | /// 44 | /// Initializes the new instance of the class. 45 | /// 46 | void initialize(); 47 | }; 48 | } // Buildings 49 | } // AHN 50 | -------------------------------------------------------------------------------- /AHN.Buildings/ContourDetection.cpp: -------------------------------------------------------------------------------- 1 | #include "ContourDetection.h" 2 | 3 | namespace AHN 4 | { 5 | namespace Buildings 6 | { 7 | void ContourDetection::initialize() 8 | { 9 | this->computation = [this](int sizeX, int sizeY) 10 | { 11 | cv::Mat edge; 12 | edge.create(sizeY, sizeX, CV_8UC1); // required format for the Canny function 13 | 14 | float min = FLT_MAX, max = 0; 15 | for (int i = 0; i < sizeX; ++i) 16 | for (int j = 0; j < sizeY; ++j) 17 | { 18 | if (this->hasSourceData(i, j)) 19 | { 20 | float d = this->sourceData(i, j); 21 | if (d < min) min = d; 22 | if (d > max) max = d; 23 | } 24 | } 25 | 26 | // transforming values into 0-255 range 27 | float interval = max - min; 28 | for (int i = 0; i < sizeX; ++i) 29 | for (int j = 0; j < sizeY; ++j) 30 | { 31 | if (this->hasSourceData(i, j)) 32 | { 33 | float value = this->sourceData(i, j); 34 | uchar rounded = std::round((value - min) / interval * 255); 35 | edge.at(j, i) = rounded; 36 | } 37 | else 38 | edge.at(j, i) = 0; 39 | } 40 | 41 | // smooth elevation image for better edge results 42 | cv::blur(edge, edge, cv::Size(smoothingFilterKernelSize,smoothingFilterKernelSize)); 43 | cannyUpperThreshold = (255 / (max - min)) * 2 * 2; 44 | cv::Canny(edge, edge, 45 | cannyUpperThreshold / cannyThresholdRatio, 46 | cannyUpperThreshold, // recommended ratio is 1:3 (or 1:2) 47 | smoothingFilterKernelSize, // equal kernel size with smoothing 48 | false); // more accurate computation 49 | 50 | findContours(edge, this->_contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); 51 | }; 52 | } 53 | 54 | std::vector >& ContourDetection::getContours() 55 | { 56 | return _contours; 57 | } 58 | } // Buildings 59 | } // AHN 60 | -------------------------------------------------------------------------------- /AHN.Buildings/ContourDetection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | 12 | namespace AHN 13 | { 14 | namespace Buildings 15 | { 16 | /// 17 | /// Performs countour detection on a DEM dataset based on the Canny edge detector. 18 | /// https://en.wikipedia.org/wiki/Canny_edge_detector 19 | /// 20 | class ContourDetection : public CloudTools::DEM::DatasetCalculation 21 | { 22 | public: 23 | int smoothingFilterKernelSize = 3; 24 | int cannyUpperThreshold; 25 | int cannyThresholdRatio = 3; 26 | 27 | /// 28 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 29 | /// 30 | /// The source file of the transformation. 31 | /// The callback method to report progress. 32 | ContourDetection(const std::string& sourcePath, 33 | Operation::ProgressType progress = nullptr) 34 | : DatasetCalculation({ sourcePath }, nullptr, progress) 35 | { 36 | initialize(); 37 | } 38 | 39 | /// 40 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 41 | /// 42 | /// The source dataset of the transformation. 43 | /// The callback method to report progress. 44 | ContourDetection(GDALDataset* sourceDataset, 45 | Operation::ProgressType progress = nullptr) 46 | : DatasetCalculation({ sourceDataset }, nullptr, progress) 47 | { 48 | initialize(); 49 | } 50 | 51 | ContourDetection(const ContourDetection&) = delete; 52 | ContourDetection& operator=(const ContourDetection&) = delete; 53 | 54 | /// 55 | /// Retrieves the detected contours. 56 | /// 57 | /// The detected contours. 58 | std::vector >& getContours(); 59 | 60 | private: 61 | std::vector > _contours; 62 | 63 | /// 64 | /// Initializes the new instance of the class. 65 | /// 66 | void initialize(); 67 | }; 68 | } // Buildings 69 | } // AHN 70 | -------------------------------------------------------------------------------- /AHN.Buildings/ContourFiltering.cpp: -------------------------------------------------------------------------------- 1 | #include "ContourFiltering.h" 2 | 3 | namespace AHN 4 | { 5 | namespace Buildings 6 | { 7 | void ContourFiltering::onPrepare() 8 | { 9 | _filteredContours.reserve(_contours.size()); 10 | } 11 | 12 | void ContourFiltering::onExecute() 13 | { 14 | std::copy_if(_contours.begin(), _contours.end(), std::back_inserter(_filteredContours), 15 | [this](const std::vector& c) { 16 | int minStraightLength = this->subsequenceLength_monotonic; 17 | auto pred = [&minStraightLength, it = c.begin()](const cv::Point& p) mutable { 18 | auto interval = it + minStraightLength; 19 | auto bp = p, ep = *interval; 20 | return std::all_of(it++, interval, [bp, ep](const cv::Point& p) { 21 | // axis-aligned bounding box 22 | return (p.x - std::min(bp.x, ep.x)) * (p.x - std::max(bp.x, ep.x)) <= 0 && 23 | (p.y - std::min(bp.y, ep.y)) * (p.y - std::max(bp.y, ep.y)) <= 0; 24 | }); 25 | }; 26 | bool monoton = c.size() >= minStraightLength && 27 | std::count_if(c.begin(), c.end() - minStraightLength, pred) > c.size() * this->minRatio_monotonic; 28 | minStraightLength = this->subsequenceLength_straight; 29 | bool hasStraight = c.size() >= minStraightLength && 30 | std::any_of(c.begin(), c.end() - minStraightLength, pred); 31 | 32 | return monoton || hasStraight; 33 | }); 34 | _filteredContours.shrink_to_fit(); 35 | } 36 | 37 | std::vector >& ContourFiltering::getContours() 38 | { 39 | return _filteredContours; 40 | } 41 | } // Buildings 42 | } // AHN 43 | -------------------------------------------------------------------------------- /AHN.Buildings/ContourFiltering.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace AHN 10 | { 11 | namespace Buildings 12 | { 13 | /// 14 | /// Initial filtering for building contours examining size and shape. 15 | /// 16 | class ContourFiltering : public CloudTools::Operation 17 | { 18 | public: 19 | int subsequenceLength_monotonic = 20; 20 | int subsequenceLength_straight = 40; 21 | float minRatio_monotonic = 5. / 6; 22 | 23 | /// 24 | /// Initializes a new instance of the class. 25 | /// 26 | /// The input contours to be filtered. 27 | ContourFiltering(const std::vector >& contours) : _contours(contours) 28 | { } 29 | 30 | /// 31 | /// Retrieves the output contours of the operation. 32 | /// 33 | /// The remaining contours. 34 | std::vector >& getContours(); 35 | 36 | protected: 37 | /// 38 | /// Prepares the output. 39 | /// 40 | void onPrepare() override; 41 | 42 | /// 43 | /// Calculates the output. 44 | /// 45 | void onExecute() override; 46 | 47 | private: 48 | std::vector > _contours; 49 | std::vector > _filteredContours; 50 | }; 51 | } // Buildings 52 | } // AHN 53 | -------------------------------------------------------------------------------- /AHN.Buildings/ContourSimplification.cpp: -------------------------------------------------------------------------------- 1 | #include "ContourSimplification.h" 2 | 3 | namespace AHN 4 | { 5 | namespace Buildings 6 | { 7 | void ContourSimplification::onPrepare() 8 | { 9 | _outputContours.reserve(_contours.size()); 10 | } 11 | 12 | void ContourSimplification::onExecute() 13 | { 14 | for (auto const &straightSegments : _contours) 15 | { 16 | // filtering identical segments 17 | std::vector > uniqueSegments; 18 | uniqueSegments.reserve(straightSegments.size()); 19 | for (auto it1 = straightSegments.begin(); it1 != straightSegments.end(); ++it1) 20 | { 21 | bool keep = true; 22 | for (auto it2 = straightSegments.begin(); it2 != it1; ++it2) 23 | { 24 | auto const &e11 = *(it1->begin()); 25 | auto const &e12 = *(it1->rbegin()); 26 | auto const &e21 = *(it2->begin()); 27 | auto const &e22 = *(it2->rbegin()); 28 | if ((cv::norm(e11 - e21) < maxSegmentEndpointPixelDifference && cv::norm(e12 - e22) < maxSegmentEndpointPixelDifference || 29 | cv::norm(e11 - e22) < maxSegmentEndpointPixelDifference && cv::norm(e12 - e21) < maxSegmentEndpointPixelDifference) && 30 | (abs(cv::norm(e11 - e12) - cv::norm(e21 - e22)) < maxSegmentNormDifference)) 31 | { 32 | keep = false; break; 33 | } 34 | int cnt = std::count_if(it1->begin(), it1->end(), 35 | [&it2](const cv::Point &x) { return std::find(it2->begin(), it2->end(), x) != it2->end(); }); 36 | if (cnt > it1->size() * minRatio_identicalPoints) 37 | { 38 | keep = false; break; 39 | } 40 | } 41 | if (keep) uniqueSegments.push_back(*it1); 42 | } 43 | uniqueSegments.shrink_to_fit(); 44 | 45 | // merging segments with similar direction 46 | std::vector > groupedSegments; 47 | groupedSegments.reserve(uniqueSegments.size()); 48 | for (auto it = uniqueSegments.begin(); it < uniqueSegments.end(); ) 49 | { 50 | int cnt = 0, sum = it->size(); 51 | bool stop = false; 52 | auto const &vec1 = *(it->begin()) - *(it->rbegin()); 53 | while (!stop && it + cnt < uniqueSegments.end() - 1) 54 | { 55 | ++cnt; 56 | auto const &vec2 = *((it + cnt)->begin()) - *((it + cnt)->rbegin()); 57 | stop = abs(atan2(vec1.y, vec1.x) - atan2(vec2.y, vec2.x)) > similarAngleThreshold; 58 | if (!stop) sum += (it + cnt)->size(); 59 | } 60 | 61 | if (cnt > 1) 62 | { 63 | std::vector v; 64 | v.reserve(sum); 65 | std::for_each(it, it + cnt, [&v](const std::vector &x) 66 | { v.insert(v.end(), x.begin(), x.end()); }); 67 | groupedSegments.push_back(v); 68 | } 69 | else 70 | groupedSegments.push_back(*it); 71 | 72 | it += cnt; 73 | if (!cnt) break; 74 | } 75 | 76 | auto longest = std::max_element(groupedSegments.begin(), groupedSegments.end(), 77 | [](const std::vector &x, const std::vector &y) { return x.size() < y.size(); }); 78 | if (longest->size() > 2 * groupedSegments.begin()->size()) 79 | { 80 | std::vector > tmp; 81 | std::reverse_copy(groupedSegments.begin(), longest, std::back_inserter(tmp)); 82 | groupedSegments.erase(groupedSegments.begin(), longest); 83 | groupedSegments.insert(groupedSegments.end(), tmp.begin(), tmp.end()); 84 | } 85 | 86 | groupedSegments.shrink_to_fit(); 87 | _outputContours.push_back(groupedSegments); 88 | } 89 | } 90 | 91 | std::vector > >& ContourSimplification::getContours() 92 | { 93 | return _outputContours; 94 | } 95 | } // Buildings 96 | } // AHN 97 | -------------------------------------------------------------------------------- /AHN.Buildings/ContourSimplification.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define _USE_MATH_DEFINES 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | namespace AHN 12 | { 13 | namespace Buildings 14 | { 15 | /// 16 | /// Removing redundant informations from the represenation of the contours. 17 | /// 18 | class ContourSimplification: public CloudTools::Operation 19 | { 20 | public: 21 | int maxSegmentEndpointPixelDifference = 7; 22 | int maxSegmentNormDifference = 10; 23 | float minRatio_identicalPoints = 1. / 2; 24 | float similarAngleThreshold = M_PI / 16; 25 | 26 | /// 27 | /// Initializes a new instance of the class. 28 | /// 29 | /// The input contours. 30 | ContourSimplification(const std::vector > >& contours) : _contours(contours) 31 | { } 32 | 33 | /// 34 | /// Retrieves the output contours of the operation. 35 | /// 36 | /// The initial contours without redundancy. 37 | std::vector > >& getContours(); 38 | 39 | protected: 40 | /// 41 | /// Prepares the output. 42 | /// 43 | void onPrepare() override; 44 | 45 | /// 46 | /// Calculates the output. 47 | /// 48 | void onExecute() override; 49 | 50 | private: 51 | std::vector > > _contours; 52 | std::vector > > _outputContours; 53 | }; 54 | } // Buildings 55 | } // AHN 56 | -------------------------------------------------------------------------------- /AHN.Buildings/ContourSplitting.cpp: -------------------------------------------------------------------------------- 1 | #include "ContourSplitting.h" 2 | 3 | namespace AHN 4 | { 5 | namespace Buildings 6 | { 7 | void ContourSplitting::onPrepare() 8 | { 9 | _segmentedContours.reserve(_contours.size()); 10 | } 11 | 12 | void ContourSplitting::onExecute() 13 | { 14 | for (auto const &contour : _contours) 15 | { 16 | std::vector > straightSegments; 17 | std::vector segment = { contour[0] }; 18 | cv::Point refDir = contour[1] - contour[0]; 19 | bool diverge1 = false, diverge2 = false; 20 | bool hasXparallel = false, hasYparallel = false; 21 | int xParallelCnt = 0, yParallelCnt = 0; 22 | for (auto it = contour.begin() + 2; it != contour.end(); ++it) 23 | { 24 | segment.push_back(*(it - 1)); 25 | cv::Point currDir = *it - *(it - 1); 26 | 27 | if (currDir.x == 0) xParallelCnt++; 28 | else 29 | { 30 | hasXparallel = hasXparallel || xParallelCnt > minStraightSegmentLength; 31 | xParallelCnt = 0; 32 | } 33 | if (currDir.y == 0) yParallelCnt++; 34 | else 35 | { 36 | hasYparallel = hasYparallel || yParallelCnt > minStraightSegmentLength; 37 | yParallelCnt = 0; 38 | } 39 | 40 | if (refDir.x == 0) 41 | { 42 | diverge1 = diverge1 || currDir.x < 0; 43 | diverge2 = diverge2 || currDir.x > 0; 44 | } 45 | if (refDir.y == 0) 46 | { 47 | diverge1 = diverge1 || currDir.y < 0; 48 | diverge2 = diverge2 || currDir.y > 0; 49 | } 50 | 51 | if (diverge1 && diverge2 || refDir.x * currDir.x < 0 || refDir.y * currDir.y < 0) 52 | { 53 | if (hasXparallel && hasYparallel) 54 | { 55 | xParallelCnt = 0; yParallelCnt = 0; 56 | std::vector inner; 57 | 58 | int parallelCnt = 0; 59 | for (auto innerIt = segment.begin() + 1; innerIt < segment.end(); ++innerIt) 60 | { 61 | inner.push_back(*(innerIt - 1)); 62 | cv::Point innerDir = *innerIt - *(innerIt - 1); 63 | 64 | if (innerDir.x == 0) xParallelCnt++; 65 | else if (xParallelCnt > minStraightSegmentLength) 66 | { 67 | xParallelCnt = 0; 68 | straightSegments.push_back(inner); 69 | inner.clear(); 70 | continue; 71 | } 72 | else xParallelCnt = 0; 73 | 74 | if (innerDir.y == 0) yParallelCnt++; 75 | else if (yParallelCnt > minStraightSegmentLength) 76 | { 77 | yParallelCnt = 0; 78 | straightSegments.push_back(inner); 79 | inner.clear(); 80 | continue; 81 | } 82 | else yParallelCnt = 0; 83 | } 84 | inner.push_back(*segment.rbegin()); 85 | if (inner.size() > 1) straightSegments.push_back(inner); 86 | else straightSegments.back().push_back(inner[0]); 87 | } 88 | else 89 | { 90 | if (segment.size() > 1) straightSegments.push_back(segment); 91 | else straightSegments.back().push_back(segment[0]); 92 | } 93 | 94 | hasXparallel = false; hasYparallel = false; 95 | xParallelCnt = 0; yParallelCnt = 0; 96 | diverge1 = false; diverge2 = false; 97 | segment.clear(); 98 | 99 | if (it != contour.end() - 1) refDir = *(it + 1) - *it; 100 | } 101 | } 102 | segment.push_back(*contour.rbegin()); 103 | if (segment.size() > 1) straightSegments.push_back(segment); 104 | else straightSegments.back().push_back(segment[0]); 105 | 106 | _segmentedContours.push_back(straightSegments); 107 | } 108 | } 109 | 110 | std::vector > >& ContourSplitting::getContours() 111 | { 112 | return _segmentedContours; 113 | } 114 | } // Buildings 115 | } // AHN 116 | -------------------------------------------------------------------------------- /AHN.Buildings/ContourSplitting.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace AHN 10 | { 11 | namespace Buildings 12 | { 13 | /// 14 | /// Dividing contours into straight segments. 15 | /// 16 | class ContourSplitting: public CloudTools::Operation 17 | { 18 | public: 19 | int minStraightSegmentLength = 5; 20 | 21 | /// 22 | /// Initializes a new instance of the class. 23 | /// 24 | /// The input contours. 25 | ContourSplitting(const std::vector >& contours) : _contours(contours) 26 | { } 27 | 28 | /// 29 | /// Retrieves the output contours of the operation. 30 | /// 31 | /// The straight contour segments for each initial contour. 32 | std::vector > >& getContours(); 33 | 34 | protected: 35 | /// 36 | /// Prepares the output. 37 | /// 38 | void onPrepare() override; 39 | 40 | /// 41 | /// Calculates the output. 42 | /// 43 | void onExecute() override; 44 | 45 | private: 46 | std::vector > _contours; 47 | std::vector > > _segmentedContours; 48 | }; 49 | } // Buildings 50 | } // AHN 51 | -------------------------------------------------------------------------------- /AHN.Buildings/IOMode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "IOMode.h" 7 | 8 | namespace AHN 9 | { 10 | namespace Buildings 11 | { 12 | std::istream& operator >> (std::istream& input, IOMode &mode) 13 | { 14 | std::string str; 15 | input >> str; 16 | boost::to_upper(str); 17 | boost::trim(str); 18 | 19 | if (str == "FILES") 20 | mode = IOMode::Files; 21 | else if (str == "MEMORY") 22 | mode = IOMode::Memory; 23 | else if (str == "STREAM") 24 | mode = IOMode::Stream; 25 | else if (str == "HADOOP") 26 | mode = IOMode::Hadoop; 27 | else 28 | mode = IOMode::Unknown; 29 | return input; 30 | } 31 | 32 | std::ostream& operator<< (std::ostream& output, const IOMode &mode) 33 | { 34 | switch (mode) 35 | { 36 | case IOMode::Files: 37 | output << "FILES"; 38 | break; 39 | case IOMode::Memory: 40 | output << "MEMORY"; 41 | break; 42 | case IOMode::Stream: 43 | output << "STREAM"; 44 | break; 45 | case IOMode::Hadoop: 46 | output << "HADOOP"; 47 | break; 48 | default: 49 | output << "UNKNOWN"; 50 | } 51 | return output; 52 | } 53 | } // Buildings 54 | } // AHN 55 | -------------------------------------------------------------------------------- /AHN.Buildings/IOMode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace AHN 6 | { 7 | namespace Buildings 8 | { 9 | enum class IOMode 10 | { 11 | Unknown = 0, // 0000 12 | Files = 1, // 0001 13 | Memory = 2, // 0010 14 | Stream = 6, // 0110 15 | Hadoop = 14, // 1110 16 | }; 17 | 18 | using Internal = std::underlying_type::type; 19 | 20 | inline IOMode operator |(IOMode lhs, IOMode rhs) 21 | { 22 | return static_cast(static_cast(lhs) | static_cast(rhs)); 23 | } 24 | 25 | inline IOMode operator &(IOMode lhs, IOMode rhs) 26 | { 27 | return static_cast(static_cast(lhs) & static_cast(rhs)); 28 | } 29 | 30 | inline bool hasFlag(IOMode mode, IOMode flag) 31 | { 32 | return (mode & flag) == flag; 33 | } 34 | 35 | std::istream& operator >>(std::istream& input, IOMode& mode); 36 | std::ostream& operator<<(std::ostream& output, const IOMode& mode); 37 | } // Buildings 38 | } // AHN 39 | -------------------------------------------------------------------------------- /BUILD.md: -------------------------------------------------------------------------------- 1 | Build instructions 2 | ============ 3 | 4 | This document summarizes how to build and install the project and its dependencies. 5 | 6 | 7 | Supported operating systems 8 | ------------ 9 | 10 | The project is continuously built and tested on the following operating systems: 11 | - Windows 10/11 12 | - Ubuntu 20.04 LTS and 22.04 LTS 13 | 14 | 15 | Dependencies 16 | ------------ 17 | - [Boost Library](https://www.boost.org/), >=1.71 *(mandatory)* 18 | - [GDAL - Geospatial Data Abstraction Library](http://www.gdal.org/)1, >=3.0 *(mandatory)* 19 | - [OpenCV](https://opencv.org/), >=4.2 *(mandatory)* 20 | - MPI - Message Passing Interface2 *(optional)* 21 | - Windows: [MS-MPI](https://learn.microsoft.com/en-us/message-passing-interface/microsoft-mpi), >=10.1.2 22 | - Linux: [Open MPI](https://www.open-mpi.org/), >=4.0.3 23 | 24 | For Windows operating systems binary releases are available to simply install the above defined libraries. 25 | For Linux operating systems installing from the standard (or other) package repository is a convenient solution if the available versions meet the requirements. 26 | 27 | 1 GDAL must be compiled with [GEOS](https://trac.osgeo.org/geos/) support enabled. 28 | 2 MPI dependency is required only for the `AHN.Buildings.MPI` project to evaluate in a HPC environment. 29 | 30 | 31 | Structure 32 | ------------ 33 | 34 | The repository in consisted of 9 projects altogether. 35 | - **CloudTools.DEM:** Algorithms on DEM transformation and calculation. 36 | - **CloudTools.DEM.Difference:** Compares multi-temporal DEMs of same area to retrieve differences. 37 | - **CloudTools.DEM.Mask:** Transforms a vector filter mask into a raster filter mask and/or applies the latter on a DEM. 38 | - **CloudTools.Common:** Reporting and I/O management. 39 | - **AHN.Buildings:** Compares an AHN-2 and AHN-3 tile pair and filters out changes in buildings. 40 | - **AHN.Buildings.Parallel:** Compares pairs of AHN-2 and AHN-3 tiles parallelly and filters out changes in buildings. 41 | - **AHN.Buildings.MPI:** Compares pairs of AHN-2 and AHN-3 tiles parallelly (through MPI) and filters out changes in buildings. 42 | - **AHN.Buildings.Aggregate:** Computes aggregative change of volume for administrative units. 43 | - **AHN.Buildings.Verify:** Verifies detected building changes against reference files. 44 | - **CloudTools.Vegetation:** Compares DEMs of same area of same area from different epochs and filters out changes in vegetation (trees). 45 | - **CloudTools.Vegetation.Verify:** Verifies detected trees changes against reference files. 46 | 47 | 48 | How to build 49 | ------------ 50 | 51 | The repository utilizes the [CMake](https://cmake.org/) cross-platform build system. To generate the build environment, run CMake: 52 | ```bash 53 | mkdir build 54 | cd build 55 | cmake .. -DCMAKE_INSTALL_PREFIX= 56 | ``` 57 | 58 | The following table contains a few CMake variables which might be relevant 59 | during compilation. 60 | 61 | | Variable | Meaning | 62 | | -------- | ------- | 63 | | `CMAKE_INSTALL_PREFIX` | Install directory. For more information see: https://cmake.org/cmake/help/v3.5/variable/CMAKE_INSTALL_PREFIX.html | 64 | | `CMAKE_BUILD_TYPE` | Specifies the build type on single-configuration generators (e.g. *Unix Makefiles*). Possible values are empty, **Debug**, **Release**, **RelWithDebInfo** and **MinSizeRel**. For more information see: https://cmake.org/cmake/help/v3.5/variable/CMAKE_BUILD_TYPE.html | 65 | 66 | ### On Windows 67 | 68 | Using CMake with the *MSVC generator* it will construct a Visual Studio solution which can be used to build the projects. Open a *Developer Command Prompt for Visual Studio* to build the solution and optionally install the binaries to the specified destination: 69 | ```batch 70 | msbuild CloudTools.sln 71 | msbuild INSTALL.vcxproj 72 | ``` 73 | 74 | You may specify the `/property:Configuration=` flag to set the project configuration. Supported values are **Debug** and **Release**. 75 | 76 | The project supports Visual Studio's CMake integration, you may create a `CMakeSettings.json` file based on the given sample (`CMakeSettings.json.sample`) and use the multi-configuration *Ninja* generator to compile against x86 or x64 architectures. 77 | 78 | For more detailed instructions, see the [Quick Start for Windows](WINDOWS_QUICK_START.md) guide. 79 | 80 | ### On Linux 81 | 82 | Using CMake with the *Unix Makefiles* generator it will construct [GNU Makefiles](https://www.gnu.org/software/make/) as a build system. Then a simple `make` command is sufficient for compilation. 83 | Specify one of the above mentioned project names in section [Structure](#structure) as the build target to only build a subset of the projects, e.g.: 84 | ~~~bash 85 | make 86 | make AHN.Buildings.Parallel 87 | ~~~ 88 | *Note:* you may add the `-j` flag to compile on multiple threads (where `` is the number of threads). 89 | 90 | How to use 91 | ------------ 92 | 93 | On build of repository, the following executables will be available: 94 | ``` 95 | dem_diff 96 | dem_mask 97 | ahn_buildings_sim 98 | ahn_buildings_par 99 | ahn_buildings_agg 100 | ahn_buildings_ver 101 | ahn_buildings_mpi 102 | vegetation 103 | vegetation_ver 104 | ``` 105 | Get usage information and available arguments with the `-h` flag. 106 | 107 | *Note:* since the GDAL library is linked dynamically, some environment variables (`GDAL_BIN`, `GDAL_DATA` and `GDAL_DRIVER_PATH`) must be configured to execute the above binaries. Set them correctly in your OS account or in the active shell. 108 | On Windows you may create a `Shell.config.cmd` file based on the given sample (`Shell.config.cmd.sample`) and run the `Shell.bat` preconfigured environment at the installation location. 109 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(CloudTools) 3 | 4 | # Set a default build type if none was specified 5 | set(DEFAULT_BUILD_TYPE "Release") 6 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 7 | message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.") 8 | set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE 9 | STRING "Choose the type of build." FORCE) 10 | # Set the possible values of build type for cmake-gui 11 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 12 | "Debug" "Release" "MinSizeRel" "RelWithDebInfo") 13 | endif() 14 | 15 | set(CMAKE_CXX_STANDARD 17) 16 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 17 | set(CMAKE_CXX_EXTENSIONS OFF) 18 | 19 | if(MSVC) 20 | # warning level 4 21 | add_compile_options(/W4) 22 | else() 23 | # add most warnings 24 | add_compile_options(-pedantic -Wall -Wno-unknown-pragmas -Wno-reorder) 25 | endif() 26 | 27 | find_package(Boost 1.71 REQUIRED COMPONENTS filesystem program_options regex) 28 | find_package(GDAL 3.0 REQUIRED) 29 | find_package(OpenCV 4.2 REQUIRED COMPONENTS core imgproc) 30 | find_package(Threads REQUIRED) 31 | find_package(MPI) 32 | 33 | include_directories(${Boost_INCLUDE_DIRS}) 34 | include_directories(${GDAL_INCLUDE_DIR}) 35 | include_directories(${OpenCV_INCLUDE_DIRS}) 36 | 37 | if(MSVC) 38 | link_directories(${Boost_LIBRARY_DIRS}) 39 | else() 40 | link_libraries(${Boost_LIBRARIES}) 41 | endif() 42 | link_libraries(${GDAL_LIBRARY}) 43 | link_libraries(${OpenCV_LIBS}) 44 | 45 | add_subdirectory(CloudTools.Common) 46 | add_subdirectory(CloudTools.DEM) 47 | add_subdirectory(CloudTools.DEM.Difference) 48 | add_subdirectory(CloudTools.DEM.Mask) 49 | add_subdirectory(AHN.Buildings) 50 | add_subdirectory(AHN.Buildings.Parallel) 51 | add_subdirectory(AHN.Buildings.Aggregate) 52 | add_subdirectory(AHN.Buildings.Verify) 53 | add_subdirectory(CloudTools.Vegetation) 54 | add_subdirectory(CloudTools.Vegetation.Verify) 55 | 56 | if(MPI_CXX_FOUND) 57 | add_subdirectory(AHN.Buildings.MPI) 58 | else() 59 | message(WARNING "MPI not found, AHN.Buildings.MPI will not be built.") 60 | endif() 61 | 62 | if(WIN32) 63 | install(FILES Shell.bat 64 | DESTINATION ${CMAKE_INSTALL_PREFIX}) 65 | if(EXISTS ${CMAKE_SOURCE_DIR}/Shell.config.cmd) 66 | install(FILES Shell.config.cmd 67 | DESTINATION ${CMAKE_INSTALL_PREFIX}) 68 | else() 69 | message(WARNING "Create Shell.config.cmd config file from Shell.config.cmd.sample!") 70 | endif() 71 | endif() 72 | -------------------------------------------------------------------------------- /CMakeSettings.json.sample: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com//fwlink//?linkid=834763 for more information about this file. 3 | "configurations": [ 4 | { 5 | "name": "x86-Debug", 6 | "generator": "Ninja", 7 | "configurationType": "Debug", 8 | "inheritEnvironments": [ "msvc_x86" ], 9 | "buildRoot": "${workspaceRoot}\\build\\${name}", 10 | "installRoot": "${workspaceRoot}\\install\\${name}", 11 | "cmakeCommandArgs": "", 12 | "buildCommandArgs": "-v", 13 | "ctestCommandArgs": "", 14 | "environments": [ 15 | { 16 | "GDAL_ROOT": "${cloudtools_x86.GDAL_ROOT}" 17 | } 18 | ] 19 | }, 20 | { 21 | "name": "x86-Release", 22 | "generator": "Ninja", 23 | "configurationType": "RelWithDebInfo", 24 | "inheritEnvironments": [ "msvc_x86" ], 25 | "buildRoot": "${workspaceRoot}\\build\\${name}", 26 | "installRoot": "${workspaceRoot}\\install\\${name}", 27 | "cmakeCommandArgs": "", 28 | "buildCommandArgs": "-v", 29 | "ctestCommandArgs": "", 30 | "environments": [ 31 | { 32 | "GDAL_ROOT": "${cloudtools_x86.GDAL_ROOT}" 33 | } 34 | ] 35 | }, 36 | { 37 | "name": "x64-Debug", 38 | "generator": "Ninja", 39 | "configurationType": "Debug", 40 | "inheritEnvironments": [ "msvc_x64_x64" ], 41 | "buildRoot": "${workspaceRoot}\\build\\${name}", 42 | "installRoot": "${workspaceRoot}\\install\\${name}", 43 | "cmakeCommandArgs": "", 44 | "buildCommandArgs": "-v", 45 | "ctestCommandArgs": "", 46 | "environments": [ 47 | { 48 | "GDAL_ROOT": "${cloudtools_x64.GDAL_ROOT}" 49 | } 50 | ] 51 | }, 52 | { 53 | "name": "x64-Release", 54 | "generator": "Ninja", 55 | "configurationType": "RelWithDebInfo", 56 | "inheritEnvironments": [ "msvc_x64_x64" ], 57 | "buildRoot": "${workspaceRoot}\\build\\${name}", 58 | "installRoot": "${workspaceRoot}\\install\\${name}", 59 | "cmakeCommandArgs": "", 60 | "buildCommandArgs": "-v", 61 | "ctestCommandArgs": "", 62 | "environments": [ 63 | { 64 | "GDAL_ROOT": "${cloudtools_x64.GDAL_ROOT}" 65 | } 66 | ] 67 | } 68 | ], 69 | "environments": [ 70 | { 71 | "namespace": "cloudtools_x86", 72 | "GDAL_ROOT": "${env.GDAL_ROOT}" 73 | }, 74 | { 75 | "namespace": "cloudtools_x64", 76 | "GDAL_ROOT": "${env.GDAL_ROOT}" 77 | } 78 | ] 79 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Coding conventions 2 | ================= 3 | 4 | Formatting 5 | ---------- 6 | 7 | - **Length** of a line should not be longer than **120** characters. In the rare 8 | cases when we need a longer line (e.g. in a preprocessor macro) use backslash 9 | character to mark a continuation. 10 | - **Tab characters** are **not** welcome in the source. Replace them with 11 | spaces. 12 | - **Indentation** is 4 characters. Continuation indent is also 4 characters. 13 | - **Parameter list** is a frequent place, when a line tends to be longer than 14 | the limit. Break the line and align parameters, as seen in the example below. 15 | 16 | ```cpp 17 | void aMethodWithManyParameters( 18 | const std::string& str1, 19 | const std::string& str2, 20 | const std::int32_t id); 21 | ``` 22 | - **Namespaces** are not indented. 23 | 24 | ```cpp 25 | namespace CloudTools 26 | { 27 | namespace DEM 28 | { 29 | /* ... */ 30 | } // DEM 31 | } // CloudTools 32 | ``` 33 | - **Blocks** should follow the K&R style, where related opening and closing 34 | brackets for namespaces, classes and function blocks should be placed to the 35 | same column; while other compound statements should have their opening braces 36 | at the same line as their respective control statements. 37 | - **Class declarations** should contain the nested types, data members and 38 | methods in this order. Therefore multiple `public`, `protected` and 39 | `private` parts are possible (also in this order). The keywords `public`, 40 | `protected`, `private` are not indented, the member declarations are indented 41 | as usual (with 4 spaces). Inside a visibility class declare types first. 42 | 43 | ```cpp 44 | class Operation 45 | { 46 | public: 47 | typedef std::function ProgressType; 48 | 49 | private: 50 | bool _isPrepared = false; 51 | bool _isExecuted = false; 52 | 53 | public: 54 | virtual ~Operation() { } 55 | bool isPrepared() const { return _isPrepared; } 56 | bool isExecuted() const { return _isExecuted; } 57 | void prepare(bool force = false); 58 | void execute(bool force = false); 59 | 60 | protected: 61 | virtual void onPrepare() = 0; 62 | virtual void onExecute() = 0; 63 | }; 64 | ``` 65 | - **Friend** declarations, if any, should be placed before the public 66 | visibility items, before the public keyword. 67 | - The pointer and reference qualifier `*` and `&` letters should come 68 | **directly** after the type, followed by a space and then the variable's name: 69 | `int* ptr`, `const MyType& rhs`, `std::unique_ptr&& data`. 70 | 71 | Naming 72 | ------ 73 | 74 | - **File names** should contain ASCII characters and written in upper CamelCase 75 | (with a few exceptions like `main.cpp`). Avoid other characters, like dash (-). 76 | Header file extension is `.h`, source file extension is `.cpp`. 77 | - **Class and Type names** are written in upper CamelCase. Avoid underscore in class 78 | or type names. Pointers to major types should be typedef-ed, and should be 79 | called according the pointed type with a `Ptr` suffix. 80 | - **Function names** start with lowercase letter, and have a capital letter for 81 | each new major tag. We do not require different names for static methods, or 82 | global functions. 83 | - **Class member names** with private or protected visibility start with 84 | underscore character following a lowercase letter, and have a capital letter 85 | for each new major tag. Do not use other underscores in member names. 86 | - **Function parameter names** start with a lowercase letter, and have a capital 87 | letter for each new major tag. Do not use other underscores in parameter names. 88 | - **Namespace names** are written in lower case. The content of main modules are 89 | organized in a two-level namespace hierarchy: namespaces `CloudTools` and `AHN` 90 | contain another namespace which describes the main module (e.g. `CloudTools::DEM`, 91 | `AHN::Buildings`). 92 | 93 | Headers 94 | ------- 95 | 96 | - **Include guards** are mandatory for all headers, the `#pragma once` 97 | preprocessor directive is used. While it is non-standard, it is widely supported 98 | by all major (modern) compilers. 99 | - **Order of the inclusion of headers** - either in source files or in other 100 | header files - should be the following: First include standard C++ headers, 101 | then Boost headers, then other supporting library headers (GDAL, MPI, etc.), 102 | then your implementing headers. Among the own headers of `CloudTools`, list the 103 | ones from other modules first. 104 | 105 | ```cpp 106 | #include 107 | #include 108 | #include 109 | #include 110 | #include 111 | 112 | #include 113 | #include 114 | #include 115 | 116 | #include 117 | #include 118 | #include "IOMode.h" 119 | #include "Process.h" 120 | ``` 121 | - **Never** apply `using namespace` directive in headers. 122 | -------------------------------------------------------------------------------- /CloudTools.Common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(common 2 | Operation.cpp Operation.h 3 | Helper.h 4 | IO/IO.cpp IO/IO.h 5 | IO/Reporter.cpp IO/Reporter.h 6 | IO/Result.cpp IO/Result.h 7 | IO/ResultCollection.cpp IO/ResultCollection.h) 8 | -------------------------------------------------------------------------------- /CloudTools.Common/Helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace CloudTools 8 | { 9 | /// 10 | /// Represents a (X, Y) point hashing functor for OGRPoint. 11 | /// 12 | struct PointHash 13 | { 14 | std::size_t operator()(const OGRPoint& p) const 15 | { 16 | std::size_t seed = 0; 17 | auto h1 = std::hash{}(p.getX()); 18 | auto h2 = std::hash{}(p.getY()); 19 | 20 | boost::hash_combine(seed, h1); 21 | boost::hash_combine(seed, h2); 22 | return seed; 23 | } 24 | }; 25 | 26 | /// 27 | /// Represents a (X, Y) point equality functor for OGRPoint. 28 | /// 29 | struct PointEqual 30 | { 31 | bool operator()(const OGRPoint& a, const OGRPoint& b) const 32 | { 33 | return a.getX() == b.getX() && a.getY() == b.getY(); 34 | } 35 | }; 36 | 37 | /// 38 | /// Represents a (X, Y) point comparator for OGRPoint. 39 | /// 40 | struct PointComparator 41 | { 42 | bool operator()(const OGRPoint& a, const OGRPoint& b) const 43 | { 44 | if (a.getX() < b.getX()) 45 | return true; 46 | else if (a.getX() > b.getX()) 47 | return false; 48 | else 49 | return a.getY() < b.getY(); 50 | } 51 | }; 52 | } // CloudTools 53 | -------------------------------------------------------------------------------- /CloudTools.Common/IO/IO.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "IO.h" 7 | 8 | namespace CloudTools 9 | { 10 | namespace IO 11 | { 12 | #pragma region Input operations 13 | 14 | bool readBoolean(const std::string &msg, bool def) 15 | { 16 | std::cout << msg; 17 | if (def) 18 | std::cout << " [Y/n] " << std::flush; 19 | else 20 | std::cout << " [y/N] " << std::flush; 21 | 22 | char answer[4]; 23 | std::cin.getline(answer, 4); 24 | if (strlen(answer) == 0) return def; 25 | 26 | std::transform(answer, answer + strlen(answer), answer, ::tolower); 27 | return strcmp(answer, "yes") == 0 || strcmp(answer, "y") == 0; 28 | } 29 | 30 | #pragma endregion 31 | 32 | #pragma region Output operations 33 | 34 | void reportProgress(float complete, const std::string &message) 35 | { 36 | eraseLine(); 37 | std::cout 38 | << "\rProgress: " 39 | << std::fixed << std::setprecision(2) << (complete * 100) << "%" 40 | << std::flush; 41 | if (message.length() > 0) 42 | std::cout << " (" << message << ")" << std::flush; 43 | if (complete == 1.0) 44 | std::cout << std::endl; 45 | } 46 | 47 | void eraseLine(std::size_t size) 48 | { 49 | for (unsigned int i = 0; i < size; ++i) 50 | std::cout << '\b'; 51 | std::cout << '\r' << std::flush; 52 | } 53 | 54 | #pragma endregion 55 | } // IO 56 | } // CloudTools 57 | -------------------------------------------------------------------------------- /CloudTools.Common/IO/IO.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace CloudTools 7 | { 8 | namespace IO 9 | { 10 | #pragma region Types 11 | 12 | enum ExitCodes 13 | { 14 | // Success 15 | Success = 0, 16 | UserAbort = -1, 17 | NoResult = -2, 18 | 19 | // Errors 20 | InvalidInput = 1, 21 | UnexcpectedError = 2, 22 | Unsupported = 3, 23 | }; 24 | 25 | #pragma endregion 26 | 27 | #pragma region Input operations 28 | 29 | /// 30 | /// Reads a boolean value from the console input. 31 | /// 32 | /// The message to display. 33 | /// The default return value. 34 | bool readBoolean(const std::string &msg, bool def = true); 35 | 36 | #pragma endregion 37 | 38 | #pragma region Output operations 39 | 40 | /// 41 | /// Displays the progress of a computation. 42 | /// 43 | /// The ratio of completeness of the process from 0.0 for just started to 1.0 for completed. 44 | /// An optional message to display. 45 | void reportProgress(float complete, const std::string &message = std::string()); 46 | 47 | /// 48 | /// Erases the current line on console. 49 | /// 50 | /// The number of characters to erase. 51 | void eraseLine(std::size_t size = 32); 52 | 53 | #pragma endregion 54 | } // IO 55 | } // CloudTools 56 | -------------------------------------------------------------------------------- /CloudTools.Common/IO/Reporter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Reporter.h" 4 | #include "IO.h" 5 | 6 | namespace CloudTools 7 | { 8 | namespace IO 9 | { 10 | #pragma region TextReporter 11 | 12 | void TextReporter::report(float complete, const std::string &message) 13 | { 14 | eraseLine(_eraseSize); 15 | _eraseSize = std::strlen("Progress: 00.00% ()") + message.length(); 16 | reportProgress(complete, message); 17 | } 18 | 19 | void TextReporter::reset() 20 | { 21 | eraseLine(_eraseSize); 22 | _eraseSize = 0; 23 | } 24 | 25 | #pragma endregion 26 | 27 | #pragma region BarReporter 28 | 29 | BarReporter::~BarReporter() 30 | { 31 | if (_progress) 32 | delete _progress; 33 | } 34 | 35 | void BarReporter::report(float complete, const std::string &message) 36 | { 37 | if (!_progress) _progress = new boost::progress_display(_size); 38 | report(static_cast(_progress->expected_count() * complete)); 39 | } 40 | 41 | void BarReporter::report(unsigned int complete, const std::string &message) 42 | { 43 | if (!_progress) _progress = new boost::progress_display(_size); 44 | *_progress += (complete - _progress->count()); 45 | } 46 | 47 | void BarReporter::reset() 48 | { 49 | if (_progress) 50 | _progress->restart(_size); 51 | } 52 | 53 | #pragma endregion 54 | } // IO 55 | } // CloudTools 56 | -------------------------------------------------------------------------------- /CloudTools.Common/IO/Reporter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace CloudTools 8 | { 9 | namespace IO 10 | { 11 | /// 12 | /// Represents an abstract progress reporter. 13 | /// 14 | class Reporter 15 | { 16 | public: 17 | virtual ~Reporter() {} 18 | 19 | /// 20 | /// Displays the progress of a computation. 21 | /// 22 | /// The ratio of completeness of the process from 0.0 for just started to 1.0 for completed. 23 | /// An optional message string to display. 24 | virtual void report(float complete, const std::string &message = std::string()) = 0; 25 | 26 | /// 27 | /// Resets the progress. 28 | /// 29 | virtual void reset() = 0; 30 | }; 31 | 32 | /// 33 | /// Represents a textual numerical progress reporter. 34 | /// 35 | class TextReporter : public Reporter 36 | { 37 | std::size_t _eraseSize = 0; 38 | 39 | public: 40 | /// 41 | /// Displays the progress of a computation. 42 | /// 43 | /// The ratio of completeness of the process from 0.0 for just started to 1.0 for completed. 44 | /// An optional message string to display. 45 | void report(float complete, const std::string &message = std::string()) override; 46 | 47 | /// 48 | /// Resets the progress. 49 | /// 50 | void reset() override; 51 | }; 52 | 53 | /// 54 | /// Represents an ASCII progress bar reporter. 55 | /// 56 | class BarReporter : public Reporter 57 | { 58 | boost::progress_display *_progress; 59 | unsigned int _size; 60 | 61 | public: 62 | explicit BarReporter(unsigned int size = 100) 63 | : _size(size), _progress(nullptr) { } 64 | ~BarReporter(); 65 | 66 | /// 67 | /// Displays the progress of a computation. 68 | /// 69 | /// The ratio of completeness of the process from 0.0 for just started to 1.0 for completed. 70 | /// An optional message string to display. 71 | void report(float complete, const std::string &message = std::string()) override; 72 | 73 | /// 74 | /// Displays the progress of a computation. 75 | /// 76 | /// The number of completed elements, where the total number of elements where given in the constrcutor. 77 | /// An optional message string to display. 78 | void report(unsigned int complete, const std::string &message = std::string()); 79 | 80 | /// 81 | /// Resets the progress. 82 | /// 83 | void reset() override; 84 | }; 85 | 86 | /// 87 | /// Represents a mute progress reporter which outputs nothing. 88 | /// 89 | class NullReporter : public Reporter 90 | { 91 | int _eraseSize = 0; 92 | 93 | public: 94 | /// 95 | /// Displays the progress of a computation. 96 | /// 97 | /// The ratio of completeness of the process from 0.0 for just started to 1.0 for completed. 98 | /// An optional message string to display. 99 | void report(float complete, const std::string &message = std::string()) override { } 100 | 101 | /// 102 | /// Resets the progress. 103 | /// 104 | void reset() override { } 105 | }; 106 | } // IO 107 | } // CloudTools 108 | -------------------------------------------------------------------------------- /CloudTools.Common/IO/Result.cpp: -------------------------------------------------------------------------------- 1 | #include "Result.h" 2 | 3 | #include 4 | 5 | namespace CloudTools 6 | { 7 | namespace IO 8 | { 9 | #pragma region Result 10 | 11 | Result::~Result() 12 | { 13 | if (dataset != nullptr) 14 | GDALClose(dataset); 15 | } 16 | 17 | Result::Result(Result&& other) noexcept 18 | { 19 | _path = other._path; 20 | dataset = other.dataset; 21 | 22 | other._path.clear(); 23 | other.dataset = nullptr; 24 | } 25 | 26 | Result& Result::operator=(Result&& other) noexcept 27 | { 28 | if (this == &other) 29 | return *this; 30 | 31 | _path = other._path; 32 | dataset = other.dataset; 33 | 34 | other._path.clear(); 35 | other.dataset = nullptr; 36 | return *this; 37 | } 38 | 39 | #pragma endregion 40 | 41 | #pragma region TemporaryFileResult 42 | 43 | TemporaryFileResult::~TemporaryFileResult() 44 | { 45 | if (!_path.empty()) 46 | { 47 | if (dataset) 48 | { 49 | // necessary, because parent dtor will be called after this 50 | GDALClose(dataset); 51 | dataset = nullptr; 52 | } 53 | fs::remove(_path); 54 | } 55 | } 56 | 57 | #pragma endregion 58 | 59 | #pragma region VirtualResult 60 | 61 | VirtualResult::VirtualResult(const std::string& path, GDALDataset* dataset) 62 | : Result(boost::starts_with(path, "/vsimem/") ? path : "/vsimem/" + path, dataset) 63 | { } 64 | 65 | VirtualResult::VirtualResult(const fs::path& path, GDALDataset* dataset) 66 | : Result(boost::starts_with(path.string(), "/vsimem/") ? path : (fs::path("/vsimem/") / path), dataset) 67 | { } 68 | 69 | VirtualResult::~VirtualResult() 70 | { 71 | VSIUnlink(_path.string().c_str()); 72 | } 73 | 74 | #pragma endregion 75 | } // IO 76 | } // CloudTools 77 | -------------------------------------------------------------------------------- /CloudTools.Common/IO/Result.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace fs = boost::filesystem; 9 | 10 | namespace CloudTools 11 | { 12 | namespace IO 13 | { 14 | /// 15 | /// Represents a result object. 16 | /// 17 | struct Result 18 | { 19 | protected: 20 | /// 21 | /// File path. 22 | /// 23 | /// 24 | /// Empty for GDAL memory objects, starts with "/vsimem/" for virtual files. 25 | /// 26 | fs::path _path; 27 | 28 | public: 29 | /// 30 | /// Dataset. 31 | /// 32 | GDALDataset* dataset; 33 | 34 | protected: 35 | /// 36 | /// Initializes a new instance of the struct. 37 | /// 38 | /// The path. 39 | /// The dataset. 40 | explicit Result(const std::string& path, GDALDataset* dataset = nullptr) 41 | : _path(path), dataset(dataset) 42 | { } 43 | 44 | /// 45 | /// Initializes a new instance of the struct. 46 | /// 47 | /// The path. 48 | /// The dataset. 49 | explicit Result(const fs::path& path, GDALDataset* dataset = nullptr) 50 | : _path(path), dataset(dataset) 51 | { } 52 | 53 | public: 54 | virtual ~Result(); 55 | Result(const Result&) = delete; 56 | Result& operator=(const Result&) = delete; 57 | 58 | Result(Result&& other) noexcept; 59 | Result& operator=(Result&& other) noexcept; 60 | 61 | /// 62 | /// Gets the path. 63 | /// 64 | std::string path() const { return _path.string(); } 65 | }; 66 | 67 | /// 68 | /// Represents a permanent file result. 69 | /// 70 | struct PermanentFileResult : Result 71 | { 72 | /// 73 | /// Initializes a new instance of the struct. 74 | /// 75 | /// The path. 76 | /// The dataset. 77 | explicit PermanentFileResult(const std::string& path, GDALDataset* dataset = nullptr) 78 | : Result(path, dataset) 79 | { } 80 | 81 | /// 82 | /// Initializes a new instance of the struct. 83 | /// 84 | /// The path. 85 | /// The dataset. 86 | explicit PermanentFileResult(const fs::path& path, GDALDataset* dataset = nullptr) 87 | : Result(path, dataset) 88 | { } 89 | 90 | PermanentFileResult(const PermanentFileResult&) = delete; 91 | PermanentFileResult& operator=(const PermanentFileResult&) = delete; 92 | 93 | PermanentFileResult(PermanentFileResult&& other) noexcept 94 | : Result(std::move(other)) 95 | { } 96 | 97 | PermanentFileResult& operator=(PermanentFileResult&& other) noexcept 98 | { 99 | *this = std::move(other); 100 | return *this; 101 | } 102 | }; 103 | 104 | /// 105 | /// Represents a temporary file result. 106 | /// 107 | struct TemporaryFileResult : PermanentFileResult 108 | { 109 | /// 110 | /// Initializes a new instance of the struct. 111 | /// 112 | /// The path. 113 | /// The dataset. 114 | explicit TemporaryFileResult(const std::string& path, GDALDataset* dataset = nullptr) 115 | : PermanentFileResult(path, dataset) 116 | { } 117 | 118 | /// 119 | /// Initializes a new instance of the struct. 120 | /// 121 | /// The path. 122 | /// The dataset. 123 | explicit TemporaryFileResult(const fs::path& path, GDALDataset* dataset = nullptr) 124 | : PermanentFileResult(path, dataset) 125 | { } 126 | 127 | ~TemporaryFileResult(); 128 | TemporaryFileResult(const TemporaryFileResult&) = delete; 129 | TemporaryFileResult& operator=(const TemporaryFileResult&) = delete; 130 | 131 | TemporaryFileResult(TemporaryFileResult&& other) noexcept 132 | : PermanentFileResult(std::move(other)) 133 | { } 134 | 135 | TemporaryFileResult& operator=(TemporaryFileResult&& other) noexcept 136 | { 137 | *this = std::move(other); 138 | return *this; 139 | } 140 | }; 141 | 142 | /// 143 | /// Represents a virtual file result. 144 | /// 145 | struct VirtualResult : Result 146 | { 147 | /// 148 | /// Initializes a new instance of the struct. 149 | /// 150 | /// 151 | /// The must start with "/vsimem/", otherwise added. 152 | /// 153 | /// The path. 154 | /// The dataset. 155 | explicit VirtualResult(const std::string& path, GDALDataset* dataset = nullptr); 156 | 157 | /// 158 | /// Initializes a new instance of the struct. 159 | /// 160 | /// 161 | /// The must start with "/vsimem/", otherwise added. 162 | /// 163 | /// The path. 164 | /// The dataset. 165 | explicit VirtualResult(const fs::path& path, GDALDataset* dataset = nullptr); 166 | 167 | ~VirtualResult(); 168 | VirtualResult(const VirtualResult&) = delete; 169 | VirtualResult& operator=(const VirtualResult&) = delete; 170 | 171 | VirtualResult(VirtualResult&& other) noexcept 172 | : Result(std::move(other)) 173 | { } 174 | 175 | VirtualResult& operator=(VirtualResult&& other) noexcept 176 | { 177 | *this = std::move(other); 178 | return *this; 179 | } 180 | }; 181 | 182 | /// 183 | /// Represents a GDAL memory object result. 184 | /// 185 | struct MemoryResult : Result 186 | { 187 | /// 188 | /// Initializes a new instance of the struct. 189 | /// 190 | /// The dataset. 191 | explicit MemoryResult(GDALDataset* dataset = nullptr) 192 | : Result(std::string(), dataset) 193 | { 194 | } 195 | 196 | MemoryResult(const MemoryResult&) = delete; 197 | MemoryResult& operator=(const MemoryResult&) = delete; 198 | 199 | MemoryResult(MemoryResult&& other) noexcept 200 | : Result(std::move(other)) 201 | { } 202 | 203 | MemoryResult& operator=(MemoryResult&& other) noexcept 204 | { 205 | *this = std::move(other); 206 | return *this; 207 | } 208 | }; 209 | } // IO 210 | } // CloudTools 211 | -------------------------------------------------------------------------------- /CloudTools.Common/IO/ResultCollection.cpp: -------------------------------------------------------------------------------- 1 | #include "ResultCollection.h" 2 | 3 | namespace CloudTools 4 | { 5 | namespace IO 6 | { 7 | ResultCollection::~ResultCollection() 8 | { 9 | for (auto& item : _results) 10 | delete item.second; 11 | _results.clear(); 12 | } 13 | 14 | Result& ResultCollection::result(const std::string& name, std::size_t index) 15 | { 16 | if (_results.count(name) <= index) 17 | throw std::out_of_range("No result found with the given name and index."); 18 | 19 | auto range = _results.equal_range(name); 20 | auto it = range.first; 21 | std::advance(it, index); 22 | return *it->second; 23 | } 24 | 25 | std::size_t ResultCollection::newResult(const std::string& name, bool isFinal) 26 | { 27 | std::pair item(name, createResult(name, isFinal)); 28 | _results.emplace(std::move(item)); 29 | return _results.count(name) - 1; 30 | } 31 | 32 | void ResultCollection::deleteResult(const std::string& name, std::size_t index) 33 | { 34 | if (_results.count(name) <= index) 35 | throw std::out_of_range("No result found with the given name and index."); 36 | 37 | auto range = _results.equal_range(name); 38 | auto it = range.first; 39 | std::advance(it, index); 40 | delete it->second; 41 | _results.erase(it); 42 | } 43 | } // IO 44 | } // CloudTools -------------------------------------------------------------------------------- /CloudTools.Common/IO/ResultCollection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "Result.h" 7 | 8 | namespace CloudTools 9 | { 10 | namespace IO 11 | { 12 | class ResultCollection 13 | { 14 | private: 15 | std::multimap _results; 16 | 17 | public: 18 | virtual ~ResultCollection(); 19 | 20 | protected: 21 | /// 22 | /// Gets the specified result object. 23 | /// 24 | /// The name of the result. 25 | /// The index of the result with the same name. 26 | CloudTools::IO::Result& result(const std::string& name, std::size_t index = 0); 27 | 28 | /// 29 | /// Creates and inserts new result object. 30 | /// 31 | /// The name of the result. 32 | /// true if the result is final, otherwise false. 33 | /// The index of the result with the same name. 34 | std::size_t newResult(const std::string& name, bool isFinal = false); 35 | 36 | /// 37 | /// Creates a new result object. 38 | /// 39 | /// The name of the result. 40 | /// true if the result is final, otherwise false. 41 | /// New result on the heap. 42 | virtual CloudTools::IO::Result* createResult(const std::string& name, bool isFinal = false) = 0; 43 | 44 | /// 45 | /// Deletes the specified result object. 46 | /// 47 | /// The name of the result. 48 | /// The index of the result with the same name. 49 | void deleteResult(const std::string& name, std::size_t index = 0); 50 | }; 51 | } // IO 52 | } // CloudTools 53 | -------------------------------------------------------------------------------- /CloudTools.Common/Operation.cpp: -------------------------------------------------------------------------------- 1 | #include "Operation.h" 2 | 3 | namespace CloudTools 4 | { 5 | #pragma region Operation 6 | 7 | void Operation::prepare(bool force) 8 | { 9 | if (!_isPrepared || force) 10 | { 11 | _isPrepared = false; 12 | _isExecuted = false; 13 | onPrepare(); 14 | _isPrepared = true; 15 | } 16 | } 17 | 18 | void Operation::execute(bool force) 19 | { 20 | prepare(force); 21 | if (!_isExecuted || force) 22 | { 23 | _isExecuted = false; 24 | onExecute(); 25 | _isExecuted = true; 26 | } 27 | } 28 | 29 | #pragma endregion 30 | 31 | #pragma region OperationSequence 32 | 33 | void OperationSequence::onExecute() 34 | { 35 | while (!end()) 36 | executeNext(); 37 | } 38 | 39 | #pragma endregion 40 | } // CloudTools 41 | -------------------------------------------------------------------------------- /CloudTools.Common/Operation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace CloudTools 7 | { 8 | /// 9 | /// Represents an operation on spatial datasets. 10 | /// 11 | class Operation 12 | { 13 | public: 14 | typedef std::function ProgressType; 15 | 16 | private: 17 | bool _isPrepared = false; 18 | bool _isExecuted = false; 19 | 20 | public: 21 | virtual ~Operation() { } 22 | 23 | /// 24 | /// Determines whether the operation is prepared. 25 | /// 26 | /// true if the operation has been already prepared; otherwise false. 27 | bool isPrepared() const { return _isPrepared; } 28 | 29 | /// 30 | /// Determines whether the operation is executed. 31 | /// 32 | /// true if the operation has been already executed; otherwise false. 33 | bool isExecuted() const { return _isExecuted; } 34 | 35 | /// 36 | /// Prepares the operation. 37 | /// 38 | /// Forces reevaluation if necessary. 39 | void prepare(bool force = false); 40 | 41 | /// 42 | /// Executes the operation. 43 | /// 44 | /// Forces reevaluation if necessary. 45 | void execute(bool force = false); 46 | 47 | protected: 48 | /// 49 | /// Verifies input data and prepares the output. 50 | /// 51 | virtual void onPrepare() = 0; 52 | 53 | /// 54 | /// Produces the output data. 55 | /// 56 | virtual void onExecute() = 0; 57 | }; 58 | 59 | /// 60 | /// Represents an iterative operation sequence on spatial datasets. 61 | /// 62 | class OperationSequence : public Operation 63 | { 64 | public: 65 | /// 66 | /// Prepares the next operation. 67 | /// 68 | /// Forces reevaluation if necessary. 69 | virtual void prepareNext(bool force = false) = 0; 70 | 71 | /// 72 | /// Executes the next operation. 73 | /// 74 | /// Forces reevaluation if necessary. 75 | virtual void executeNext(bool force = false) = 0; 76 | 77 | /// 78 | /// Determines whether all steps of the computation has been evaluated. 79 | /// 80 | virtual bool end() const = 0; 81 | 82 | protected: 83 | void onPrepare() override final { } 84 | void onExecute() override final; 85 | }; 86 | } // CloudTools 87 | -------------------------------------------------------------------------------- /CloudTools.DEM.Difference/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(../) 2 | 3 | add_executable(dem_diff 4 | main.cpp) 5 | target_link_libraries(dem_diff 6 | dem common 7 | ${GDAL_LIBRARY}) 8 | 9 | install(TARGETS dem_diff 10 | DESTINATION ${CMAKE_INSTALL_PREFIX}) 11 | -------------------------------------------------------------------------------- /CloudTools.DEM.Mask/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(../) 2 | 3 | add_executable(dem_mask 4 | main.cpp) 5 | target_link_libraries(dem_mask 6 | dem common) 7 | 8 | install(TARGETS dem_mask 9 | DESTINATION ${CMAKE_INSTALL_PREFIX}) 10 | -------------------------------------------------------------------------------- /CloudTools.DEM/Algorithms/HierachicalClustering.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "../DatasetTransformation.hpp" 6 | #include "../ClusterMap.h" 7 | 8 | namespace CloudTools 9 | { 10 | namespace DEM 11 | { 12 | /// 13 | /// Represents a hierarchical clustering for DEM datasets. 14 | /// 15 | template 16 | class HierarchicalClustering : public DatasetTransformation 17 | { 18 | public: 19 | enum Method 20 | { 21 | Agglomerative, 22 | //Divisive 23 | }; 24 | 25 | /// 26 | /// The applied hierarchical clustering method. 27 | /// 28 | Method method; 29 | 30 | /// 31 | /// The threshold of accepted height difference between neighbouring grid points. 32 | /// 33 | double threshold = 0.5; 34 | 35 | /// 36 | /// The maximum number of iterations to be applied in the algorithm. 37 | /// 38 | int maxIterations = 100; 39 | 40 | /// 41 | /// The minimum size of clusters to include in the final result. 42 | /// 43 | int minimumSize = 4; 44 | 45 | public: 46 | /// 47 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 48 | /// 49 | /// The source path of the algorithm. 50 | /// The target file of the algorithm. 51 | /// The applied hierarchical clustering method. 52 | /// The callback method to report progress. 53 | HierarchicalClustering(const std::string& sourcePath, 54 | const std::string& targetPath, 55 | Method method = Method::Agglomerative, 56 | Operation::ProgressType progress = nullptr) 57 | : DatasetTransformation({ sourcePath }, targetPath, nullptr, progress), 58 | method(method) 59 | { 60 | initialize(); 61 | } 62 | 63 | /// 64 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 65 | /// 66 | /// The source dataset of the algorithm. 67 | /// The target file of the algorithm. 68 | /// The applied hierarchical clustering method. 69 | /// The callback method to report progress. 70 | HierarchicalClustering(GDALDataset* sourceDataset, 71 | const std::string& targetPath, 72 | Method method = Method::Agglomerative, 73 | Operation::ProgressType progress = nullptr) 74 | : DatasetTransformation({ sourceDataset }, targetPath, nullptr, progress), 75 | method(method) 76 | { 77 | initialize(); 78 | } 79 | 80 | HierarchicalClustering(const HierarchicalClustering&) = delete; 81 | HierarchicalClustering& operator=(const HierarchicalClustering&) = delete; 82 | 83 | private: 84 | /// 85 | /// Initializes the new instance of the class. 86 | /// 87 | void initialize(); 88 | }; 89 | 90 | template 91 | void HierarchicalClustering::initialize() 92 | { 93 | this->nodataValue = 0; 94 | 95 | // https://en.wikipedia.org/wiki/Hierarchical_clustering 96 | this->computation = [this](int sizeX, int sizeY) 97 | { 98 | if (this->maxIterations < 1) 99 | this->maxIterations = 1; 100 | 101 | ClusterMap clusterMap; 102 | 103 | switch (method) 104 | { 105 | case Method::Agglomerative: 106 | { 107 | for (int i = 0; i < sizeX; ++i) 108 | for (int j = 0; j < sizeY; ++j) 109 | if (this->hasSourceData(i, j)) 110 | { 111 | clusterMap.createCluster(i, j); 112 | } 113 | 114 | auto mergeClusters = [this, &clusterMap](int x1, int y1, int x2, int y2) 115 | { 116 | if (this->hasSourceData(x1, y1) && this->hasSourceData(x2, y2) && 117 | clusterMap.clusterIndex(x1, y1) != clusterMap.clusterIndex(x2, y2)) 118 | { 119 | DataType diff = std::abs(this->sourceData(x1, y1) - this->sourceData(x2, y2)); 120 | if (diff < this->threshold) 121 | { 122 | clusterMap.mergeClusters( 123 | clusterMap.clusterIndex(x1, y1), 124 | clusterMap.clusterIndex(x2, y2)); 125 | 126 | return true; 127 | } 128 | } 129 | return false; 130 | }; 131 | if (this->progress) 132 | this->progress(0.1f, "Cluster map created"); 133 | 134 | int changes; 135 | int iteration = 0; 136 | do 137 | { 138 | changes = 0; 139 | ++iteration; 140 | 141 | for (int i = 0; i < sizeX; ++i) 142 | for (int j = 0; j < sizeY; ++j) 143 | if (this->hasSourceData(i, j)) 144 | { 145 | /* 146 | * Right & downwards merge direction: 147 | * o 1 148 | * 2 3 149 | */ 150 | 151 | bool merged = false; 152 | merged |= mergeClusters(i, j, i, j + 1); 153 | merged |= mergeClusters(i, j, i + 1, j); 154 | merged |= mergeClusters(i, j, i + 1, j + 1); 155 | 156 | if (merged) 157 | ++changes; 158 | } 159 | 160 | if (this->progress) 161 | this->progress(0.1f + 0.8f / this->maxIterations, 162 | "Finished clustering round #" + std::to_string(iteration) + 163 | " with " + std::to_string(changes) + " changes"); 164 | } while (changes > 0 && iteration < this->maxIterations); 165 | 166 | break; 167 | } 168 | } 169 | if (this->progress) 170 | this->progress(0.9f, "Clustering completed"); 171 | 172 | if (this->minimumSize > 1) 173 | { 174 | clusterMap.removeSmallClusters(this->minimumSize); 175 | if (this->progress) 176 | this->progress(0.95f, "Small clusters removed"); 177 | } 178 | 179 | for (GUInt32 index : clusterMap.clusterIndexes()) 180 | for (const OGRPoint& point : clusterMap.points(index)) 181 | { 182 | this->setTargetData(point.getX(), point.getY(), index); 183 | } 184 | 185 | if (this->progress) 186 | this->progress(1.f, "Target created"); 187 | }; 188 | } 189 | } // DEM 190 | } // CloudTools 191 | -------------------------------------------------------------------------------- /CloudTools.DEM/Algorithms/MatrixTransformation.cpp: -------------------------------------------------------------------------------- 1 | #include "../Window.hpp" 2 | #include "MatrixTransformation.h" 3 | 4 | using namespace CloudTools::DEM; 5 | 6 | namespace CloudTools 7 | { 8 | namespace DEM 9 | { 10 | void MatrixTransformation::initialize() 11 | { 12 | // Initialize convolution matrix 13 | const int matrixSize = 2 * _range + 1; 14 | this->_matrix = new float[matrixSize * matrixSize]; 15 | std::fill(this->_matrix, this->_matrix + matrixSize * matrixSize, 1.f); 16 | 17 | // Set computation method 18 | this->computation = [this](int x, int y, const std::vector>& sources) 19 | { 20 | const Window& source = sources[0]; 21 | if (!source.hasData()) return static_cast(this->nodataValue); 22 | 23 | float value = 0; 24 | float counter = 0; 25 | for(int i = -this->range(); i <= this->range(); ++i) 26 | for(int j = -this->range(); j <= this->range(); ++j) 27 | if (source.hasData(i, j)) 28 | { 29 | const float matrixValue = this->getMatrix(i, j); 30 | value += (source.data(i, j) * matrixValue); 31 | counter += matrixValue; 32 | } 33 | 34 | return value / counter; 35 | }; 36 | this->nodataValue = 0; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /CloudTools.DEM/Algorithms/MatrixTransformation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "../SweepLineTransformation.hpp" 5 | 6 | namespace CloudTools 7 | { 8 | namespace DEM 9 | { 10 | /// 11 | /// Convolution matrix transformation. 12 | /// 13 | class MatrixTransformation : public SweepLineTransformation 14 | { 15 | private: 16 | /// 17 | /// Kernel. 18 | /// 19 | float* _matrix; 20 | 21 | public: 22 | /// 23 | /// Initializes a new instance of the class. Loads input metadata and defines calculation. 24 | /// 25 | /// The source file of the matrix transformation. 26 | /// The target file of the matrix transformation. 27 | /// The callback method to report progress. 28 | MatrixTransformation(std::string sourcePath, 29 | const std::string& targetPath, 30 | int range, 31 | Operation::ProgressType progress = nullptr) 32 | : SweepLineTransformation({ sourcePath }, targetPath, range, nullptr, progress) 33 | { 34 | initialize(); 35 | } 36 | 37 | /// 38 | /// Initializes a new instance of the class. Loads input metadata and defines calculation. 39 | /// 40 | /// The source dataset of the matrix transformation. 41 | /// The target file of the matrix transformation. 42 | /// The callback method to report progress. 43 | MatrixTransformation(GDALDataset* sourceDataset, 44 | const std::string& targetPath, 45 | int range, 46 | ProgressType progress = nullptr) 47 | : SweepLineTransformation({ sourceDataset }, targetPath, range, nullptr, progress) 48 | { 49 | initialize(); 50 | } 51 | 52 | ~MatrixTransformation() 53 | { 54 | delete[] this->_matrix; 55 | } 56 | 57 | MatrixTransformation(const MatrixTransformation&) = delete; 58 | MatrixTransformation& operator=(const MatrixTransformation&) = delete; 59 | 60 | float getMatrix(int i, int j) const 61 | { 62 | if (i < -_range || i > _range) 63 | throw std::out_of_range("i is out of range."); 64 | if (j < -_range || j > _range) 65 | throw std::out_of_range("j is out of range."); 66 | 67 | const int matrixSize = 2 * _range + 1; 68 | return _matrix[(_range + i) * matrixSize + (_range + j)]; 69 | } 70 | 71 | void setMatrix(int i, int j, float value) 72 | { 73 | if (i < -_range || i > _range) 74 | throw std::out_of_range("i is out of range."); 75 | if (j < -_range || j > _range) 76 | throw std::out_of_range("j is out of range."); 77 | 78 | const int matrixSize = 2 * _range + 1; 79 | _matrix[(_range + i) * matrixSize + (_range + j)] = value; 80 | } 81 | 82 | private: 83 | void initialize(); 84 | }; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /CloudTools.DEM/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(../) 2 | 3 | add_library(dem 4 | Calculation.cpp Calculation.h 5 | Creation.cpp Creation.h 6 | Transformation.cpp Transformation.h 7 | Color.cpp Color.h 8 | Helper.cpp Helper.h 9 | Metadata.cpp Metadata.h 10 | Rasterize.cpp Rasterize.h 11 | ClusterMap.cpp ClusterMap.h 12 | Window.hpp 13 | SweepLineCalculation.hpp 14 | SweepLineTransformation.hpp 15 | DatasetCalculation.hpp 16 | DatasetTransformation.hpp 17 | Filters/ClusterFilter.hpp 18 | Filters/MajorityFilter.hpp 19 | Filters/MorphologyFilter.hpp 20 | Filters/NoiseFilter.hpp 21 | Comparers/Difference.hpp 22 | Algorithms/HierachicalClustering.hpp 23 | Algorithms/MatrixTransformation.cpp Algorithms/MatrixTransformation.h) 24 | -------------------------------------------------------------------------------- /CloudTools.DEM/Calculation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | #include "Metadata.h" 10 | 11 | namespace CloudTools 12 | { 13 | namespace DEM 14 | { 15 | /// 16 | /// Represents a calculation on DEM datasets. 17 | /// 18 | class Calculation : public virtual Operation 19 | { 20 | public: 21 | /// 22 | /// The spatial reference system for the sources and the target. 23 | /// 24 | /// 25 | /// Setting this property will override SRS detection. 26 | /// 27 | std::string spatialReference; 28 | 29 | /// 30 | /// Callback function for reporting progress. 31 | /// 32 | ProgressType progress; 33 | 34 | /// 35 | /// Enforces strict data type matching for the source files. 36 | /// 37 | bool strictTypes = false; 38 | 39 | protected: 40 | std::vector _sourcePaths; 41 | std::vector _sourceDatasets; 42 | std::vector _sourceMetadata; 43 | bool _sourceOwnership; 44 | 45 | RasterMetadata _targetMetadata; 46 | public: 47 | /// 48 | /// Initializes a new instance of the class and loads source metadata. 49 | /// 50 | /// The source files of the calculation. 51 | /// The target file of the calculation. 52 | /// The callback method to report progress. 53 | Calculation(const std::vector& sourcePaths, 54 | ProgressType progress = nullptr); 55 | 56 | /// 57 | /// Initializes a new instance of the class and loads source metadata. 58 | /// 59 | /// The source datasets of the calculation. 60 | /// The target file of the calculation. 61 | /// The callback method to report progress. 62 | Calculation(const std::vector& sourceDatasets, 63 | ProgressType progress = nullptr); 64 | 65 | Calculation(const Calculation&) = delete; 66 | Calculation& operator=(const Calculation&) = delete; 67 | ~Calculation(); 68 | 69 | std::size_t sourceCount() const 70 | { 71 | return _sourceDatasets.size(); 72 | } 73 | 74 | const RasterMetadata& sourceMetadata(unsigned int index) const; 75 | const RasterMetadata& sourceMetadata(const std::string& file) const; 76 | const RasterMetadata& targetMetadata() const; 77 | 78 | protected: 79 | /// 80 | /// Verifies sources and calculates the metadata for the target. 81 | /// 82 | void onPrepare() override; 83 | }; 84 | } // DEM 85 | } // CloudTools 86 | -------------------------------------------------------------------------------- /CloudTools.DEM/ClusterMap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "Helper.h" 14 | 15 | namespace CloudTools 16 | { 17 | namespace DEM 18 | { 19 | /// 20 | /// Represents a cluster map of a DEM dataset. 21 | /// 22 | class ClusterMap 23 | { 24 | private: 25 | std::map _seedPoints; 26 | std::map> _clusterIndexes; 27 | std::unordered_map _clusterPoints; 28 | GUInt32 _nextClusterIndex = 1; 29 | int _sizeX, _sizeY; 30 | 31 | public: 32 | /// 33 | /// Initializes a new, empty instance of the class. 34 | /// 35 | ClusterMap() = default; 36 | 37 | /// 38 | /// Initializes a new instance of the class with maximum width and height. 39 | /// 40 | /// The height of the cluster map. 41 | /// The width of the cluster map. 42 | ClusterMap(int sizeX, int sizeY) : _sizeX(sizeX), _sizeY(sizeY) 43 | { 44 | } 45 | 46 | void setSizeX(int x); 47 | 48 | void setSizeY(int y); 49 | 50 | int sizeX(); 51 | 52 | int sizeY(); 53 | 54 | /// 55 | /// Retrieves the cluster index for a given grid point. 56 | /// 57 | /// The abcissa of the point. 58 | /// The ordinate of the point. 59 | /// The cluster index for the point. 60 | GUInt32 clusterIndex(int x, int y) const; 61 | 62 | /// 63 | /// Retrieves all cluster indexes. 64 | /// 65 | /// The indexes. 66 | std::vector clusterIndexes() const; 67 | 68 | /// 69 | /// Attaches a given grid point to the given cluster. 70 | /// 71 | /// The index of the cluster. 72 | /// The abcissa of the point. 73 | /// The ordinate of the point. 74 | /// The height of the initial point. 75 | /// 76 | void addPoint(GUInt32 clusterIndex, int x, int y, double z = 0.0); 77 | 78 | /// 79 | /// Eliminates a given grid point from the given cluster. 80 | /// 81 | /// The index of the cluster. 82 | /// The abcissa of the point. 83 | /// The ordinate of the point. 84 | void removePoint(GUInt32 clusterIndex, int x, int y); 85 | 86 | /// 87 | /// Retrieves the direct neighbors of the points in a cluster. 88 | /// 89 | /// The index of the cluster. 90 | /// The neighboring points contained by the cluster. 91 | std::vector neighbors(GUInt32 clusterIndex) const; 92 | 93 | /// 94 | /// Calculates the 3 dimensional center of gravity of a cluster by 95 | /// taking the average of the coordinates of its points. 96 | /// 97 | /// The index of the cluster. 98 | /// The center of gravity of the cluster. 99 | OGRPoint center3D(GUInt32 clusterIndex) const; 100 | 101 | /// 102 | /// Calculates the 2 dimensional center of a cluster by 103 | /// taking the average of the X and Y coordinates of its points. 104 | /// 105 | /// The index of the cluster. 106 | /// The center of the cluster. 107 | OGRPoint center2D(GUInt32 clusterIndex) const; 108 | 109 | /// 110 | /// Retrieves the point with the biggest Z coordinate in the cluster. 111 | /// 112 | /// The index of the cluster. 113 | /// The highest point of the cluster. 114 | OGRPoint highestPoint(GUInt32 clusterIndex) const; 115 | 116 | /// 117 | /// Retrieves the point with the lowest Z coordinate in the cluster. 118 | /// 119 | /// The index of the cluster. 120 | /// The lowest point of the cluster. 121 | OGRPoint lowestPoint(GUInt32 clusterIndex) const; 122 | 123 | /// 124 | /// Returns the minimal bounding box of the cluster. 125 | /// 126 | /// The index of the cluster. 127 | /// 128 | std::vector boundingBox(GUInt32 clusterIndex) const; 129 | 130 | /// 131 | /// Retrieves the seed point of a cluster. 132 | /// 133 | /// The index of the cluster. 134 | /// The seed point of the cluster. 135 | OGRPoint seedPoint(GUInt32 clusterIndex) const; 136 | 137 | /// 138 | /// Retrieves the points in a cluster. 139 | /// 140 | /// The index of the cluster. 141 | /// The points contained by the cluster. 142 | const std::vector& points(GUInt32 clusterIndex) const; 143 | 144 | /// 145 | /// Creates a new cluster with an initial point. 146 | /// 147 | /// The abcissa of the initial point. 148 | /// The ordinate of the initial point. 149 | /// The height of the initial point. 150 | /// The id of the created cluster. 151 | GUInt32 createCluster(int x, int y, double z = 0.0); 152 | 153 | /// 154 | /// Merges 2 clusters. 155 | /// 156 | /// 157 | /// The smaller cluster will be merged into the larger one. 158 | /// 159 | /// 160 | /// 161 | void mergeClusters(GUInt32 clusterA, GUInt32 clusterB); 162 | 163 | /// 164 | /// Removes a cluster from the mapping. 165 | /// 166 | /// The index of the cluster to be removed. 167 | void removeCluster(GUInt32 clusterIndex); 168 | 169 | /// 170 | /// Removes small clusters. 171 | /// 172 | /// The minimum threshold of size to keep a cluster. 173 | /// The number of removed clusters. 174 | std::size_t removeSmallClusters(unsigned int threshold); 175 | 176 | /// 177 | /// Shuffles the points in the clusters in a randomized order. 178 | /// 179 | void shuffle(); 180 | 181 | private: 182 | static std::random_device rd; 183 | static std::mt19937 engine; 184 | }; 185 | } // DEM 186 | } // CloudTools 187 | -------------------------------------------------------------------------------- /CloudTools.DEM/Color.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "Color.h" 9 | 10 | namespace CloudTools 11 | { 12 | namespace DEM 13 | { 14 | Color::Color(int red, int green, int blue, int alpha) 15 | { 16 | setRed(red); 17 | setGreen(green); 18 | setBlue(blue); 19 | setAlpha(alpha); 20 | } 21 | 22 | const Color Color::Red(255, 0, 0); 23 | const Color Color::Green(0, 255, 0); 24 | const Color Color::Blue(0, 0, 255); 25 | const Color Color::White(255, 255, 255); 26 | const Color Color::Black(0, 0, 0); 27 | const Color Color::Transparent(0, 0, 0, 0); 28 | 29 | std::istream& operator>>(std::istream &in, Color &color) 30 | { 31 | std::string input; 32 | in >> input; 33 | std::transform(input.begin(), input.end(), input.begin(), ::tolower); 34 | 35 | if (input == "red") 36 | color = Color::Red; 37 | else if (input == "green") 38 | color = Color::Green; 39 | else if (input == "blue") 40 | color = Color::Blue; 41 | else if (input == "white") 42 | color = Color::White; 43 | else if (input == "black") 44 | color = Color::Black; 45 | else if (input == "transparent") 46 | color = Color::Transparent; 47 | else 48 | { 49 | std::vector params; 50 | boost::split(params, input, boost::is_any_of(",")); 51 | if (params.size() < 3) 52 | throw std::invalid_argument("At least 3 bands must given for an RGBA color."); 53 | if (params.size() > 4) 54 | throw std::invalid_argument("Maximum 4 bands can given for an RGBA color."); 55 | 56 | try 57 | { 58 | color.setRed(std::stoi(params[0])); 59 | color.setGreen(std::stoi(params[1])); 60 | color.setBlue(std::stoi(params[2])); 61 | if (params.size() == 4) 62 | color.setAlpha(std::stoi(params[3])); 63 | else 64 | color.setAlpha(255); 65 | } 66 | catch (std::invalid_argument&) 67 | { 68 | throw std::invalid_argument("Bad value for color band, values must be between 0-255."); 69 | } 70 | } 71 | return in; 72 | } 73 | } // DEM 74 | } // CloudTools 75 | -------------------------------------------------------------------------------- /CloudTools.DEM/Color.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace CloudTools 7 | { 8 | namespace DEM 9 | { 10 | /// 11 | /// Represents a color in the RGBA space. 12 | /// 13 | class Color 14 | { 15 | public: 16 | static const Color Red; 17 | static const Color Green; 18 | static const Color Blue; 19 | static const Color White; 20 | static const Color Black; 21 | static const Color Transparent; 22 | 23 | private: 24 | int _red; 25 | int _green; 26 | int _blue; 27 | int _alpha; 28 | 29 | public: 30 | Color() : Color(255, 255, 255) { } 31 | Color(int red, int green, int blue, int alpha = 255); 32 | 33 | bool operator==(const Color &other) const 34 | { 35 | return 36 | _red == other._red && 37 | _green == other._green && 38 | _blue == other._blue && 39 | _alpha == other._alpha; 40 | } 41 | bool operator!=(const Color &other) const 42 | { 43 | return !(*this == other); 44 | } 45 | 46 | int red() const { return _red; } 47 | int green() const { return _green; } 48 | int blue() const { return _blue; } 49 | int alpha() const { return _alpha; } 50 | 51 | void setRed(int value) 52 | { 53 | if (value < 0 || value > 255) 54 | throw std::invalid_argument("The value should be between 0 and 255."); 55 | _red = value; 56 | } 57 | 58 | void setGreen(int value) 59 | { 60 | if (value < 0 || value > 255) 61 | throw std::invalid_argument("The value should be between 0 and 255."); 62 | _green = value; 63 | } 64 | 65 | void setBlue(int value) 66 | { 67 | if (value < 0 || value > 255) 68 | throw std::invalid_argument("The value should be between 0 and 255."); 69 | _blue = value; 70 | } 71 | 72 | void setAlpha(int value) 73 | { 74 | if (value < 0 || value > 255) 75 | throw std::invalid_argument("The value should be between 0 and 255."); 76 | _alpha = value; 77 | } 78 | }; 79 | 80 | std::istream& operator>>(std::istream &in, Color &color); 81 | } // DEM 82 | } // CloudTools 83 | -------------------------------------------------------------------------------- /CloudTools.DEM/Comparers/Difference.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../SweepLineTransformation.hpp" 8 | #include "../Window.hpp" 9 | 10 | namespace CloudTools 11 | { 12 | namespace DEM 13 | { 14 | /// 15 | /// Represents a difference comparison for DEM datasets. 16 | /// 17 | template 18 | class Difference : public SweepLineTransformation 19 | { 20 | public: 21 | double maximumThreshold = 1000; 22 | double minimumThreshold = 0; 23 | 24 | public: 25 | /// 26 | /// Initializes a new instance of the class. Loads input metadata and defines calculation. 27 | /// 28 | /// The source files of the difference comparison. 29 | /// The target file of the difference comparison. 30 | /// The callback method to report progress. 31 | Difference(const std::vector& sourcePaths, 32 | const std::string& targetPath, 33 | Operation::ProgressType progress = nullptr) 34 | : SweepLineTransformation(sourcePaths, targetPath, nullptr, progress) 35 | { 36 | initialize(); 37 | } 38 | 39 | /// 40 | /// Initializes a new instance of the class. Loads input metadata and defines calculation. 41 | /// 42 | /// The source datasets of the difference comparison. 43 | /// The target file of the difference comparison. 44 | /// The callback method to report progress. 45 | Difference(const std::vector& sourceDatasets, 46 | const std::string& targetPath, 47 | Operation::ProgressType progress = nullptr) 48 | : SweepLineTransformation(sourceDatasets, targetPath, 0, nullptr, progress) 49 | { 50 | initialize(); 51 | } 52 | 53 | Difference(const Difference&) = delete; 54 | Difference& operator=(const Difference&) = delete; 55 | 56 | private: 57 | /// 58 | /// Initializes the new instance of the class. 59 | /// 60 | void initialize(); 61 | }; 62 | 63 | template 64 | void Difference::initialize() 65 | { 66 | this->computation = [this](int x, int y, const std::vector>& sources) 67 | { 68 | if (!sources[0].hasData() || !sources[1].hasData()) 69 | return static_cast(this->nodataValue); 70 | 71 | DataType difference = sources[1].data() - sources[0].data(); 72 | if (std::abs(difference) >= this->maximumThreshold || std::abs(difference) <= this->minimumThreshold) 73 | difference = static_cast(this->nodataValue); 74 | return difference; 75 | }; 76 | } 77 | } // DEM 78 | } // CloudTools 79 | -------------------------------------------------------------------------------- /CloudTools.DEM/Creation.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Creation.h" 4 | 5 | namespace CloudTools 6 | { 7 | namespace DEM 8 | { 9 | Creation::~Creation() 10 | { 11 | if (_targetOwnerShip && _targetDataset != nullptr) 12 | GDALClose(_targetDataset); 13 | } 14 | 15 | GDALDataset* Creation::target() 16 | { 17 | if (!isExecuted()) 18 | throw std::logic_error("The computation is not executed."); 19 | _targetOwnerShip = false; 20 | return _targetDataset; 21 | } 22 | } // DEM 23 | } // CloudTools 24 | -------------------------------------------------------------------------------- /CloudTools.DEM/Creation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include "Metadata.h" 8 | 9 | namespace CloudTools 10 | { 11 | namespace DEM 12 | { 13 | /// 14 | /// Represents a creation of a DEM dataset. 15 | /// 16 | class Creation : public virtual Operation 17 | { 18 | public: 19 | /// 20 | /// Target output format. 21 | /// 22 | /// 23 | /// For supported formats, . 24 | /// 25 | std::string targetFormat = "GTiff"; 26 | 27 | /// 28 | /// Format specific output creation options. 29 | /// 30 | /// 31 | /// For supported options, . 32 | /// 33 | std::map createOptions; 34 | 35 | /// 36 | /// The nodata value. 37 | /// 38 | /// 39 | /// Default value as defined by GDAL (gdalrasterband.cpp). 40 | /// 41 | double nodataValue = -1e10; 42 | 43 | protected: 44 | std::string _targetPath; 45 | GDALDataset* _targetDataset = nullptr; 46 | bool _targetOwnerShip = true; 47 | 48 | public: 49 | /// 50 | /// Initializes a new instance of the class. 51 | /// 52 | /// The target file of the creation. 53 | Creation(const std::string& targetPath) 54 | : _targetPath(targetPath) 55 | { } 56 | 57 | Creation(const Creation&) = delete; 58 | Creation& operator=(const Creation&) = delete; 59 | ~Creation(); 60 | 61 | /// 62 | /// Retrieves the target dataset. 63 | /// 64 | /// 65 | /// By calling this method, the target datatset will be released by the transformation and won't be automatically freed. 66 | /// 67 | /// The target dataset. 68 | GDALDataset* target(); 69 | }; 70 | } // DEM 71 | } // CloudTools 72 | -------------------------------------------------------------------------------- /CloudTools.DEM/Filters/MajorityFilter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "../Window.hpp" 7 | #include "../SweepLineTransformation.hpp" 8 | 9 | namespace CloudTools 10 | { 11 | namespace DEM 12 | { 13 | /// 14 | /// Represents a majority filter for DEM datasets. 15 | /// 16 | template 17 | class MajorityFilter : public SweepLineTransformation 18 | { 19 | public: 20 | /// 21 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 22 | /// 23 | /// The source path of the filter. 24 | /// The target file of the filter. 25 | /// The range of surrounding data to involve. 26 | /// The callback method to report progress. 27 | MajorityFilter(const std::string& sourcePath, 28 | const std::string& targetPath, 29 | int range, 30 | Operation::ProgressType progress = nullptr) 31 | : SweepLineTransformation({ sourcePath }, targetPath, range, nullptr, progress) 32 | { 33 | initialize(); 34 | } 35 | 36 | /// 37 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 38 | /// 39 | /// The source dataset of the filter. 40 | /// The target file of the filter. 41 | /// The range of surrounding data to involve. 42 | /// The callback method to report progress. 43 | MajorityFilter(GDALDataset* sourceDataset, 44 | const std::string& targetPath, 45 | int range, 46 | Operation::ProgressType progress = nullptr) 47 | : SweepLineTransformation({ sourceDataset }, targetPath, range, nullptr, progress) 48 | { 49 | initialize(); 50 | } 51 | 52 | MajorityFilter(const MajorityFilter&) = delete; 53 | MajorityFilter& operator=(const MajorityFilter&) = delete; 54 | 55 | private: 56 | /// 57 | /// Initializes the new instance of the class. 58 | /// 59 | void initialize(); 60 | }; 61 | 62 | template 63 | void MajorityFilter::initialize() 64 | { 65 | // http://desktop.arcgis.com/en/arcmap/10.3/tools/spatial-analyst-toolbox/majority-filter.htm 66 | // http://desktop.arcgis.com/en/arcmap/10.3/tools/spatial-analyst-toolbox/smoothing-zone-edges-with-boundary-clean-and-majority-filter.htm 67 | 68 | this->computation = [this](int x, int y, const std::vector>& sources) 69 | { 70 | const Window& source = sources[0]; 71 | 72 | float sum = 0; 73 | int counter = 0; 74 | for (int i = -this->range(); i <= this->range(); ++i) 75 | for (int j = -this->range(); j <= this->range(); ++j) 76 | if (source.hasData(i, j)) 77 | { 78 | sum += source.data(i, j); 79 | ++counter; 80 | } 81 | 82 | if (counter < (std::pow(this->range() * 2 + 1, 2) / 2)) return static_cast(this->nodataValue); 83 | else return source.hasData() ? source.data() : sum / counter; 84 | }; 85 | this->nodataValue = 0; 86 | } 87 | } // DEM 88 | } // CloudTools 89 | -------------------------------------------------------------------------------- /CloudTools.DEM/Filters/MorphologyFilter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "../Window.hpp" 6 | #include "../SweepLineTransformation.hpp" 7 | 8 | namespace CloudTools 9 | { 10 | namespace DEM 11 | { 12 | /// 13 | /// Represents a noise filter for DEM datasets. 14 | /// 15 | template 16 | class MorphologyFilter : public SweepLineTransformation 17 | { 18 | public: 19 | enum Method 20 | { 21 | Dilation, 22 | Erosion 23 | }; 24 | 25 | /// 26 | /// The applied morphology method. 27 | /// 28 | Method method; 29 | 30 | /// 31 | /// Threshold value for morphology filter. 32 | /// 33 | /// Default value is -1, which will be 0 for dilation and 9 for erosion. 34 | /// 35 | int threshold = -1; 36 | 37 | public: 38 | /// 39 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 40 | /// 41 | /// The source path of the filter. 42 | /// The target file of the filter. 43 | /// The applied morphology method. 44 | /// The callback method to report progress. 45 | MorphologyFilter(const std::string& sourcePath, 46 | const std::string& targetPath, 47 | Method method = Method::Dilation, 48 | Operation::ProgressType progress = nullptr) 49 | : SweepLineTransformation({ sourcePath }, targetPath, 1, nullptr, progress), 50 | method(method) 51 | { 52 | initialize(); 53 | } 54 | 55 | /// 56 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 57 | /// 58 | /// The source dataset of the filter. 59 | /// The target file of the filter. 60 | /// The applied morphology method. 61 | /// The callback method to report progress. 62 | MorphologyFilter(GDALDataset* sourceDataset, 63 | const std::string& targetPath, 64 | Method method = Method::Dilation, 65 | Operation::ProgressType progress = nullptr) 66 | : SweepLineTransformation({ sourceDataset }, targetPath, 1, nullptr, progress), 67 | method(method) 68 | { 69 | initialize(); 70 | } 71 | 72 | MorphologyFilter(const MorphologyFilter&) = delete; 73 | MorphologyFilter& operator=(const MorphologyFilter&) = delete; 74 | 75 | private: 76 | /// 77 | /// Initializes the new instance of the class. 78 | /// 79 | void initialize(); 80 | }; 81 | 82 | template 83 | void MorphologyFilter::initialize() 84 | { 85 | // https://en.wikipedia.org/wiki/Mathematical_morphology 86 | // https://www.cs.auckland.ac.nz/courses/compsci773s1c/lectures/ImageProcessing-html/topic4.htm 87 | 88 | this->computation = [this](int x, int y, const std::vector>& sources) 89 | { 90 | if (this->method == Method::Dilation && this->threshold == -1) 91 | this->threshold = 0; 92 | if (this->method == Method::Erosion && this->threshold == -1) 93 | this->threshold = 9; 94 | 95 | const Window& source = sources[0]; 96 | 97 | float sum = 0; 98 | int counter = 0; 99 | for (int i = -1; i <= 1; ++i) 100 | for (int j = -1; j <= 1; ++j) 101 | if (source.hasData(i, j)) 102 | { 103 | sum += source.data(i, j); 104 | ++counter; 105 | } 106 | 107 | if (this->method == Method::Dilation && !source.hasData() && counter > this->threshold) 108 | return sum / counter; 109 | if (this->method == Method::Erosion && source.hasData() && counter < this->threshold) 110 | return static_cast(this->nodataValue); 111 | return source.hasData() ? source.data() : static_cast(this->nodataValue); 112 | }; 113 | this->nodataValue = 0; 114 | } 115 | } // DEM 116 | } // CloudTools 117 | -------------------------------------------------------------------------------- /CloudTools.DEM/Filters/NoiseFilter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../SweepLineTransformation.hpp" 8 | #include "../Window.hpp" 9 | 10 | namespace CloudTools 11 | { 12 | namespace DEM 13 | { 14 | /// 15 | /// Represents a noise filter for DEM datasets. 16 | /// 17 | template 18 | class NoiseFilter : public SweepLineTransformation 19 | { 20 | public: 21 | /// 22 | /// The threshold of noise in percentage (between values 0 and 1). 23 | /// 24 | /// 25 | /// Noise is the average percentage of difference compared to the surrounding area. 26 | /// 27 | double threshold = 0.5; 28 | 29 | public: 30 | /// 31 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 32 | /// 33 | /// The source path of the filter. 34 | /// The target file of the filter. 35 | /// The range of surrounding data to involve. 36 | /// The callback method to report progress. 37 | NoiseFilter(const std::string& sourcePath, 38 | const std::string& targetPath, 39 | int range, 40 | Operation::ProgressType progress = nullptr) 41 | : SweepLineTransformation({ sourcePath }, targetPath, range, nullptr, progress) 42 | { 43 | initialize(); 44 | } 45 | 46 | /// 47 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 48 | /// 49 | /// The source dataset of the filter. 50 | /// The target file of the filter. 51 | /// The range of surrounding data to involve. 52 | /// The callback method to report progress. 53 | NoiseFilter(GDALDataset* sourceDataset, 54 | const std::string& targetPath, 55 | int range, 56 | Operation::ProgressType progress = nullptr) 57 | : SweepLineTransformation({ sourceDataset }, targetPath, range, nullptr, progress) 58 | { 59 | initialize(); 60 | } 61 | 62 | NoiseFilter(const NoiseFilter&) = delete; 63 | NoiseFilter& operator=(const NoiseFilter&) = delete; 64 | 65 | private: 66 | /// 67 | /// Initializes the new instance of the class. 68 | /// 69 | void initialize(); 70 | }; 71 | 72 | template 73 | void NoiseFilter::initialize() 74 | { 75 | // Noise is the average percentage of difference compared to the surrounding area. 76 | this->computation = [this](int x, int y, const std::vector>& sources) 77 | { 78 | const Window& source = sources[0]; 79 | if (!source.hasData()) return static_cast(this->nodataValue); 80 | 81 | float noise = 0; 82 | int counter = -1; 83 | for (int i = -this->range(); i <= this->range(); ++i) 84 | for (int j = -this->range(); j <= this->range(); ++j) 85 | if (source.hasData(i, j)) 86 | { 87 | noise += std::abs(source.data() - source.data(i, j)) 88 | / std::min(std::abs(source.data()), std::abs(source.data(i, j))); 89 | ++counter; 90 | } 91 | 92 | if (counter == 0) return static_cast(this->nodataValue); 93 | if (noise / counter > this->threshold) return static_cast(this->nodataValue); 94 | else return source.data(); 95 | }; 96 | this->nodataValue = 0; 97 | } 98 | } // DEM 99 | } // CloudTools 100 | -------------------------------------------------------------------------------- /CloudTools.DEM/Helper.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "Helper.h" 6 | 7 | namespace CloudTools 8 | { 9 | namespace DEM 10 | { 11 | GDALDataType gdalType(std::string& dataType) 12 | { 13 | std::transform(dataType.begin(), dataType.end(), dataType.begin(), ::tolower); 14 | if (dataType == "byte") 15 | return GDALDataType::GDT_Byte; 16 | if (dataType == "int16") 17 | return GDALDataType::GDT_Int16; 18 | if (dataType == "uint16") 19 | return GDALDataType::GDT_UInt16; 20 | if (dataType == "int32") 21 | return GDALDataType::GDT_Int32; 22 | if (dataType == "uint32") 23 | return GDALDataType::GDT_UInt32; 24 | if (dataType == "float32") 25 | return GDALDataType::GDT_Float32; 26 | if (dataType == "float64") 27 | return GDALDataType::GDT_Float64; 28 | 29 | // Complex types are not supported. 30 | return GDALDataType::GDT_Unknown; 31 | } 32 | 33 | std::string SRSName(const OGRSpatialReference& reference) 34 | { 35 | const char* authorityName = reference.GetAuthorityName(nullptr); 36 | const char* authorityCode = reference.GetAuthorityCode(nullptr); 37 | 38 | if (authorityName && authorityCode) 39 | return std::string(authorityName).append(":").append(authorityCode); 40 | else 41 | return std::string(); 42 | } 43 | 44 | std::string SRSDescription(const OGRSpatialReference& reference) 45 | { 46 | char *wkt; 47 | reference.exportToPrettyWkt(&wkt); 48 | std::string srs = std::string(wkt); 49 | CPLFree(wkt); 50 | 51 | return srs; 52 | } 53 | } // DEM 54 | } // CloudTools 55 | -------------------------------------------------------------------------------- /CloudTools.DEM/Helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | namespace CloudTools 13 | { 14 | namespace DEM 15 | { 16 | /// 17 | /// Returns the GDAL type for . 18 | /// 19 | template 20 | GDALDataType gdalType() 21 | { 22 | const std::type_info& typeInfo = typeid(T); 23 | if (typeInfo == typeid(GByte)) 24 | return GDALDataType::GDT_Byte; 25 | if (typeInfo == typeid(GUInt16)) 26 | return GDALDataType::GDT_UInt16; 27 | if (typeInfo == typeid(GInt16)) 28 | return GDALDataType::GDT_Int16; 29 | if (typeInfo == typeid(GUInt32)) 30 | return GDALDataType::GDT_UInt32; 31 | if (typeInfo == typeid(GInt32)) 32 | return GDALDataType::GDT_Int32; 33 | if (typeInfo == typeid(float)) 34 | return GDALDataType::GDT_Float32; 35 | if (typeInfo == typeid(double)) 36 | return GDALDataType::GDT_Float64; 37 | 38 | // Complex types are not supported. 39 | return GDALDataType::GDT_Unknown; 40 | } 41 | 42 | /// 43 | /// Returns the GDAL type for . 44 | /// 45 | /// 46 | /// May change the capitalization of the parameter. 47 | /// 48 | GDALDataType gdalType(std::string& dataType); 49 | 50 | /// 51 | /// Retrieves the well known name for a spatial reference system. 52 | /// 53 | /// The spatial reference system. 54 | std::string SRSName(const OGRSpatialReference& reference); 55 | 56 | /// 57 | /// Retrieves the pretty-formatted WKT string for a spatial reference system. 58 | /// 59 | /// The spatial reference system. 60 | std::string SRSDescription(const OGRSpatialReference& reference); 61 | } // DEM 62 | } // CloudTools 63 | 64 | namespace std 65 | { 66 | /// 67 | /// Represents the default hashing functor for std::pair objects. 68 | /// 69 | template 70 | struct hash> 71 | { 72 | std::size_t operator() (const std::pair& p) const 73 | { 74 | std::size_t seed = 0; 75 | auto h1 = std::hash{}(p.first); 76 | auto h2 = std::hash{}(p.second); 77 | 78 | boost::hash_combine(seed, h1); 79 | boost::hash_combine(seed, h2); 80 | return seed; 81 | } 82 | }; 83 | } // std 84 | -------------------------------------------------------------------------------- /CloudTools.DEM/Metadata.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | namespace CloudTools 12 | { 13 | namespace DEM 14 | { 15 | /// 16 | /// Represents a metadata collection of a DEM. 17 | /// 18 | class Metadata 19 | { 20 | protected: 21 | Metadata() { }; 22 | 23 | public: 24 | Metadata(const Metadata&) = delete; 25 | Metadata& operator= (const Metadata&) = delete; 26 | virtual ~Metadata() {} 27 | 28 | virtual double originX() const = 0; 29 | virtual double originY() const = 0; 30 | virtual double extentX() const = 0; 31 | virtual double extentY() const = 0; 32 | virtual const OGRSpatialReference& reference() const = 0; 33 | 34 | bool isOverlapping(const Metadata&); 35 | }; 36 | 37 | /// 38 | /// Represents a metadata collection of vector file. 39 | /// 40 | class VectorMetadata : public Metadata 41 | { 42 | protected: 43 | double _originX; 44 | double _originY; 45 | double _extentX; 46 | double _extentY; 47 | OGRSpatialReference _reference; 48 | 49 | public: 50 | VectorMetadata() : _originX(0), _originY(0), 51 | _extentX(0), _extentY(0), 52 | _reference(nullptr) 53 | { } 54 | VectorMetadata(GDALDataset* dataset, const std::vector& layerNames); 55 | VectorMetadata(const std::vector& layers); 56 | 57 | VectorMetadata(const VectorMetadata& other); 58 | VectorMetadata& operator= (const VectorMetadata& other); 59 | 60 | bool operator==(const VectorMetadata &other) const; 61 | bool operator!=(const VectorMetadata &other) const; 62 | 63 | #pragma region Getters 64 | 65 | double originX() const override { return _originX; } 66 | double originY() const override { return _originY; } 67 | double extentX() const override { return _extentX; } 68 | double extentY() const override { return _extentY; } 69 | const OGRSpatialReference& reference() const override { return _reference; } 70 | virtual OGRSpatialReference& reference() { return _reference; } 71 | 72 | #pragma endregion 73 | 74 | #pragma region Setters 75 | 76 | virtual void setOriginX(double originX) { _originX = originX; } 77 | virtual void setOriginY(double originY) { _originY = originY; } 78 | virtual void setExtentX(double extentX) { _extentX = extentX; } 79 | virtual void setExtentY(double extentY) { _extentY = extentY; } 80 | virtual void setReference(const OGRSpatialReference &reference) 81 | { 82 | _reference = reference; 83 | } 84 | 85 | #pragma endregion 86 | 87 | private: 88 | void load(const std::vector& layers); 89 | }; 90 | 91 | /// 92 | /// Represents a metadata collection of a raster file. 93 | /// 94 | class RasterMetadata : public Metadata 95 | { 96 | protected: 97 | double _originX; 98 | double _originY; 99 | int _rasterSizeX; 100 | int _rasterSizeY; 101 | double _pixelSizeX; 102 | double _pixelSizeY; 103 | double _extentX; 104 | double _extentY; 105 | OGRSpatialReference _reference; 106 | 107 | public: 108 | RasterMetadata() : _originX(0), _originY(0), 109 | _rasterSizeX(0), _rasterSizeY(0), 110 | _pixelSizeX(0), _pixelSizeY(0), 111 | _extentX(0), _extentY(0), 112 | _reference(nullptr) 113 | { } 114 | RasterMetadata(GDALDataset* dataset); 115 | 116 | RasterMetadata(const RasterMetadata& other); 117 | RasterMetadata& operator= (const RasterMetadata& other); 118 | 119 | bool operator==(const RasterMetadata &other) const; 120 | bool operator!=(const RasterMetadata &other) const; 121 | 122 | #pragma region Getters 123 | 124 | double originX() const override { return _originX; } 125 | double originY() const override { return _originY; } 126 | virtual int rasterSizeX() const { return _rasterSizeX; } 127 | virtual int rasterSizeY() const { return _rasterSizeY; } 128 | virtual double pixelSizeX() const { return _pixelSizeX; } 129 | virtual double pixelSizeY() const { return _pixelSizeY; } 130 | double extentX() const override { return _extentX; } 131 | double extentY() const override { return _extentY; } 132 | const OGRSpatialReference& reference() const override { return _reference; } 133 | virtual OGRSpatialReference& reference() { return _reference; } 134 | 135 | #pragma endregion 136 | 137 | #pragma region Setters 138 | 139 | virtual void setOriginX(double originX) { _originX = originX; } 140 | virtual void setOriginY(double originY) { _originY = originY; } 141 | virtual void setRasterSizeX(int rasterSizeX) 142 | { 143 | _rasterSizeX = rasterSizeX; 144 | _extentX = std::abs(_rasterSizeX * _pixelSizeX); 145 | } 146 | 147 | virtual void setRasterSizeY(int rasterSizeY) 148 | { 149 | _rasterSizeY = rasterSizeY; 150 | _extentY = std::abs(_rasterSizeY * _pixelSizeY); 151 | } 152 | virtual void setPixelSizeX(double pixelSizeX) 153 | { 154 | _pixelSizeX = pixelSizeX; 155 | _extentX = std::abs(_rasterSizeX * _pixelSizeX); 156 | } 157 | 158 | virtual void setPixelSizeY(double pixelSizeY) 159 | { 160 | _pixelSizeY = pixelSizeY; 161 | _extentY = std::abs(_rasterSizeY * _pixelSizeY); 162 | } 163 | virtual void setExtentX(double extentX) 164 | { 165 | setRasterSizeX(static_cast(std::ceil(extentX / _pixelSizeX))); 166 | } 167 | virtual void setExtentY(double extentY) 168 | { 169 | setRasterSizeX(static_cast(std::ceil(extentY / _pixelSizeY))); 170 | } 171 | virtual void setReference(const OGRSpatialReference &reference) 172 | { 173 | _reference = reference; 174 | } 175 | 176 | #pragma endregion 177 | 178 | /// 179 | /// Returns the georeferencing transform array (using format of GDAL). 180 | /// 181 | std::array geoTransform() const; 182 | 183 | /// 184 | /// Loads the metadata from the georeferencing transform array (using format of GDAL). 185 | /// 186 | void setGeoTransform(const std::array& geoTransform); 187 | 188 | /// 189 | /// Loads the metadata from the georeferencing transform (using format of GDAL). 190 | /// 191 | void setGeoTransform(const double* geoTransform); 192 | }; 193 | 194 | std::ostream& operator<<(std::ostream& out, const Metadata& metadata); 195 | std::ostream& operator<<(std::ostream& out, const RasterMetadata& metadata); 196 | } // DEM 197 | } // CloudTools 198 | -------------------------------------------------------------------------------- /CloudTools.DEM/Rasterize.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | #include "Metadata.h" 12 | 13 | namespace CloudTools 14 | { 15 | namespace DEM 16 | { 17 | /// 18 | /// Represents a converter of vector layers into a raster filter file. 19 | /// 20 | class Rasterize : public Operation 21 | { 22 | public: 23 | /// 24 | /// Output pixel size alongside axis X. 25 | /// 26 | double pixelSizeX = 1; 27 | /// 28 | /// Output pixel size alongside axis Y. 29 | /// 30 | double pixelSizeY = -1; 31 | 32 | /// 33 | /// Target output format. 34 | /// 35 | /// 36 | /// For supported formats, . 37 | /// 38 | std::string targetFormat = "GTiff"; 39 | 40 | /// 41 | /// The target value that will be burned. 42 | /// 43 | uint8_t targetValue = 255; 44 | 45 | /// 46 | /// The target attribute field on the features to be used for a burn-in value. 47 | /// 48 | /// 49 | /// Overrides when definied. 50 | /// 51 | std::string targetField; 52 | 53 | /// 54 | /// Format specific output creation options. 55 | /// 56 | /// 57 | /// For supported options, . 58 | /// 59 | std::map createOptions; 60 | 61 | /// 62 | /// The spatial reference system for the source and target files. 63 | /// 64 | /// 65 | /// Setting this property will override SRS detection. 66 | /// 67 | std::string spatialReference; 68 | 69 | /// 70 | /// The nodata value. 71 | /// 72 | short nodataValue = 0; 73 | 74 | /// 75 | /// Callback function for reporting progress. 76 | /// 77 | ProgressType progress; 78 | 79 | /// 80 | /// The data type of the target raster. 81 | /// 82 | /// 83 | /// Will be automatically guessed if left default. 84 | /// 85 | GDALDataType targetType = GDALDataType::GDT_Unknown; 86 | 87 | protected: 88 | std::string _sourcePath; 89 | std::string _targetPath; 90 | 91 | GDALDataset* _sourceDataset; 92 | GDALDataset* _targetDataset; 93 | std::vector _layers; 94 | 95 | VectorMetadata _sourceMetadata; 96 | RasterMetadata _targetMetadata; 97 | 98 | bool _isClipped = false; 99 | bool _sourceOwnership; 100 | bool _targetOwnerShip = true; 101 | 102 | public: 103 | /// 104 | /// Initializes a new instance of the class and loads source metadata. 105 | /// 106 | /// 107 | /// Loads input metadata. 108 | /// 109 | /// The vector source path. 110 | /// The raster target path. 111 | /// The layers to be rasterized. If empty, the first layer will be used. 112 | /// The callback method to report progress. 113 | Rasterize(const std::string& sourcePath, const std::string& targetPath, 114 | const std::vector& layers = std::vector(), 115 | ProgressType progress = nullptr); 116 | 117 | /// 118 | /// Initializes a new instance of the class and loads source metadata. 119 | /// 120 | /// 121 | /// Loads input metadata. 122 | /// 123 | /// The vector source dataset. 124 | /// The raster target path. 125 | /// The layers to be rasterized. If empty, the first layer will be used. 126 | /// The callback method to report progress. 127 | Rasterize(GDALDataset* sourceDataset, const std::string& targetPath, 128 | const std::vector& layers = std::vector(), 129 | ProgressType progress = nullptr); 130 | 131 | Rasterize(const Rasterize&) = delete; 132 | Rasterize& operator=(const Rasterize&) = delete; 133 | ~Rasterize(); 134 | 135 | const VectorMetadata& sourceMetadata() const; 136 | const RasterMetadata& targetMetadata() const; 137 | 138 | /// 139 | /// Retrieves the target dataset. 140 | /// 141 | /// 142 | /// By calling this method, the target datatset will be released by the operation and won't be automatically freed. 143 | /// 144 | /// The target dataset. 145 | GDALDataset* target(); 146 | 147 | /// 148 | /// Clips the target raster with a specified window. 149 | /// 150 | /// The origin X coordinate for the clipping. 151 | /// The origin Y coordinate for the clipping. 152 | /// The raster size alongside axis X for the clipping. 153 | /// The raster size alongside axis Y for the clipping. 154 | void clip(double originX, double originY, 155 | int rasterSizeX, int rasterSizeY); 156 | 157 | protected: 158 | /// 159 | /// Calculates the metadata of the output. 160 | /// 161 | void onPrepare() override; 162 | 163 | /// 164 | /// Produces the output file. 165 | /// 166 | void onExecute() override; 167 | 168 | private: 169 | /// 170 | /// Routes the C-style GDAL progress reports to the defined reporter. 171 | /// 172 | static int CPL_STDCALL gdalProgress(double dfComplete, const char *pszMessage, void *pProgressArg); 173 | }; 174 | } // DEM 175 | } // CloudTools 176 | -------------------------------------------------------------------------------- /CloudTools.DEM/Transformation.cpp: -------------------------------------------------------------------------------- 1 | namespace CloudTools 2 | { 3 | namespace DEM 4 | { 5 | 6 | } // DEM 7 | } // CloudTools 8 | -------------------------------------------------------------------------------- /CloudTools.DEM/Transformation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "Calculation.h" 6 | #include "Creation.h" 7 | 8 | namespace CloudTools 9 | { 10 | namespace DEM 11 | { 12 | /// 13 | /// Represents a transformation on DEM datasets. 14 | /// 15 | class Transformation : public Calculation, public Creation 16 | { 17 | public: 18 | /// 19 | /// Initializes a new instance of the class and loads source metadata. 20 | /// 21 | /// The source files of the transformation. 22 | /// The target file of the transformation. 23 | /// The callback method to report progress. 24 | Transformation(const std::vector& sourcePaths, 25 | const std::string& targetPath, 26 | ProgressType progress = nullptr) 27 | : Calculation(sourcePaths, progress), 28 | Creation(targetPath) 29 | { } 30 | 31 | /// 32 | /// Initializes a new instance of the class and loads source metadata. 33 | /// 34 | /// The source datasets of the transformation. 35 | /// The target file of the transformation. 36 | /// The callback method to report progress. 37 | Transformation(const std::vector& sourceDatasets, 38 | const std::string& targetPath, 39 | ProgressType progress = nullptr) 40 | : Calculation(sourceDatasets, progress), 41 | Creation(targetPath) 42 | { } 43 | 44 | Transformation(const Transformation&) = delete; 45 | Transformation& operator=(const Transformation&) = delete; 46 | ~Transformation() { }; 47 | }; 48 | } // DEM 49 | } // CloudTools 50 | -------------------------------------------------------------------------------- /CloudTools.DEM/Window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace CloudTools 6 | { 7 | namespace DEM 8 | { 9 | /// 10 | /// Represents a window to a sub-dataset matrix with continuous representation. 11 | /// 12 | template 13 | struct Window 14 | { 15 | public: 16 | int centerX; 17 | int centerY; 18 | 19 | private: 20 | const DataType* _data; 21 | const DataType _nodataValue; 22 | 23 | const int _sizeX; 24 | const int _sizeY; 25 | const int _offsetX; 26 | const int _offsetY; 27 | 28 | public: 29 | /// 30 | /// Initializes a new instance of the struct. 31 | /// 32 | /// The continuous data. 33 | /// The nodata value. 34 | /// The width of the matrix. 35 | /// The height of the matrix. 36 | /// The abcissa offset of the sub-dataset. 37 | /// The ordinate offset of the sub-dataset. 38 | /// The center abcissa position of inquiry. 39 | /// The center ordinate position of inquiry. 40 | Window(const DataType* data, DataType nodataValue, 41 | int sizeX, int sizeY, 42 | int offsetX, int offsetY, 43 | int centerX, int centerY) 44 | : _data(data), _nodataValue(nodataValue), 45 | _sizeX(sizeX), _sizeY(sizeY), 46 | _offsetX(offsetX), _offsetY(offsetY), 47 | centerX(centerX), centerY(centerY) 48 | { 49 | if (_sizeX < 0 || _sizeY < 0) 50 | throw std::invalid_argument("Negative data dimensions are prohibited."); 51 | } 52 | 53 | /// 54 | /// Determines whether the given center contains a valid data in the sub-dataset. 55 | /// 56 | bool hasData() const 57 | { 58 | return hasData(0, 0); 59 | } 60 | 61 | /// 62 | /// Determines whether the specified position relative to the given center contains a valid data in the sub-dataset. 63 | /// 64 | /// Relative abcissa. 65 | /// Relative ordinate. 66 | bool hasData(int i, int j) const 67 | { 68 | if (!isValid(i, j)) 69 | return false; 70 | return _data[position(i, j)] != _nodataValue; 71 | } 72 | 73 | /// 74 | /// Retrieves the data at the given center in the sub-dataset. 75 | /// 76 | DataType data() const 77 | { 78 | return data(0, 0); 79 | } 80 | 81 | /// 82 | /// Retrieves the data at the specified position relative to the given center in the sub-dataset. 83 | /// 84 | /// Relative abcissa. 85 | /// Relative ordinate. 86 | DataType data(int i, int j) const 87 | { 88 | if (!isValid(i, j)) 89 | return _nodataValue; 90 | return _data[position(i, j)]; 91 | } 92 | 93 | private: 94 | bool isValid(int i, int j) const 95 | { 96 | return centerX + i >= _offsetX && centerX + i < _offsetX + _sizeX && 97 | centerY + j >= _offsetY && centerY + j < _offsetY + _sizeY; 98 | } 99 | 100 | int position(int i, int j) const 101 | { 102 | return (centerY - _offsetY + j) * _sizeX + centerX - _offsetX + i; 103 | } 104 | }; 105 | } // DEM 106 | } // CloudTools 107 | 108 | -------------------------------------------------------------------------------- /CloudTools.Vegetation.Verify/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(../) 2 | 3 | add_executable(vegetation_ver 4 | main.cpp) 5 | target_link_libraries(vegetation_ver 6 | dem common) 7 | 8 | install(TARGETS vegetation_ver 9 | DESTINATION ${CMAKE_INSTALL_PREFIX}) 10 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/BuildingFacadeSeedRemoval.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | namespace CloudTools::DEM 12 | { 13 | /// 14 | /// Represents the removal of false positive seed points close to buildings 15 | /// 16 | template 17 | class BuildingFacadeSeedRemoval : public DatasetCalculation 18 | { 19 | std::vector &seedPoints; 20 | public: 21 | int threshold; 22 | /// 23 | /// Initializes a new instance of the class. Loads input metadata and defines calculation. 24 | /// 25 | /// The source files of the difference comparison. 26 | /// The callback method to report progress. 27 | BuildingFacadeSeedRemoval(std::vector& seedPoints, 28 | const std::vector& sourcePaths, 29 | Operation::ProgressType progress = nullptr, 30 | int threshold = 20) 31 | : DatasetCalculation(sourcePaths, nullptr, progress), 32 | seedPoints(seedPoints), 33 | threshold(threshold) 34 | { 35 | initialize(); 36 | } 37 | 38 | BuildingFacadeSeedRemoval(const BuildingFacadeSeedRemoval&) = delete; 39 | BuildingFacadeSeedRemoval& operator=(const BuildingFacadeSeedRemoval&) = delete; 40 | 41 | private: 42 | /// 43 | /// Initializes the new instance of the class. 44 | /// 45 | void initialize(); 46 | }; 47 | 48 | template 49 | void BuildingFacadeSeedRemoval::initialize() 50 | { 51 | this->computation = [this](int x, int y) 52 | { 53 | std::vector idxs; 54 | int c = 0; 55 | for (auto& point: seedPoints) 56 | { 57 | int counter = 0; 58 | int ws = 3; // window size = ws*2 + 1 59 | int px = point.getX(); 60 | int py = point.getY(); 61 | for(int i = px - ws; i <= px + ws; i++){ 62 | for(int j = py - ws; j <= py + ws; j++){ 63 | if(!this->hasSourceData(0,i,j) && this->sourceData(1,i,j) > 10){ 64 | counter++; 65 | } 66 | } 67 | } 68 | if(counter > threshold){ 69 | idxs.push_back(c); 70 | } 71 | c++; 72 | } 73 | for (int i = idxs.size() - 1; i >= 0; --i) { 74 | seedPoints.erase(seedPoints.begin() + idxs[i]); 75 | } 76 | }; 77 | } 78 | } // CloudTools 79 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(../) 2 | 3 | add_executable(vegetation 4 | main.cpp 5 | NoiseFilter.cpp NoiseFilter.h 6 | TreeCrownSegmentation.cpp TreeCrownSegmentation.h 7 | HausdorffDistance.cpp HausdorffDistance.h 8 | MorphologyClusterFilter.cpp MorphologyClusterFilter.h 9 | CentroidDistance.cpp CentroidDistance.h 10 | InterpolateNoData.cpp InterpolateNoData.h 11 | EliminateNonTrees.cpp EliminateNonTrees.h 12 | VolumeDifference.cpp VolumeDifference.h 13 | DistanceCalculation.h 14 | HeightDifference.h HeightDifference.cpp 15 | PreProcess.cpp PreProcess.h 16 | PostProcess.cpp PostProcess.h 17 | BuildingFacadeSeedRemoval.hpp 18 | RiverMask.hpp) 19 | 20 | target_link_libraries(vegetation 21 | PRIVATE ${PDAL_LIBRARIES} 22 | dem 23 | common 24 | Threads::Threads) 25 | 26 | target_include_directories(vegetation PRIVATE 27 | ${PDAL_INCLUDE_DIRS} 28 | ${PDAL_INCLUDE_DIRS}/pdal) 29 | 30 | install(TARGETS vegetation 31 | DESTINATION ${CMAKE_INSTALL_PREFIX}) 32 | 33 | # ahn_vegetation -> vegetation symlink for backward compatibility 34 | install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ./vegetation ${CMAKE_INSTALL_PREFIX}/ahn_vegetation)") -------------------------------------------------------------------------------- /CloudTools.Vegetation/CentroidDistance.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "CentroidDistance.h" 5 | 6 | namespace CloudTools 7 | { 8 | namespace Vegetation 9 | { 10 | void CentroidDistance::onExecute() 11 | { 12 | progress(0.f, "Performing centroid distance based cluster pairing."); 13 | 14 | bool hasChanged; 15 | do 16 | { 17 | hasChanged = false; 18 | std::multimap> bKey; 19 | 20 | for (const GUInt32 indexA : clusterMapA.clusterIndexes()) 21 | { 22 | double dist = std::numeric_limits::max(); 23 | GUInt32 i = -1; 24 | 25 | if (std::find_if(closestClusters.begin(), 26 | closestClusters.end(), 27 | [&indexA](const std::pair, double>& p) 28 | { 29 | return p.first.first == indexA; 30 | }) 31 | == closestClusters.end()) 32 | { 33 | for (const GUInt32 indexB : clusterMapB.clusterIndexes()) 34 | { 35 | OGRPoint centerA = clusterMapA.center2D(indexA); 36 | OGRPoint centerB = clusterMapB.center2D(indexB); 37 | double newDist = centerA.Distance(¢erB); 38 | if (newDist < dist && 39 | std::find_if(closestClusters.begin(), 40 | closestClusters.end(), 41 | [&indexB](const std::pair, double>& p) 42 | { 43 | return p.first.second == indexB; 44 | }) 45 | == closestClusters.end()) 46 | { 47 | dist = newDist; 48 | i = indexB; 49 | } 50 | } 51 | 52 | if (dist <= maximumDistance) 53 | { 54 | bKey.insert(std::make_pair(i, std::make_pair(indexA, dist))); // clusterMapB index is key 55 | } 56 | } 57 | } 58 | 59 | for (auto it = bKey.begin(), end = bKey.end(); 60 | it != end; it = bKey.upper_bound(it->first)) 61 | { 62 | GUInt32 indexB = it->first; 63 | std::pair> minPair = *it; 64 | if (bKey.count(indexB) > 1) 65 | { 66 | auto iter = it; 67 | while (iter != end && iter->first == it->first) 68 | { 69 | if (minPair.second.second > iter->second.second) 70 | minPair = *iter; 71 | iter++; 72 | } 73 | hasChanged = true; 74 | } 75 | closestClusters.insert(std::make_pair(std::make_pair(minPair.second.first, minPair.first), 76 | minPair.second.second)); 77 | } 78 | } 79 | while (hasChanged); 80 | 81 | // TODO: add intermediate reporting into above loop 82 | if (progress) 83 | progress(0.8f, "Cluster map pairs calculated."); 84 | 85 | for (GUInt32 index : clusterMapA.clusterIndexes()) 86 | if (std::find_if(closestClusters.begin(), closestClusters.end(), 87 | [&index](const std::pair, double>& item) 88 | { 89 | return item.first.first == index; 90 | }) == closestClusters.end()) 91 | lonelyClustersA.push_back(index); 92 | 93 | if (progress) 94 | progress(0.9f, "Lonely Epoch-A clusters calculated."); 95 | 96 | for (GUInt32 index : clusterMapB.clusterIndexes()) 97 | if (std::find_if(closestClusters.begin(), closestClusters.end(), 98 | [&index](const std::pair, double>& item) 99 | { 100 | return item.first.second == index; 101 | }) == closestClusters.end()) 102 | lonelyClustersB.push_back(index); 103 | 104 | if (progress) 105 | progress(1.f, "Lonely Epoch-B clusters calculated."); 106 | } 107 | } // Vegetation 108 | } // CloudTools 109 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/CentroidDistance.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "DistanceCalculation.h" 9 | 10 | namespace CloudTools 11 | { 12 | namespace Vegetation 13 | { 14 | class CentroidDistance : public DistanceCalculation 15 | { 16 | public: 17 | CentroidDistance(CloudTools::DEM::ClusterMap& clusterMapA, 18 | CloudTools::DEM::ClusterMap& clusterMapB, 19 | double maximumDistance = 10.0, // in units of resolution (e.g. with 0.5m resolution it is 5 meters) 20 | Operation::ProgressType progress = nullptr) 21 | : DistanceCalculation(clusterMapA, clusterMapB, maximumDistance, progress) 22 | { 23 | } 24 | 25 | private: 26 | void onExecute() override; 27 | }; 28 | } // Vegetation 29 | } // CloudTools 30 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/DistanceCalculation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace CloudTools 9 | { 10 | namespace Vegetation 11 | { 12 | class DistanceCalculation : public CloudTools::Operation 13 | { 14 | public: 15 | double maximumDistance; 16 | CloudTools::DEM::ClusterMap clusterMapA; 17 | CloudTools::DEM::ClusterMap clusterMapB; 18 | 19 | /// 20 | /// Callback function for reporting progress. 21 | /// 22 | ProgressType progress; 23 | 24 | DistanceCalculation(const CloudTools::DEM::ClusterMap& clusterMapA, 25 | const CloudTools::DEM::ClusterMap& clusterMapB, 26 | double maximumDistance = 10.0, // in units of resolution (e.g. with 0.5m resolution it is 5 meters) 27 | Operation::ProgressType progress = nullptr) 28 | : clusterMapA(clusterMapA), clusterMapB(clusterMapB), maximumDistance(maximumDistance), 29 | progress(progress) 30 | { 31 | } 32 | 33 | const std::map, double>& closest() const 34 | { 35 | return closestClusters; 36 | } 37 | 38 | const std::vector& lonelyA() const 39 | { 40 | return lonelyClustersA; 41 | } 42 | 43 | const std::vector& lonelyB() const 44 | { 45 | return lonelyClustersB; 46 | } 47 | 48 | void onPrepare() override 49 | {} 50 | 51 | protected: 52 | std::map, double> closestClusters; 53 | std::vector lonelyClustersA; 54 | std::vector lonelyClustersB; 55 | }; 56 | } // Vegetation 57 | } // CloudTools 58 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/EliminateNonTrees.cpp: -------------------------------------------------------------------------------- 1 | #include "EliminateNonTrees.h" 2 | 3 | namespace CloudTools 4 | { 5 | namespace Vegetation 6 | { 7 | void EliminateNonTrees::initialize() 8 | { 9 | this->computation = [this](int x, int y, const std::vector>& sources) 10 | { 11 | const Window& source = sources[0]; 12 | 13 | if (!source.hasData() || source.data() < this->threshold) 14 | return static_cast(this->nodataValue); 15 | else 16 | return source.data(); 17 | }; 18 | } 19 | } // Vegetation 20 | } // CloudTools 21 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/EliminateNonTrees.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | using namespace CloudTools::DEM; 7 | 8 | namespace CloudTools 9 | { 10 | namespace Vegetation 11 | { 12 | class EliminateNonTrees : public CloudTools::DEM::SweepLineTransformation 13 | { 14 | public: 15 | float threshold; 16 | 17 | /// 18 | /// Initializes a new instance of the class. Loads input metadata and defines calculation. 19 | /// 20 | /// The source files of the difference comparison. 21 | /// The target file of the difference comparison. 22 | /// The callback method to report progress. 23 | EliminateNonTrees(const std::vector& sourcePaths, 24 | const std::string& targetPath, 25 | CloudTools::Operation::ProgressType progress = nullptr, 26 | float threshold = 1.5) 27 | : CloudTools::DEM::SweepLineTransformation(sourcePaths, targetPath, nullptr, progress), 28 | threshold(threshold) 29 | { 30 | initialize(); 31 | } 32 | 33 | /// 34 | /// Initializes a new instance of the class. Loads input metadata and defines calculation. 35 | /// 36 | /// The source datasets of the difference comparison. 37 | /// The target file of the difference comparison. 38 | /// The callback method to report progress. 39 | EliminateNonTrees(const std::vector& sourceDatasets, 40 | const std::string& targetPath, 41 | CloudTools::Operation::ProgressType progress = nullptr, 42 | float threshold = 1.5) 43 | : CloudTools::DEM::SweepLineTransformation(sourceDatasets, targetPath, 0, nullptr, progress), 44 | threshold(threshold) 45 | { 46 | initialize(); 47 | } 48 | 49 | EliminateNonTrees(const EliminateNonTrees&) = delete; 50 | 51 | EliminateNonTrees& operator=(const EliminateNonTrees&) = delete; 52 | 53 | private: 54 | void initialize(); 55 | }; 56 | } // Vegetation 57 | } // CloudTools 58 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/HausdorffDistance.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "HausdorffDistance.h" 4 | 5 | namespace CloudTools 6 | { 7 | namespace Vegetation 8 | { 9 | void HausdorffDistance::onExecute() 10 | { 11 | progress(0.f, "Performing Hausdorff-distance based cluster pairing."); 12 | 13 | randomizePoints(); 14 | 15 | // A -> B 16 | for (const GUInt32 indexA : clusterMapA.clusterIndexes()) 17 | { 18 | for (const GUInt32 indexB : clusterMapB.clusterIndexes()) 19 | { 20 | auto centerB = clusterMapB.center2D(indexB); 21 | auto centerA = clusterMapA.center2D(indexA); 22 | double distance = centerA.Distance(¢erB); 23 | if (distance >= maximumDistance) 24 | continue; 25 | 26 | double cmax = 0; 27 | for (const OGRPoint& pointA : clusterMapA.points(indexA)) 28 | { 29 | double cmin = std::numeric_limits::max(); 30 | for (const OGRPoint& pointB : clusterMapB.points(indexB)) 31 | { 32 | double dist = pointA.Distance(&pointB); 33 | if (dist < cmax) 34 | { 35 | cmin = 0; 36 | break; 37 | } 38 | cmin = std::min(dist, cmin); 39 | } 40 | cmax = std::max(cmin, cmax); 41 | } 42 | hausdorffDistancesA.emplace(std::make_pair(std::make_pair(indexA, indexB), cmax)); 43 | } 44 | } 45 | 46 | // TODO: add intermediate reporting into above loop 47 | if (progress) 48 | progress(0.35f, "Epoch-A to B distances calculated."); 49 | 50 | // B -> A 51 | for (const GUInt32 indexB : clusterMapB.clusterIndexes()) 52 | { 53 | for (const GUInt32 indexA : clusterMapA.clusterIndexes()) 54 | { 55 | auto centerB = clusterMapB.center2D(indexB); 56 | auto centerA = clusterMapA.center2D(indexA); 57 | double distance = centerA.Distance(¢erB); 58 | if (distance >= maximumDistance) 59 | continue; 60 | 61 | double cmax = 0; 62 | for (const OGRPoint& pointB : clusterMapB.points(indexB)) 63 | { 64 | double cmin = std::numeric_limits::max(); 65 | for (const OGRPoint& pointA : clusterMapA.points(indexA)) 66 | { 67 | double dist = pointB.Distance(&pointA); 68 | if (dist < cmax) 69 | { 70 | cmin = 0; 71 | break; 72 | } 73 | cmin = std::min(dist, cmin); 74 | } 75 | cmax = std::max(cmin, cmax); 76 | } 77 | hausdorffDistancesB.emplace(std::make_pair(std::make_pair(indexB, indexA), cmax)); 78 | } 79 | } 80 | 81 | // TODO: add intermediate reporting into above loop 82 | if (progress) 83 | progress(0.7f, "Epoch-B to A distances calculated."); 84 | 85 | bool hasConflict; 86 | do 87 | { 88 | hasConflict = false; 89 | std::multimap> bKey; 90 | 91 | for (const GUInt32 indexA : clusterMapA.clusterIndexes()) 92 | { 93 | double dist = std::numeric_limits::max(); 94 | GUInt32 i = -1; 95 | 96 | if (std::find_if(closestClusters.begin(), 97 | closestClusters.end(), 98 | [&indexA](const std::pair, double>& p) 99 | { 100 | return p.first.first == indexA; 101 | }) 102 | == closestClusters.end()) 103 | { 104 | for (const GUInt32 indexB : clusterMapB.clusterIndexes()) 105 | { 106 | auto resA = hausdorffDistancesA.find({indexA, indexB}); 107 | auto resB = hausdorffDistancesB.find({indexB, indexA}); 108 | 109 | if (resA != hausdorffDistancesA.end() 110 | && resB != hausdorffDistancesB.end()) 111 | { 112 | double newDist = std::max(resA->second, resB->second); 113 | 114 | if (newDist < dist && 115 | std::find_if(closestClusters.begin(), 116 | closestClusters.end(), 117 | [&indexB](const std::pair, double>& p) 118 | { 119 | return p.first.second == indexB; 120 | }) 121 | == closestClusters.end()) 122 | { 123 | dist = newDist; 124 | i = indexB; 125 | } 126 | } 127 | } 128 | 129 | if (dist <= maximumDistance) 130 | { 131 | bKey.insert(std::make_pair(i, std::make_pair(indexA, dist))); // clusterMapB index is key 132 | } 133 | } 134 | } 135 | 136 | for (auto it = bKey.begin(), end = bKey.end(); 137 | it != end; it = bKey.upper_bound(it->first)) 138 | { 139 | GUInt32 indexB = it->first; 140 | std::pair> minPair = *it; 141 | if (bKey.count(indexB) > 1) 142 | { 143 | auto iter = it; 144 | while (iter != end && iter->first == it->first) 145 | { 146 | if (minPair.second.second > iter->second.second) 147 | minPair = *iter; 148 | iter++; 149 | } 150 | hasConflict = true; 151 | } 152 | closestClusters.insert(std::make_pair(std::make_pair(minPair.second.first, minPair.first), 153 | minPair.second.second)); 154 | } 155 | } 156 | while (hasConflict); 157 | 158 | if (progress) 159 | progress(0.8f, "Cluster map pairs calculated."); 160 | 161 | for (GUInt32 indexA : clusterMapA.clusterIndexes()) 162 | if (std::find_if(closestClusters.begin(), closestClusters.end(), 163 | [&indexA](const std::pair, double>& item) 164 | { 165 | return item.first.first == indexA; 166 | }) == closestClusters.end()) 167 | lonelyClustersA.push_back(indexA); 168 | 169 | if (progress) 170 | progress(0.9f, "Lonely Epoch-A clusters calculated."); 171 | 172 | for (GUInt32 indexB : clusterMapB.clusterIndexes()) 173 | if (std::find_if(closestClusters.begin(), closestClusters.end(), 174 | [&indexB](const std::pair, double>& item) 175 | { 176 | return item.first.second == indexB; 177 | }) == closestClusters.end()) 178 | lonelyClustersB.push_back(indexB); 179 | 180 | if (progress) 181 | progress(1.f, "Lonely Epoch-B clusters calculated."); 182 | } 183 | 184 | GUInt32 HausdorffDistance::closestCluster(GUInt32 index) 185 | { 186 | GUInt32 closest = index; 187 | std::map distances; 188 | for (auto elem : hausdorffDistancesA) 189 | if (elem.first.first == index) 190 | distances.emplace(std::make_pair(elem.second, elem.first.second)); 191 | 192 | closest = distances.begin()->second; 193 | return closest; 194 | } 195 | 196 | std::map, double> HausdorffDistance::distances() const 197 | { 198 | return hausdorffDistancesA; 199 | } 200 | 201 | 202 | void HausdorffDistance::randomizePoints() 203 | { 204 | clusterMapA.shuffle(); 205 | clusterMapB.shuffle(); 206 | } 207 | } // Vegetation 208 | } // CloudTools -------------------------------------------------------------------------------- /CloudTools.Vegetation/HausdorffDistance.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "DistanceCalculation.h" 10 | 11 | namespace CloudTools 12 | { 13 | namespace Vegetation 14 | { 15 | class HausdorffDistance : public DistanceCalculation 16 | { 17 | public: 18 | HausdorffDistance(CloudTools::DEM::ClusterMap& clusterMapA, 19 | CloudTools::DEM::ClusterMap& clusterMapB, 20 | double maximumDistance = 16.0, // in units of resolution (e.g. with 0.5m resolution it is 8 meters) 21 | Operation::ProgressType progress = nullptr) 22 | : DistanceCalculation(clusterMapA, clusterMapB, maximumDistance, progress) 23 | { 24 | } 25 | 26 | double clusterDistance(GUInt32, GUInt32); 27 | 28 | GUInt32 closestCluster(GUInt32); 29 | 30 | std::map, double> distances() const; 31 | 32 | private: 33 | std::map, double> hausdorffDistancesA; 34 | std::map, double> hausdorffDistancesB; 35 | 36 | void onExecute() override; 37 | void randomizePoints(); 38 | }; 39 | } // Vegetation 40 | } // CloudTools 41 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/HeightDifference.cpp: -------------------------------------------------------------------------------- 1 | #include "HeightDifference.h" 2 | 3 | namespace CloudTools 4 | { 5 | namespace Vegetation 6 | { 7 | void HeightDifference::calculateDifference() 8 | { 9 | std::map, double> differences; 10 | OGRPoint highestA, highestB; 11 | double diff; 12 | for (const auto& elem : distance->closest()) 13 | { 14 | highestA = clusterMapA.highestPoint(elem.first.first); 15 | highestB = clusterMapB.highestPoint(elem.first.second); 16 | 17 | diff = highestB.getZ() - highestA.getZ(); 18 | differences.insert(std::make_pair(elem.first, diff)); 19 | } 20 | } 21 | } // Vegetation 22 | } // CloudTools -------------------------------------------------------------------------------- /CloudTools.Vegetation/HeightDifference.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "DistanceCalculation.h" 6 | 7 | using namespace CloudTools::DEM; 8 | 9 | namespace CloudTools 10 | { 11 | namespace Vegetation 12 | { 13 | class HeightDifference 14 | { 15 | public: 16 | ClusterMap clusterMapA, clusterMapB; 17 | DistanceCalculation* distance; 18 | 19 | HeightDifference(ClusterMap& clusterMapA, 20 | ClusterMap& clusterMapB, 21 | DistanceCalculation* distance) 22 | : clusterMapA(clusterMapA), clusterMapB(clusterMapB), distance(distance) 23 | { 24 | calculateDifference(); 25 | } 26 | 27 | private: 28 | void calculateDifference(); 29 | }; 30 | } // Vegetation 31 | } // CloudTools 32 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/InterpolateNoData.cpp: -------------------------------------------------------------------------------- 1 | #include "InterpolateNoData.h" 2 | 3 | namespace CloudTools 4 | { 5 | namespace Vegetation 6 | { 7 | void InterpolateNoData::initialize() 8 | { 9 | this->computation = [this](int x, int y, const std::vector>& sources) 10 | { 11 | const CloudTools::DEM::Window& source = sources[0]; 12 | if (source.hasData()) 13 | return source.data(); 14 | 15 | int counter = 0; 16 | float data = 0; 17 | 18 | for (int i = -this->range(); i <= this->range(); i++) 19 | for (int j = -this->range(); j <= this->range(); j++) 20 | if (source.hasData(i, j)) 21 | { 22 | counter++; 23 | data += source.data(i, j); 24 | } 25 | 26 | if (this->threshold > 1.0 || this->threshold < 0.0) 27 | this->threshold = 0.5; 28 | 29 | if (counter < ((std::pow((this->range() * 2 + 1), 2.0) - 1) * this->threshold)) 30 | { 31 | return static_cast(this->nodataValue); 32 | } 33 | 34 | return static_cast(data / counter); 35 | }; 36 | } 37 | } // Vegetation 38 | } // CloudTools 39 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/InterpolateNoData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace CloudTools 7 | { 8 | namespace Vegetation 9 | { 10 | class InterpolateNoData : public CloudTools::DEM::SweepLineTransformation 11 | { 12 | public: 13 | float threshold = 0.5; 14 | 15 | /// 16 | /// Initializes a new instance of the class. Loads input metadata and defines calculation. 17 | /// 18 | /// The source files of the difference comparison. 19 | /// The target file of the difference comparison. 20 | /// The callback method to report progress. 21 | InterpolateNoData(const std::vector& sourcePaths, 22 | const std::string& targetPath, 23 | CloudTools::Operation::ProgressType progress = nullptr, 24 | float ratio = 0.5) 25 | : CloudTools::DEM::SweepLineTransformation(sourcePaths, targetPath, 1, nullptr, progress), 26 | threshold(ratio) 27 | { 28 | initialize(); 29 | } 30 | 31 | /// 32 | /// Initializes a new instance of the class. Loads input metadata and defines calculation. 33 | /// 34 | /// The source datasets of the difference comparison. 35 | /// The target file of the difference comparison. 36 | /// The callback method to report progress. 37 | InterpolateNoData(const std::vector& sourceDatasets, 38 | const std::string& targetPath, 39 | CloudTools::Operation::ProgressType progress = nullptr, 40 | float ratio = 0.5) 41 | : CloudTools::DEM::SweepLineTransformation(sourceDatasets, targetPath, 1, nullptr, progress), 42 | threshold(ratio) 43 | { 44 | initialize(); 45 | } 46 | 47 | InterpolateNoData(const InterpolateNoData&) = delete; 48 | 49 | InterpolateNoData& operator=(const InterpolateNoData&) = delete; 50 | 51 | private: 52 | void initialize(); 53 | }; 54 | } // Vegetation 55 | } // CloudTools 56 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/MorphologyClusterFilter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "MorphologyClusterFilter.h" 4 | 5 | namespace CloudTools 6 | { 7 | namespace Vegetation 8 | { 9 | void MorphologyClusterFilter::initialize() 10 | { 11 | this->computation = [this](int sizeX, int sizeY) 12 | { 13 | if (this->method == Method::Dilation && this->threshold == -1) 14 | this->threshold = 0; 15 | if (this->method == Method::Erosion && this->threshold == -1) 16 | this->threshold = 9; 17 | 18 | int counter; 19 | if (this->method == Method::Erosion) 20 | { 21 | std::vector pointSet; 22 | for (GUInt32 index : _clusterMap.clusterIndexes()) 23 | { 24 | pointSet.clear(); 25 | for (const OGRPoint& p : _clusterMap.points(index)) 26 | { 27 | counter = 0; 28 | for (int i = p.getX() - 1; i <= p.getX() + 1; i++) 29 | for (int j = p.getY() - 1; j <= p.getY() + 1; j++) 30 | { 31 | OGRPoint point(i, j); 32 | if (std::find_if(_clusterMap.points(index).begin(), 33 | _clusterMap.points(index).end(), 34 | [&point](const OGRPoint& p) 35 | { 36 | return point.getX() == p.getX() && 37 | point.getY() == p.getY(); 38 | }) 39 | != _clusterMap.points(index).end() 40 | && _clusterMap.clusterIndex(i, j) == _clusterMap.clusterIndex(p.getX(), p.getY())) 41 | { 42 | ++counter; 43 | } 44 | } 45 | if (counter < this->threshold) 46 | pointSet.emplace_back(p); 47 | } 48 | 49 | for (auto p : pointSet) 50 | _clusterMap.removePoint(index, p.getX(), p.getY()); 51 | } 52 | } 53 | 54 | if (this->method == Method::Dilation) 55 | { 56 | std::vector pointSet; 57 | for (GUInt32 index : _clusterMap.clusterIndexes()) 58 | { 59 | pointSet.clear(); 60 | for (OGRPoint& p : _clusterMap.neighbors(index)) 61 | { 62 | if (!hasSourceData(p.getX(), p.getY())) 63 | continue; 64 | 65 | counter = 0; 66 | for (int i = p.getX() - 1; i <= p.getX() + 1; i++) 67 | for (int j = p.getY() - 1; j <= p.getY() + 1; j++) 68 | { 69 | OGRPoint point(i, j); 70 | if (std::find_if(_clusterMap.points(index).begin(), 71 | _clusterMap.points(index).end(), 72 | [&point](const OGRPoint& p) 73 | { 74 | return point.getX() == p.getX() && 75 | point.getY() == p.getY(); 76 | }) 77 | != _clusterMap.points(index).end() 78 | && _clusterMap.clusterIndex(i, j) == index) 79 | ++counter; 80 | } 81 | if (counter > this->threshold) 82 | pointSet.emplace_back(p); 83 | } 84 | for (auto& p : pointSet) 85 | _clusterMap.addPoint(index, p.getX(), p.getY(), sourceData(p.getX(), p.getY())); 86 | } 87 | } 88 | }; 89 | } 90 | 91 | CloudTools::DEM::ClusterMap& MorphologyClusterFilter::target() 92 | { 93 | return this->_clusterMap; 94 | } 95 | } // Vegetation 96 | } // CloudTools 97 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/MorphologyClusterFilter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace CloudTools 8 | { 9 | namespace Vegetation 10 | { 11 | class MorphologyClusterFilter : public CloudTools::DEM::DatasetCalculation 12 | { 13 | public: 14 | enum Method 15 | { 16 | Dilation, 17 | Erosion 18 | }; 19 | 20 | /// 21 | /// The applied morphology method. 22 | /// 23 | Method method; 24 | 25 | /// 26 | /// Threshold value for morphology filter. 27 | /// 28 | /// Default value is -1, which will be 0 for dilation and 9 for erosion. 29 | /// 30 | int threshold = -1; 31 | 32 | private: 33 | CloudTools::DEM::ClusterMap _clusterMap; 34 | 35 | public: 36 | /// 37 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 38 | /// 39 | /// The cluster map to apply the morphological filter on. 40 | /// The source raster datasets storing the height values for the points. 41 | /// The method (DILATION or EROSION) to apply. 42 | /// The callback method to report progress. 43 | /// 44 | MorphologyClusterFilter(CloudTools::DEM::ClusterMap& source, 45 | const std::vector& sourceDatasets, 46 | Method method = Method::Dilation, 47 | ProgressType progress = nullptr) 48 | : DatasetCalculation(sourceDatasets, nullptr, progress), 49 | _clusterMap(source), method(method) 50 | { 51 | initialize(); 52 | } 53 | 54 | MorphologyClusterFilter(const MorphologyClusterFilter&) = delete; 55 | 56 | MorphologyClusterFilter& operator=(const MorphologyClusterFilter&) = delete; 57 | 58 | CloudTools::DEM::ClusterMap& target(); 59 | 60 | private: 61 | void initialize(); 62 | }; 63 | } // Vegetation 64 | } // CloudTools -------------------------------------------------------------------------------- /CloudTools.Vegetation/NoiseFilter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "NoiseFilter.h" 7 | 8 | using namespace CloudTools::DEM; 9 | 10 | namespace CloudTools 11 | { 12 | namespace Vegetation 13 | { 14 | NoiseFilter::NoiseFilter(GDALDataset* sourceDataset, 15 | const std::string& targetPath, 16 | int range, 17 | ProgressType progress) 18 | : SweepLineTransformation({sourceDataset}, targetPath, range, nullptr, progress) 19 | { 20 | // Noise is the average percentage of difference compared to the surrounding area. 21 | this->computation = [this](int x, int y, const std::vector>& sources) 22 | { 23 | const Window& source = sources[0]; 24 | if (!source.hasData()) return static_cast(this->nodataValue); 25 | 26 | float noise = 0; 27 | int counter = -1; 28 | for (int i = -this->range(); i <= this->range(); ++i) 29 | for (int j = -this->range(); j <= this->range(); ++j) 30 | if (source.hasData(i, j)) 31 | { 32 | noise += std::abs(source.data() - source.data(i, j)) 33 | / std::min(std::abs(source.data()), std::abs(source.data(i, j))); 34 | ++counter; 35 | } 36 | 37 | if (counter == 0) 38 | return source.data(); 39 | if (noise / counter >= this->threshold) 40 | return source.data(); 41 | else 42 | return static_cast(this->nodataValue); 43 | }; 44 | this->nodataValue = 0; 45 | } 46 | } // Vegetation 47 | } // CloudTools 48 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/NoiseFilter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace CloudTools 8 | { 9 | namespace Vegetation 10 | { 11 | /// 12 | /// Represents a noise filter for DEM datasets. 13 | /// 14 | class NoiseFilter : public CloudTools::DEM::SweepLineTransformation 15 | { 16 | public: 17 | /// 18 | /// The threshold of noise in percentage (between values 0 and 1). 19 | /// 20 | /// 21 | /// Noise is the average percentage of difference compared to the surrounding area. 22 | /// 23 | double threshold = 0.5; 24 | 25 | public: 26 | /// 27 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 28 | /// 29 | /// The source dataset of the filter. 30 | /// The target file of the filter. 31 | /// The range of surrounding data to involve. 32 | /// The callback method to report progress. 33 | NoiseFilter(GDALDataset* sourceDataset, 34 | const std::string& targetPath, 35 | int range, 36 | ProgressType progress = nullptr); 37 | 38 | NoiseFilter(const NoiseFilter&) = delete; 39 | 40 | NoiseFilter& operator=(const NoiseFilter&) = delete; 41 | }; 42 | } // Vegetation 43 | } // CloudTools 44 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/PostProcess.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "DistanceCalculation.h" 7 | 8 | namespace CloudTools 9 | { 10 | namespace Vegetation 11 | { 12 | class PostProcess : public CloudTools::Operation 13 | { 14 | public: 15 | enum DifferenceMethod 16 | { 17 | Hausdorff, 18 | Centroid 19 | }; 20 | 21 | /// 22 | /// Callback function for reporting progress. 23 | /// 24 | ProgressType progress; 25 | 26 | protected: 27 | /// 28 | /// Internal progress reporter piped to override message. 29 | /// 30 | ProgressType _progress; 31 | /// 32 | /// The current progress message to report. 33 | /// 34 | std::string _progressMessage; 35 | 36 | public: 37 | PostProcess(const std::string& dsmInputPathA, const std::string& dsmInputPathB, 38 | const CloudTools::DEM::ClusterMap& clusterMapA, const CloudTools::DEM::ClusterMap& clusterMapB, 39 | const std::string& outputDir, 40 | DifferenceMethod method = DifferenceMethod::Centroid) 41 | : _dsmInputPathA(dsmInputPathA), _dsmInputPathB(dsmInputPathB), 42 | _clustersA(clusterMapA), _clustersB(clusterMapB), 43 | _outputDir(outputDir), 44 | _method(method) 45 | { 46 | } 47 | 48 | protected: 49 | /// 50 | /// Verifies the configuration. 51 | /// 52 | void onPrepare() override; 53 | 54 | /// 55 | /// Produces the output file(s). 56 | /// 57 | void onExecute() override; 58 | 59 | private: 60 | std::string _dsmInputPathA, _dsmInputPathB; 61 | CloudTools::DEM::ClusterMap _clustersA, _clustersB; 62 | std::string _outputDir; 63 | DifferenceMethod _method; 64 | CloudTools::DEM::RasterMetadata _rasterMetadata; 65 | 66 | void writeClusterPairsToFile(const std::string& outPath, std::shared_ptr distance); 67 | 68 | void writeClusterHeightsToFile(const std::string& outPath, std::shared_ptr distance); 69 | }; 70 | } // Vegetation 71 | } // CloudTools 72 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/PreProcess.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace CloudTools 9 | { 10 | namespace Vegetation 11 | { 12 | class PreProcess : public CloudTools::Operation, protected CloudTools::IO::ResultCollection 13 | { 14 | public: 15 | enum ProcessingMethod 16 | { 17 | Standard, 18 | SeedRemoval 19 | }; 20 | /// 21 | /// Callback function for reporting progress. 22 | /// 23 | ProgressType progress; 24 | 25 | /// 26 | /// Iteration steps of morphology operations. 27 | /// 28 | unsigned int morphologyCounter = 3; 29 | 30 | /// 31 | /// Threshold value for morphological erosion. 32 | /// 33 | unsigned int erosionThreshold = 6; 34 | 35 | /// 36 | /// Remove clusters smaller than this threshold. 37 | /// 38 | unsigned int removalRadius = 16; 39 | 40 | /// 41 | /// Keep intermediate results on disk after progress. 42 | /// 43 | bool debug = false; 44 | 45 | protected: 46 | /// 47 | /// Internal progress reporter piped to override message. 48 | /// 49 | ProgressType _progress; 50 | /// 51 | /// The current progress message to report. 52 | /// 53 | std::string _progressMessage; 54 | 55 | public: 56 | PreProcess(const std::string& prefix, 57 | const std::string& dtmInputPath, 58 | const std::string& dsmInputPath, 59 | const std::string& outputDir, 60 | const ProcessingMethod processingMethod = ProcessingMethod::Standard) 61 | : _prefix(prefix), 62 | _dtmInputPath(dtmInputPath), 63 | _dsmInputPath(dsmInputPath), 64 | _outputDir(outputDir), 65 | _processingMethod(processingMethod) 66 | { 67 | } 68 | 69 | inline CloudTools::DEM::ClusterMap& target() 70 | { 71 | if (!isExecuted()) 72 | throw std::logic_error("The operation is not executed."); 73 | return _targetCluster; 74 | } 75 | 76 | inline CloudTools::DEM::RasterMetadata targetMetadata() 77 | { 78 | if (!isExecuted()) 79 | throw std::logic_error("The operation is not executed."); 80 | return _targetMetadata; 81 | } 82 | 83 | protected: 84 | /// 85 | /// Verifies the configuration. 86 | /// 87 | void onPrepare() override; 88 | 89 | /// 90 | /// Produces the output file(s). 91 | /// 92 | void onExecute() override; 93 | 94 | /// 95 | /// Creates a new result object. 96 | /// 97 | /// The name of the result. 98 | /// true if the result is final, otherwise false. 99 | /// New result on the heap. 100 | CloudTools::IO::Result* createResult(const std::string& name, bool isFinal = false) override; 101 | 102 | private: 103 | ProcessingMethod _processingMethod; 104 | 105 | std::string _prefix, _dtmInputPath, _dsmInputPath, _outputDir; 106 | CloudTools::DEM::RasterMetadata _targetMetadata; 107 | CloudTools::DEM::ClusterMap _targetCluster; 108 | 109 | /// 110 | /// Applies blurring convolution. 3x3 Gaussian kernel. 111 | /// 112 | /// 113 | /// Kernel matrix: 114 | /// [ 1 2 1 115 | /// 2 4 2 116 | /// 1 2 1 ] 117 | /// 118 | /// The source dataset of the matrix transformation. 119 | /// The target file of the matrix transformation. 120 | GDALDataset* blur3x3Middle4(GDALDataset* sourceDataset, const std::string& targetPath); 121 | 122 | /// 123 | /// Applies blurring convolution. 3x3 Gaussian kernel. 124 | /// 125 | /// 126 | /// Kernel matrix: 127 | /// [ 1 3 1 128 | /// 3 12 3 129 | /// 1 3 1 ] 130 | /// 131 | /// The source dataset of the matrix transformation. 132 | /// The target file of the matrix transformation. 133 | GDALDataset* blur3x3Middle12(GDALDataset* sourceDataset, const std::string& targetPath); 134 | 135 | /// 136 | /// Applies blurring convolution. 5x5 Gaussian kernel. 137 | /// 138 | /// 139 | /// Kernel matrix: 140 | /// [ 1 4 6 4 1 141 | /// 4 16 24 16 4 142 | /// 6 24 36 24 6 143 | /// 4 16 24 16 4 144 | /// 1 4 6 4 1 ] 145 | /// 146 | /// The source dataset of the matrix transformation. 147 | /// The target file of the matrix transformation. 148 | GDALDataset* blur5x5Middle36(GDALDataset* sourceDataset, const std::string& targetPath); 149 | 150 | std::vector collectSeedPoints(GDALDataset* target); 151 | 152 | /// 153 | /// Removed deformed clusters from the cluster map. 154 | /// 155 | /// 156 | /// A cluster is deemed deformed (as a tree), if one the following 3 conditions apply: 157 | /// - its X dimension is less than half of its Y dimension; 158 | /// - its Y dimension is less than half of its X dimension; 159 | /// - less than 60% of its bounding box belongs to the cluster. 160 | /// 161 | /// The cluster map to transform. 162 | void removeDeformedClusters(CloudTools::DEM::ClusterMap& clusterMap); 163 | 164 | void writePointsToFile(std::vector points, const std::string& outPath); 165 | 166 | void writeClusterMapToFile(const std::string& outPath); 167 | }; 168 | } // Vegetation 169 | } // CloudTools 170 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/RiverMask.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | namespace CloudTools 11 | { 12 | namespace DEM 13 | { 14 | /// 15 | /// Represents a DTM in which rivers are not nodata points, but instead have a constant value of -0.4 16 | /// 17 | template 18 | class RiverMask : public SweepLineTransformation 19 | { 20 | public: 21 | RiverMask(const std::vector& sourcePaths, 22 | const std::string& targetPath, 23 | Operation::ProgressType progress = nullptr) 24 | : SweepLineTransformation(sourcePaths, targetPath, nullptr, progress) 25 | { 26 | initialize(); 27 | } 28 | 29 | RiverMask(const RiverMask&) = delete; 30 | RiverMask& operator=(const RiverMask&) = delete; 31 | 32 | private: 33 | /// 34 | /// Initializes the new instance of the class. 35 | /// 36 | void initialize(); 37 | /// 38 | /// Extremal low value for rivers. 39 | /// 40 | const int riverHeight = -1000; 41 | }; 42 | 43 | template 44 | void RiverMask::initialize() 45 | { 46 | /// returns the DTM value, if both the DTM and the DSM value is given; 47 | /// returns -0.4 if only the DTM value or neither the DTM and the DSM value is given; 48 | /// return the DSM value, if only the DSM value is given. 49 | this->computation = [this](int x, int y, const std::vector>& sources) 50 | { 51 | if (!sources[0].hasData() && sources[1].hasData()) 52 | return static_cast(this->nodataValue); 53 | if (!sources[1].hasData()) 54 | return static_cast(riverHeight); 55 | if (sources[0].hasData()) 56 | return static_cast(sources[0].data()); 57 | }; 58 | } 59 | } // DEM 60 | } // CloudTools 61 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/TreeCrownSegmentation.cpp: -------------------------------------------------------------------------------- 1 | #include "TreeCrownSegmentation.h" 2 | 3 | using namespace CloudTools; 4 | using namespace CloudTools::DEM; 5 | 6 | namespace CloudTools 7 | { 8 | namespace Vegetation 9 | { 10 | void TreeCrownSegmentation::initialize() 11 | { 12 | this->computation = [this](int sizeX, int sizeY) 13 | { 14 | clusters.setSizeX(sizeX); 15 | clusters.setSizeY(sizeY); 16 | 17 | // Create initial clusters from seed points 18 | for (const auto& point : this->seedPoints) 19 | { 20 | clusters.createCluster(point.getX(), point.getY(), point.getZ()); 21 | } 22 | 23 | bool hasChanged; 24 | double currentVerticalDistance = initialVerticalDistance; 25 | do 26 | { 27 | std::map> expandPoints; 28 | for (GUInt32 index : clusters.clusterIndexes()) 29 | expandPoints.insert(std::make_pair(index, expandCluster(index, currentVerticalDistance))); 30 | 31 | hasChanged = false; 32 | std::vector indexes = clusters.clusterIndexes(); 33 | std::map mergePairs; 34 | for (std::size_t i = 0; i < indexes.size(); ++i) 35 | { 36 | for (std::size_t j = i + 1; j < indexes.size(); ++j) 37 | { 38 | GUInt32 index_i = indexes[i]; 39 | GUInt32 index_j = indexes[j]; 40 | 41 | std::vector intersection; 42 | std::set_intersection(expandPoints[index_i].begin(), expandPoints[index_i].end(), 43 | expandPoints[index_j].begin(), expandPoints[index_j].end(), 44 | std::back_inserter(intersection), PointComparator()); 45 | 46 | double oneSeedHeight = clusters.seedPoint(index_i).getZ(); 47 | double otherSeedHeight = clusters.seedPoint(index_j).getZ(); 48 | 49 | for (const OGRPoint& point : intersection) 50 | { 51 | double pointHeight = point.getZ(); 52 | double diff = oneSeedHeight - pointHeight + otherSeedHeight - pointHeight; 53 | double normalizedDiff = diff / std::min(oneSeedHeight, otherSeedHeight); 54 | 55 | if (normalizedDiff < 1.0 56 | && mergePairs.count(index_j) == 0 && mergePairs.count(index_i) == 0) 57 | { 58 | mergePairs[index_i] = index_j; 59 | mergePairs[index_j] = index_i; 60 | break; 61 | } 62 | } 63 | } 64 | } 65 | 66 | for (const auto& pair : mergePairs) 67 | { 68 | if (pair.first < pair.second) 69 | clusters.mergeClusters(pair.first, pair.second); 70 | } 71 | 72 | for (const auto& pair : expandPoints) 73 | { 74 | indexes = clusters.clusterIndexes(); 75 | GUInt32 index = pair.first; 76 | 77 | if (std::find(indexes.begin(), indexes.end(), index) == indexes.end()) 78 | { 79 | index = mergePairs[index]; 80 | } 81 | 82 | for (const auto& point : pair.second) 83 | { 84 | try 85 | { 86 | clusters.clusterIndex(point.getX(), point.getY()); 87 | } 88 | catch (std::out_of_range&) 89 | { 90 | clusters.addPoint(index, point.getX(), point.getY(), point.getZ()); 91 | hasChanged = true; 92 | } 93 | } 94 | } 95 | 96 | if (currentVerticalDistance < maxVerticalDistance) 97 | currentVerticalDistance = std::min(currentVerticalDistance + increaseVerticalDistance, maxVerticalDistance); 98 | } 99 | while (hasChanged || currentVerticalDistance < maxVerticalDistance); 100 | }; 101 | } 102 | 103 | std::set TreeCrownSegmentation::expandCluster( 104 | GUInt32 index, double verticalThreshold) 105 | { 106 | std::set expand; 107 | OGRPoint center = clusters.center2D(index); 108 | 109 | for (const OGRPoint& p : clusters.neighbors(index)) 110 | { 111 | double horizontalDistance = std::sqrt(std::pow(center.getX() - p.getX(), 2.0) 112 | + std::pow(center.getY() - p.getY(), 2.0)); 113 | double verticalDistance = std::abs(sourceData(p.getX(), p.getY()) 114 | - sourceData(clusters.seedPoint(index).getX(), 115 | clusters.seedPoint(index).getY())); 116 | 117 | if (this->hasSourceData(p.getX(), p.getY()) && horizontalDistance <= maxHorizontalDistance 118 | && verticalDistance <= verticalThreshold) 119 | expand.insert(OGRPoint(p.getX(), p.getY(), sourceData(p.getX(), p.getY()))); 120 | } 121 | 122 | return expand; 123 | } 124 | 125 | ClusterMap& TreeCrownSegmentation::clusterMap() 126 | { 127 | return this->clusters; 128 | } 129 | } // Vegetation 130 | } // CloudTools 131 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/TreeCrownSegmentation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace CloudTools 12 | { 13 | namespace Vegetation 14 | { 15 | class TreeCrownSegmentation : public CloudTools::DEM::DatasetCalculation 16 | { 17 | public: 18 | /// 19 | /// The tree crown seed points of the algorithm. 20 | /// 21 | std::vector seedPoints; 22 | 23 | public: 24 | double maxVerticalDistance = 14.0; // in meters 25 | double maxHorizontalDistance = 12.0; // in units of resolution (e.g. with 0.5m resolution it is 6 meters) 26 | double initialVerticalDistance = 2.0; // in meters 27 | double increaseVerticalDistance = 2.0; // in meters 28 | 29 | /// 30 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 31 | /// 32 | /// The source path of the algorithm. 33 | /// The target file of the algorithm. 34 | /// The tree crown seed points. 35 | /// The callback method to report progress. 36 | TreeCrownSegmentation(const std::string& sourcePath, 37 | const std::vector& seedPoints, 38 | Operation::ProgressType progress = nullptr) 39 | : DatasetCalculation({sourcePath}, nullptr, progress), 40 | seedPoints(seedPoints) 41 | { 42 | initialize(); 43 | } 44 | 45 | /// 46 | /// Initializes a new instance of the class. Loads input metadata and defines computation. 47 | /// 48 | /// The source dataset of the algorithm. 49 | /// The target file of the algorithm. 50 | /// The tree crown seed points. 51 | /// The callback method to report progress. 52 | TreeCrownSegmentation(GDALDataset* sourceDataset, 53 | const std::vector& seedPoints, 54 | Operation::ProgressType progress = nullptr) 55 | : DatasetCalculation({sourceDataset}, nullptr, progress), 56 | seedPoints(seedPoints) 57 | { 58 | initialize(); 59 | } 60 | 61 | TreeCrownSegmentation(const TreeCrownSegmentation&) = delete; 62 | 63 | TreeCrownSegmentation& operator=(const TreeCrownSegmentation&) = delete; 64 | 65 | CloudTools::DEM::ClusterMap& clusterMap(); 66 | 67 | private: 68 | CloudTools::DEM::ClusterMap clusters; 69 | 70 | /// 71 | /// Initializes the new instance of the class. 72 | /// 73 | void initialize(); 74 | 75 | std::set expandCluster(GUInt32 index, double verticalThreshold); 76 | }; 77 | } // Vegetation 78 | } // CloudTools 79 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/VolumeDifference.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "VolumeDifference.h" 4 | 5 | namespace CloudTools 6 | { 7 | namespace Vegetation 8 | { 9 | void VolumeDifference::calculateVolume() 10 | { 11 | std::pair> volumeA = calculateLonelyEpochVolume(Epoch::A, this->clusterMapA); 12 | this->fullVolumeA = volumeA.first; 13 | this->lonelyVolumeA = volumeA.second; 14 | 15 | std::pair> volumeB = calculateLonelyEpochVolume(Epoch::B, this->clusterMapB); 16 | this->fullVolumeB = volumeB.first; 17 | this->lonelyVolumeB = volumeB.second; 18 | 19 | calculateDifference(); 20 | } 21 | 22 | std::pair> VolumeDifference::calculateLonelyEpochVolume(Epoch epoch, ClusterMap& map) 23 | { 24 | double fullVolume = 0.0; 25 | std::map lonelyVolume; 26 | std::vector lonely; 27 | 28 | if (epoch == Epoch::A) 29 | lonely = this->distance->lonelyA(); 30 | 31 | if (epoch == Epoch::B) 32 | lonely = this->distance->lonelyB(); 33 | 34 | for (const auto& elem : lonely) 35 | { 36 | double volume = std::accumulate(map.points(elem).begin(), map.points(elem).end(), 37 | 0.0, [](double sum, const OGRPoint& point) 38 | { 39 | return sum + point.getZ(); 40 | }); 41 | volume *= 0.25; 42 | lonelyVolume.insert(std::make_pair(elem, volume)); 43 | fullVolume += std::abs(volume); 44 | } 45 | 46 | return std::make_pair(fullVolume, lonelyVolume); 47 | } 48 | 49 | void VolumeDifference::calculateDifference() 50 | { 51 | double clusterVolumeA, clusterVolumeB; 52 | 53 | for (const auto& elem : this->distance->closest()) 54 | { 55 | clusterVolumeA = std::accumulate(this->clusterMapA.points(elem.first.first).begin(), 56 | this->clusterMapA.points(elem.first.first).end(), 0.0, 57 | [](double sum, const OGRPoint& point) 58 | { 59 | return sum + point.getZ(); 60 | }); 61 | clusterVolumeA *= 0.25; 62 | this->fullVolumeA += std::abs(clusterVolumeA); 63 | 64 | clusterVolumeB = std::accumulate(this->clusterMapB.points(elem.first.second).begin(), 65 | this->clusterMapB.points(elem.first.second).end(), 0.0, 66 | [](double sum, const OGRPoint& point) 67 | { 68 | return sum + point.getZ(); 69 | }); 70 | clusterVolumeB *= 0.25; 71 | this->fullVolumeB += std::abs(clusterVolumeB); 72 | 73 | this->diffs.insert(std::make_pair(elem.first, clusterVolumeB - clusterVolumeA)); 74 | } 75 | } 76 | } // Vegetation 77 | } // CloudTools 78 | -------------------------------------------------------------------------------- /CloudTools.Vegetation/VolumeDifference.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "DistanceCalculation.h" 8 | 9 | using namespace CloudTools::DEM; 10 | 11 | namespace CloudTools 12 | { 13 | namespace Vegetation 14 | { 15 | class VolumeDifference 16 | { 17 | public: 18 | double fullVolumeA, fullVolumeB; 19 | std::map lonelyVolumeA, lonelyVolumeB; 20 | std::map, double> diffs; 21 | 22 | VolumeDifference(ClusterMap& clusterMapA, 23 | ClusterMap& clusterMapB, 24 | std::shared_ptr distance) 25 | : clusterMapA(clusterMapA), clusterMapB(clusterMapB), distance(distance) 26 | { 27 | calculateVolume(); 28 | } 29 | 30 | private: 31 | enum class Epoch 32 | { 33 | A, B 34 | }; 35 | 36 | ClusterMap clusterMapA, clusterMapB; 37 | std::shared_ptr distance; 38 | 39 | void calculateVolume(); 40 | 41 | std::pair> calculateLonelyEpochVolume(Epoch epoch, ClusterMap& map); 42 | 43 | void calculateDifference(); 44 | }; 45 | } // Vegetation 46 | } // CloudTools 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2016-2020 Máté Cserép 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PointCloudTools 2 | ============ 3 | 4 | Massive airborne laser altimetry (ALS) point cloud and digital elevation model (DEM) processing library. 5 | The toolset supports change detection of the built-up area and vegetation, tested primarily on the study dataset [AHN - Actueel Hoogtebestand Nederland](http://www.ahn.nl/). 6 | 7 | 8 | Build and install 9 | ------------ 10 | 11 | The project is continuously built and tested on Windows 10/11 and Ubuntu 20.04/22.04 LTS. 12 | See the [Build instructions](BUILD.md) for a detailed guide. 13 | 14 | 15 | Demo 16 | ------------ 17 | 18 | An interactive visualization of changes in buildings and vegetation for the Dutch AHN point clouds is available at: 19 | **https://gis.inf.elte.hu/ahn/** 20 | 21 | ![Change detection of the built-up area](doc/screenshot_building_small.png) 22 | ![Change detection of the vegetation](doc/screenshot_vegetation_small.png) 23 | 24 | Publications 25 | ------------ 26 | 27 | * Máté Cserép, Roderik Lindenbergh: *Distributed processing of Dutch AHN laser altimetry changes of the built-up area*, International Journal of Applied Earth Observation and Geoinformation, Vol. 116 , Article 103174, 2023, [DOI: 10.1016/j.jag.2022.103174](https://doi.org/10.1016/j.jag.2022.103174) 28 | * Anett Fekete, Máté Cserép *Tree segmentation and change detection of large urban areas based on airborne LiDAR*, Computers & Geosciences, Vol. 156 , Article 104900, 2021, [DOI: 10.1016/j.cageo.2021.104900](https://doi.org/10.1016/j.cageo.2021.104900) 29 | 30 | 31 | Contributing 32 | ------------ 33 | 34 | Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on coding conventions. 35 | 36 | 37 | License 38 | ------------ 39 | 40 | This project is licensed under the BSD 3-Clause License - see the [LICENSE](LICENSE) file for details. 41 | -------------------------------------------------------------------------------- /Shell.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM CloudTools Environment 3 | 4 | :: Load configuration 5 | if exist "%~dp0\Shell.config.cmd" ( 6 | call "%~dp0\Shell.config.cmd" 7 | ) else ( 8 | echo Create Shell.config.cmd config file from Shell.config.cmd.sample! 9 | exit /b 10 | ) 11 | set PATH=%~dp0;%GDAL_BIN%;%PATH% 12 | 13 | :: Check GDAL/OGR 14 | where gdalinfo >nul 2>&1 15 | if %ERRORLEVEL% neq 0 ( 16 | echo GDAL/OGR tools not found. 17 | exit /b 18 | ) 19 | 20 | :: Check CloudTools 21 | where dem_diff >nul 2>&1 22 | if %ERRORLEVEL% neq 0 ( 23 | echo CloudTools not found. 24 | exit /b 25 | ) 26 | 27 | echo === CloudTools Environment === 28 | echo. 29 | 30 | @if [%1]==[] ( 31 | echo Available programs: 32 | echo dem_diff 33 | echo dem_mask 34 | echo ahn_buildings_sim 35 | echo ahn_buildings_par 36 | echo ahn_buildings_agg 37 | echo ahn_buildings_ver 38 | echo ahn_buildings_mpi 39 | echo vegetation 40 | echo vegetation_ver 41 | echo. 42 | cmd.exe /k "" 43 | ) else ( 44 | cmd /c "%*" 45 | ) -------------------------------------------------------------------------------- /Shell.config.cmd.sample: -------------------------------------------------------------------------------- 1 | set GDAL_BIN=%GDAL_BIN% 2 | set GDAL_DATA=%GDAL_DATA% 3 | set GDAL_DRIVER_PATH=%GDAL_DRIVER_PATH% 4 | -------------------------------------------------------------------------------- /WINDOWS_QUICK_START.md: -------------------------------------------------------------------------------- 1 | # Quick Start for Window OS 2 | 3 | This guide describes how to install the dependencies and compile *PointCloudTools* on Windows OS 4 | in a quick and easy way. It is assumed that you already have Visual Studio 2019/2022 with the 5 | *Desktop development with C++* workload installed. 6 | 7 | 8 | ## Install dependencies 9 | 10 | ### Install Boost 11 | 12 | Download the official prebuilt Boost binaries from the following repository: 13 | https://sourceforge.net/projects/boost/files/boost-binaries/ 14 | 15 | Select a recent version (minimum requirement is 1.58), compatible with your MSVC version (Visual C++ 2022 is MSVC 14.3). 16 | 64 bit version is preferred to be installed, but the 32 bit version can also be installed if required. 17 | 18 | ### Install GDAL 19 | 20 | Download the unofficial prebuilt GDAL binaries for Windows from GISInternals: 21 | https://www.gisinternals.com/release.php 22 | 23 | Select a recent version matching your Visual Studio / MSVC version. 24 | 64 bit version is preferred to be installed, but the 32 bit version can also be installed if required. 25 | 26 | *Note:* There is two separate download links. You have to download both *Compiled binaries* AND *Compiled libraries and headers*, then extract them to the same folder. 27 | 28 | ### Install OpenCV 29 | 30 | Download the official prebuilt OpenCV binaries for Windows from their website: 31 | https://opencv.org/releases/ 32 | 33 | Select a recent version (minimum requirement is 4.2). 34 | The downloadable executable is a self-extracting archive, which you can extract at any preferred location. 35 | 36 | *Note:* for more recent versions, only the 64 bit version of OpenCV is shipped in this self-extracting archive, but that should be preferred anyway. 37 | 38 | ### Install MS-MPI *(optional)* 39 | 40 | Download the official prebuilt binaries for Microsoft's implementation of the MPI library from the follwoing website: 41 | https://learn.microsoft.com/en-us/message-passing-interface/microsoft-mpi 42 | 43 | For development, you especially need to install the SDK, execute the `msmpisdk.msi` file. 44 | 45 | 46 | ## Set up environment variables 47 | 48 | Set up the following environment variables with the appropriate paths 49 | used in your system. 50 | 51 | | Name | Value | 52 | | ------------ | ---------------------- | 53 | | `BOOST_ROOT` | C:\Programs\Boost\boost_1_81_0 | 54 | | `GDAL_ROOT` | C:\Programs\GDAL\3.6.2 | 55 | | `GDAL_BIN` | C:\Programs\GDAL\3.6.2\bin;C:\Programs\GDAL\3.6.2\bin\gdal\apps | 56 | | `GDAL_DATA` | C:\Programs\GDAL\3.6.2\bin\gdal-data | 57 | | `GDAL_DRIVER_PATH` | C:\Programs\GDAL\3.6.2\bin\gdal\plugins | 58 | | `PROJ_LIB` | C:\Programs\GDAL\3.6.2\bin\proj6\share | 59 | | `OpenCV_DIR` | C:\Programs\OpenCV\build | 60 | | `MPI_HOME` | C:\Program Files (x86)\Microsoft SDKs\MPI | 61 | 62 | *Note:* for newer GDAL versions 3.8+, there is a `proj9` folder instead of `proj6`, which should be used. 63 | 64 | Also add the GDAL and the OpenCV binary directories to the `PATH` environment variable, as these will be linked dynamically: 65 | - add the paths in the `GDAL_BIN` variable; 66 | - add the OpenCV binary directory (e.g. `C:\Programs\OpenCV\build\x64\vc16\bin`). 67 | 68 | 69 | ## Build *PointCloudTools* from command Line 70 | 71 | Launch a *Developer Command Prompt for VS* and generate the build environment with CMake: 72 | ```batch 73 | mkdir build 74 | cd build 75 | cmake .. -DCMAKE_INSTALL_PREFIX=..\install 76 | ``` 77 | 78 | Build the solution and optionally install the binaries to the specified `install` destination. 79 | ```batch 80 | msbuild CloudTools.sln 81 | msbuild INSTALL.vcxproj 82 | ``` 83 | 84 | 85 | ## Build *PointCloudTools* from Visual Studio 86 | 87 | As a preliminary steps *(has to be done only once)*, 88 | create a copy of the `CMakeSettings.json.sample` file with the 89 | name `CMakeSettings.json`. 90 | This file contains the prepared build configurations which will be loaded by Visual Studio for the *Ninja* generator used by CMake. 91 | 92 | Then simply open the folder of the project in VS, as Visual Studio has integrated CMake support. 93 | Build steps in Visual Studio: 94 | 1. Select the `x64-Debug` or `x64-Release` build configuration in the top menu bar. 95 | *Remark:* You may use the *x86* versions if you have installed the 32 bit dependencies. Note however, that 32 bit processes have limited memory access around 3GB, which can be an issue when processing larger files with *PointCloudTools*. 96 | 2. CMake should be executed automatically, but you can enforce to run it with the *Project -> Delete Cache and Reconfigure* option. 97 | 3. Build the project with the *Build -> Build All* menu option. 98 | 4. Install the project to the default `install` folder with the *Build -> Install CloudTools* option. 99 | -------------------------------------------------------------------------------- /doc/UML_Buildings_Model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GISLab-ELTE/PointCloudTools/5a1608760379e24223b28a38265f7ced98e3171c/doc/UML_Buildings_Model.png -------------------------------------------------------------------------------- /doc/UML_Operation_Model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GISLab-ELTE/PointCloudTools/5a1608760379e24223b28a38265f7ced98e3171c/doc/UML_Operation_Model.png -------------------------------------------------------------------------------- /doc/UML_Vegetation_Model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GISLab-ELTE/PointCloudTools/5a1608760379e24223b28a38265f7ced98e3171c/doc/UML_Vegetation_Model.png -------------------------------------------------------------------------------- /doc/screenshot_building_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GISLab-ELTE/PointCloudTools/5a1608760379e24223b28a38265f7ced98e3171c/doc/screenshot_building_small.png -------------------------------------------------------------------------------- /doc/screenshot_vegetation_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GISLab-ELTE/PointCloudTools/5a1608760379e24223b28a38265f7ced98e3171c/doc/screenshot_vegetation_small.png --------------------------------------------------------------------------------