diff --git a/depthmapX/mainwindowmoduleregistry.cpp b/depthmapX/mainwindowmoduleregistry.cpp index 5238510f..4a038551 100644 --- a/depthmapX/mainwindowmoduleregistry.cpp +++ b/depthmapX/mainwindowmoduleregistry.cpp @@ -16,9 +16,11 @@ #include "mainwindowmoduleregistry.hpp" #include "modules/segmentshortestpaths/gui/segmentpathsmainwindow.h" +#include "modules/vgaparallel/gui/vgaparallelmainwindow.h" void MainWindowModuleRegistry::populateModules() { // Register any main window modules here REGISTER_MAIN_WINDOW_MODULE(SegmentPathsMainWindow); + REGISTER_MAIN_WINDOW_MODULE(VGAParallelMainWindow) // ********* } diff --git a/depthmapXcli/modeparserregistry.cpp b/depthmapXcli/modeparserregistry.cpp index e54a3d4f..9802c9ee 100644 --- a/depthmapXcli/modeparserregistry.cpp +++ b/depthmapXcli/modeparserregistry.cpp @@ -26,6 +26,7 @@ #include "stepdepthparser.h" #include "mapconvertparser.h" #include "modules/segmentshortestpaths/cli/segmentshortestpathparser.h" +#include "modules/vgaparallel/cli/vgaparallelparser.h" void ModeParserRegistry::populateParsers() @@ -43,5 +44,6 @@ void ModeParserRegistry::populateParsers() REGISTER_PARSER(StepDepthParser); REGISTER_PARSER(MapConvertParser); REGISTER_PARSER(SegmentShortestPathParser); + REGISTER_PARSER(VgaParallelParser) // ********* } diff --git a/depthmapXcli/runmethods.h b/depthmapXcli/runmethods.h index 5aa57538..12777697 100644 --- a/depthmapXcli/runmethods.h +++ b/depthmapXcli/runmethods.h @@ -44,6 +44,7 @@ class Point2f; namespace dm_runmethods{ std::unique_ptr loadGraph(const std::string& filename, IPerformanceSink &perfWriter); + std::unique_ptr getCommunicator(const CommandLineParser &clp); void importFiles(const CommandLineParser &cmdP, const ImportParser &parser, IPerformanceSink &perfWriter); void linkGraph(const CommandLineParser &cmdP, const LinkParser &parser, IPerformanceSink &perfWriter ); void runVga(const CommandLineParser &cmdP, const VgaParser &vgaP, const IRadiusConverter &converter, IPerformanceSink &perfWriter ); diff --git a/modules/vgaparallel/CMakeLists.txt b/modules/vgaparallel/CMakeLists.txt new file mode 100644 index 00000000..70b4dd7b --- /dev/null +++ b/modules/vgaparallel/CMakeLists.txt @@ -0,0 +1,34 @@ +# Copyright (C) 2020 Petros Koutsolampros + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +if(MODULES_CORE) +add_subdirectory(core) +endif() + +if(MODULES_GUI) + add_subdirectory(gui) +endif() + +if(MODULES_CLI) + add_subdirectory(cli) +endif() + +if(MODULES_CORE_TEST) + add_subdirectory(coreTest) +endif() + +if(MODULES_CLI_TEST) + add_subdirectory(cliTest) +endif() diff --git a/modules/vgaparallel/RegressionTest/regressionconfig.json b/modules/vgaparallel/RegressionTest/regressionconfig.json new file mode 100644 index 00000000..582fd805 --- /dev/null +++ b/modules/vgaparallel/RegressionTest/regressionconfig.json @@ -0,0 +1,14 @@ +{ + "rundir": "rundir", + "basebinlocation": "../../BaselineBinaries", + "testbinlocation": "../../../build", + "testcases": { + "shortest_segment_metric_path": [{ + "infile": "../../../testdata/barnsbury_extended1_segment.graph", + "outfile": "out.graph", + "mode": "VGAPARALLEL", + "extraArgs": { + } + }] + } +} diff --git a/modules/vgaparallel/cli/CMakeLists.txt b/modules/vgaparallel/cli/CMakeLists.txt new file mode 100644 index 00000000..578bec46 --- /dev/null +++ b/modules/vgaparallel/cli/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (C) 2020 Petros Koutsolampros + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(vgaparallelcli vgaparallelcli) +set(vgaparallelcli_SRCS + vgaparallelparser.cpp) + +set(modules_cli "${modules_cli}" "vgaparallelcli" CACHE INTERNAL "modules_cli" FORCE) + +add_compile_definitions(VGAPARALLEL_CLI_LIBRARY) + +add_library(${vgaparallelcli} OBJECT ${vgaparallelcli_SRCS}) diff --git a/modules/vgaparallel/cli/vgaparallelparser.cpp b/modules/vgaparallel/cli/vgaparallelparser.cpp new file mode 100644 index 00000000..eda3dff0 --- /dev/null +++ b/modules/vgaparallel/cli/vgaparallelparser.cpp @@ -0,0 +1,118 @@ +// Copyright (C) 2017 Christian Sailer +// Copyright (C) 2020 Petros Koutsolampros + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "vgaparallelparser.h" +#include "depthmapXcli/exceptions.h" +#include "depthmapXcli/parsingutils.h" +#include "depthmapXcli/runmethods.h" +#include "depthmapXcli/simpletimer.h" +#include "modules/vgaparallel/core/vgaangularopenmp.h" +#include "modules/vgaparallel/core/vgametricopenmp.h" +#include "modules/vgaparallel/core/vgavisualglobalopenmp.h" +#include "modules/vgaparallel/core/vgavisuallocaladjmatrix.h" +#include "modules/vgaparallel/core/vgavisuallocalopenmp.h" +#include "salalib/options.h" +#include +#include + +using namespace depthmapX; + +VgaParallelParser::VgaParallelParser() : m_vgaMode(VgaMode::NONE) {} + +void VgaParallelParser::parse(int argc, char *argv[]) { + for (int i = 1; i < argc;) { + + if (std::strcmp("-vm", argv[i]) == 0) { + if (m_vgaMode != VgaMode::NONE) { + throw CommandLineException("-vm can only be used once, modes are mutually exclusive"); + } + ENFORCE_ARGUMENT("-vm", i) + if (std::strcmp(argv[i], "visiblity-global") == 0) { + m_vgaMode = VgaMode::VISBILITY_GLOBAL; + } else if (std::strcmp(argv[i], "visibility-local") == 0) { + m_vgaMode = VgaMode::VISBILITY_LOCAL; + } else if (std::strcmp(argv[i], "visibility-local-adjmatrix") == 0) { + m_vgaMode = VgaMode::VISBILITY_LOCAL_ADJMATRIX; + } else if (std::strcmp(argv[i], "metric") == 0) { + m_vgaMode = VgaMode::METRIC; + } else if (std::strcmp(argv[i], "angular") == 0) { + m_vgaMode = VgaMode::ANGULAR; + } else { + throw CommandLineException(std::string("Invalid VGAPARALLEL mode: ") + argv[i]); + } + } else if (std::strcmp(argv[i], "-vr") == 0) { + ENFORCE_ARGUMENT("-vr", i) + m_radius = argv[i]; + } + ++i; + } + + if (m_vgaMode == VgaMode::VISBILITY_GLOBAL) { + if (m_radius.empty()) { + throw CommandLineException( + "Global measures in VGA/visibility analysis require a radius, use -vr "); + } + if (m_radius != "n" && !has_only_digits(m_radius)) { + throw CommandLineException(std::string("Radius must be a positive integer number or n, got ") + m_radius); + } + + } else if (m_vgaMode == VgaMode::METRIC) { + if (m_radius.empty()) { + throw CommandLineException("Metric vga requires a radius, use -vr "); + } + if (m_radius != "n" && !has_only_digits(m_radius)) { + throw CommandLineException(std::string("Radius must be a positive integer number or n, got ") + m_radius); + } + } +} + +void VgaParallelParser::run(const CommandLineParser &clp, IPerformanceSink &perfWriter) const { + + auto mgraph = dm_runmethods::loadGraph(clp.getFileName().c_str(), perfWriter); + + std::unique_ptr analysis = nullptr; + + RadiusConverter radiusConverter; + switch (getVgaMode()) { + case VgaMode::VISBILITY_GLOBAL: + analysis = std::unique_ptr(new VGAVisualGlobalOpenMP( + mgraph->getDisplayedPointMap(), radiusConverter.ConvertForVisibility(getRadius()), false)); + break; + case VgaMode::VISBILITY_LOCAL: + analysis = std::unique_ptr(new VGAVisualLocalOpenMP(mgraph->getDisplayedPointMap())); + break; + case VgaMode::VISBILITY_LOCAL_ADJMATRIX: + analysis = std::unique_ptr(new VGAVisualLocalAdjMatrix(mgraph->getDisplayedPointMap(), false)); + break; + case VgaMode::METRIC: + analysis = std::unique_ptr( + new VGAMetricOpenMP(mgraph->getDisplayedPointMap(), radiusConverter.ConvertForMetric(getRadius()), false)); + break; + case VgaMode::ANGULAR: + analysis = std::unique_ptr(new VGAAngularOpenMP( + mgraph->getDisplayedPointMap(), radiusConverter.ConvertForMetric(getRadius()), false)); + break; + default: + throw depthmapX::SetupCheckException("Unsupported VGA mode"); + } + + std::cout << " ok\nAnalysing graph..." << std::flush; + + DO_TIMED("Run VGA", analysis->run(dm_runmethods::getCommunicator(clp).get())) + std::cout << " ok\nWriting out result..." << std::flush; + DO_TIMED("Writing graph", mgraph->write(clp.getOuputFile().c_str(), METAGRAPH_VERSION, false)) + std::cout << " ok" << std::endl; +} diff --git a/modules/vgaparallel/cli/vgaparallelparser.h b/modules/vgaparallel/cli/vgaparallelparser.h new file mode 100644 index 00000000..bae97ef5 --- /dev/null +++ b/modules/vgaparallel/cli/vgaparallelparser.h @@ -0,0 +1,52 @@ +// Copyright (C) 2017 Christian Sailer + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include "depthmapXcli/commandlineparser.h" +#include "depthmapXcli/imodeparser.h" +#include "depthmapXcli/radiusconverter.h" +#include + +class VgaParallelParser : public IModeParser { + public: + virtual std::string getModeName() const { return "VGA"; } + + virtual std::string getHelp() const { + return "Mode options for VGAPARALLEL:\n" + "-vm one of visiblity-global (default algorithm)" + " visibility-local (default algorithm)" + " visibility-local-adjmatrix (alternative algorithm)" + " metric (default algorithm)" + " angular (default algorithm)\n" + "-vr radius between 1 and 99 or n, to limit visibility\n"; + } + + public: + VgaParallelParser(); + virtual void parse(int argc, char *argv[]); + virtual void run(const CommandLineParser &clp, IPerformanceSink &perfWriter) const; + + enum VgaMode { NONE, VISBILITY_GLOBAL, VISBILITY_LOCAL, VISBILITY_LOCAL_ADJMATRIX, METRIC, ANGULAR }; + + // vga options + VgaMode getVgaMode() const { return m_vgaMode; } + const std::string &getRadius() const { return m_radius; } + + private: + // vga options + VgaMode m_vgaMode; + std::string m_radius; +}; diff --git a/modules/vgaparallel/cliTest/CMakeLists.txt b/modules/vgaparallel/cliTest/CMakeLists.txt new file mode 100644 index 00000000..18d5ebc6 --- /dev/null +++ b/modules/vgaparallel/cliTest/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (C) 2020 Petros Koutsolampros + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(vgaparallelclitest vgaparallelclitest) +set(vgaparallelclitest_SRCS + vgaparallelparsertest.cpp) + +set(modules_cliTest "${modules_cliTest}" "vgaparallelclitest" CACHE INTERNAL "modules_cliTest" FORCE) + +add_compile_definitions(VGAPARALLEL_CLI_TEST_LIBRARY) + +add_library(${vgaparallelclitest} OBJECT ${vgaparallelclitest_SRCS}) diff --git a/modules/vgaparallel/cliTest/vgaparallelparsertest.cpp b/modules/vgaparallel/cliTest/vgaparallelparsertest.cpp new file mode 100644 index 00000000..d0c62217 --- /dev/null +++ b/modules/vgaparallel/cliTest/vgaparallelparsertest.cpp @@ -0,0 +1,83 @@ +// Copyright (C) 2020 Petros Koutsolampros + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "modules/vgaparallel/cli/vgaparallelparser.h" +#include "cliTest/argumentholder.h" +#include "cliTest/selfcleaningfile.h" +#include + +TEST_CASE("VgaParallelParser", "Error cases") { + SECTION("Missing argument to -vr") { + VgaParallelParser parser; + ArgumentHolder ah{ + "prog", + "-vr", + }; + REQUIRE_THROWS_WITH(parser.parse(ah.argc(), ah.argv()), Catch::Contains("-vr requires an argument")); + } + + SECTION("Missing argument to -vm") { + VgaParallelParser parser; + ArgumentHolder ah{"prog", "-vm"}; + REQUIRE_THROWS_WITH(parser.parse(ah.argc(), ah.argv()), Catch::Contains("-vm requires an argument")); + } + + SECTION("rubbish input to -vm") { + VgaParallelParser parser; + ArgumentHolder ah{"prog", "-vm", "foo"}; + REQUIRE_THROWS_WITH(parser.parse(ah.argc(), ah.argv()), Catch::Contains("Invalid VGAPARALLEL mode: foo")); + } + + SECTION("no input to -vr in metric analysis") { + VgaParallelParser parser; + ArgumentHolder ah{"prog", "-vm", "metric"}; + REQUIRE_THROWS_WITH(parser.parse(ah.argc(), ah.argv()), + Catch::Contains("Metric vga requires a radius, use -vr ")); + } + + SECTION("rubbish input to -vr in metric analysis") { + VgaParallelParser parser; + ArgumentHolder ah{"prog", "-vm", "metric", "-vr", "foo"}; + REQUIRE_THROWS_WITH(parser.parse(ah.argc(), ah.argv()), + Catch::Contains("Radius must be a positive integer number or n, got foo")); + } + + SECTION("no input to -vr in visual global analysis") { + VgaParallelParser parser; + ArgumentHolder ah{"prog", "-vm", "visiblity-global"}; + REQUIRE_THROWS_WITH( + parser.parse(ah.argc(), ah.argv()), + Catch::Contains("Global measures in VGA/visibility analysis require a radius, use -vr ")); + } + + SECTION("rubbish input to -vr in visual global analysis") { + VgaParallelParser parser; + ArgumentHolder ah{"prog", "-vm", "visiblity-global", "-vr", "foo"}; + REQUIRE_THROWS_WITH(parser.parse(ah.argc(), ah.argv()), + Catch::Contains("Radius must be a positive integer number or n, got foo")); + } +} + +TEST_CASE("Successful VgaParallelParser", "Read successfully") { + VgaParallelParser parser; + + SECTION("Read from commandline") { + ArgumentHolder ah{"prog", "-vm", "metric", "-vr", "5"}; + parser.parse(ah.argc(), ah.argv()); + } + + REQUIRE(parser.getVgaMode() == VgaParallelParser::VgaMode::METRIC); + REQUIRE(parser.getRadius() == "5"); +} diff --git a/modules/vgaparallel/core/CMakeLists.txt b/modules/vgaparallel/core/CMakeLists.txt new file mode 100644 index 00000000..6e370c6c --- /dev/null +++ b/modules/vgaparallel/core/CMakeLists.txt @@ -0,0 +1,34 @@ +# Copyright (C) 2020 Petros Koutsolampros + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(vgaparallelcore vgaparallelcore) +set(vgaparallelcore_SRCS + vgavisuallocalopenmp.cpp + vgavisuallocaladjmatrix.cpp + vgavisualglobalopenmp.cpp + vgametricopenmp.cpp + vgaangularopenmp.cpp) + +include(CMakeOpenMP.txt) + +set(modules_core "${modules_core}" "vgaparallelcore" CACHE INTERNAL "modules_core" FORCE) + +add_compile_definitions(VGAPARALLEL_CORE_LIBRARY) + +add_library(${vgaparallelcore} OBJECT ${vgaparallelcore_SRCS}) + +find_package(OpenMP REQUIRED) + +target_link_libraries(${vgaparallelcore} OpenMP::OpenMP_CXX) diff --git a/modules/vgaparallel/core/CMakeOpenMP.txt b/modules/vgaparallel/core/CMakeOpenMP.txt new file mode 100644 index 00000000..3be307a5 --- /dev/null +++ b/modules/vgaparallel/core/CMakeOpenMP.txt @@ -0,0 +1,62 @@ +# Copyright (C) 2020 Petros Koutsolampros + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +option(USE_OPENMP "Enable OpenMP" ON) + +if(USE_OPENMP) + find_package(OpenMP) + + # If OpenMP wasn't found, try if we can find it in the default Macports location + if((NOT OPENMP_FOUND) AND (NOT OPENMP_CXX_FOUND) AND EXISTS "/opt/local/lib/libomp/libomp.dylib") + set(OpenMP_C_FLAGS "-Xpreprocessor -fopenmp -I/opt/local/include/libomp/") + set(OpenMP_C_LIB_NAMES omp) + set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -I/opt/local/include/libomp/") + set(OpenMP_CXX_LIB_NAMES omp) + set(OpenMP_omp_LIBRARY "/opt/local/lib/libomp/libomp.dylib") + + find_package(OpenMP) + if (OPENMP_FOUND OR OPENMP_CXX_FOUND) + message(STATUS "Found libomp in macports default location.") + else() + message(FATAL_ERROR "Didn't find libomp. Tried macports default location but also didn't find it.") + endif() + endif() + + # If OpenMP wasn't found, try if we can find it in the default Homebrew location + if((NOT OPENMP_FOUND) AND (NOT OPENMP_CXX_FOUND) AND EXISTS "/usr/local/opt/libomp/lib/libomp.dylib") + set(OpenMP_C_FLAGS "-Xpreprocessor -fopenmp -I/usr/local/opt/libomp/include") + set(OpenMP_C_LIB_NAMES omp) + set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -I/usr/local/opt/libomp/include") + set(OpenMP_CXX_LIB_NAMES omp) + set(OpenMP_omp_LIBRARY "/usr/local/opt/libomp/lib/libomp.dylib") + + find_package(OpenMP) + if (OPENMP_FOUND OR OPENMP_CXX_FOUND) + message(STATUS "Found libomp in homebrew default location.") + else() + message(FATAL_ERROR "Didn't find libomp. Tried homebrew default location but also didn't find it.") + endif() + endif() + + if((NOT OPENMP_FOUND) AND (NOT OPENMP_CXX_FOUND)) + message(FATAL_ERROR "Did not find OpenMP.") + endif() + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") +else() + if( (CMAKE_CXX_COMPILER_ID MATCHES "[cC][lL][aA][nN][gG]") + OR (CMAKE_CXX_COMPILER_ID MATCHES "[gG][nN][uU]")) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas") + endif() +endif(USE_OPENMP) \ No newline at end of file diff --git a/modules/vgaparallel/core/vgaangularopenmp.cpp b/modules/vgaparallel/core/vgaangularopenmp.cpp new file mode 100644 index 00000000..ac9d6ba3 --- /dev/null +++ b/modules/vgaparallel/core/vgaangularopenmp.cpp @@ -0,0 +1,187 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "vgaangularopenmp.h" + +#include "genlib/stringutils.h" + +#include + +bool VGAAngularOpenMP::run(Communicator *comm) { + time_t atime = 0; + + if (comm) { + qtimer(atime, 0); + comm->CommPostMessage(Communicator::NUM_RECORDS, m_map.getFilledPointCount()); + } + + AttributeTable &attributes = m_map.getAttributeTable(); + + std::vector filled; + std::vector rows; + + for (int i = 0; i < m_map.getCols(); i++) { + for (int j = 0; j < m_map.getRows(); j++) { + PixelRef curs = PixelRef(static_cast(i), static_cast(j)); + if (m_map.getPoint(curs).filled()) { + filled.push_back(curs); + rows.push_back(attributes.getRowPtr(AttributeKey(curs))); + } + } + } + + int count = 0; + + std::vector col_data(filled.size()); + + int i, N = int(filled.size()); +#pragma omp parallel for default(shared) private(i) schedule(dynamic) + for (i = 0; i < N; i++) { + if (m_gates_only) { + count++; + continue; + } + + DataPoint &dp = col_data[i]; + + depthmapX::RowMatrix miscs(m_map.getRows(), m_map.getCols()); + depthmapX::RowMatrix cumangles(m_map.getRows(), m_map.getCols()); + + miscs.initialiseValues(0); + cumangles.initialiseValues(-1.0f); + + float total_angle = 0.0f; + int total_nodes = 0; + + // note that m_misc is used in a different manner to analyseGraph / PointDepth + // here it marks the node as used in calculation only + + std::set search_list; + search_list.insert(AngularTriple(0.0f, filled[size_t(i)], NoPixel)); + cumangles(filled[size_t(i)].y, filled[size_t(i)].x) = 0.0f; + + while (search_list.size()) { + std::set::iterator it = search_list.begin(); + AngularTriple here = *it; + search_list.erase(it); + if (int(m_radius) != -1 && double(here.angle) > m_radius) { + break; + } + Point &p = m_map.getPoint(here.pixel); + int &p1misc = miscs(here.pixel.y, here.pixel.x); + float &p1cumangle = cumangles(here.pixel.y, here.pixel.x); + // nb, the filled check is necessary as diagonals seem to be stored with 'gaps' left in + if (p.filled() && p1misc != ~0) { + extractAngular(p.getNode(), search_list, &m_map, here, miscs, cumangles); + p1misc = ~0; + if (!p.getMergePixel().empty()) { + Point &p2 = m_map.getPoint(p.getMergePixel()); + int &p2misc = miscs(p.getMergePixel().y, p.getMergePixel().x); + float &p2cumangle = cumangles(p.getMergePixel().y, p.getMergePixel().x); + if (p2misc != ~0) { + p2cumangle = p1cumangle; + extractAngular(p2.getNode(), search_list, &m_map, + AngularTriple(here.angle, p.getMergePixel(), NoPixel), miscs, cumangles); + p2misc = ~0; + } + } + total_angle += p1cumangle; + total_nodes += 1; + } + } + + if (total_nodes > 0) { + dp.mean_depth = float(double(total_angle) / double(total_nodes)); + } + dp.total_depth = total_angle; + dp.count = float(total_nodes); + + count++; // <- increment count + + if (comm) { + if (qtimer(atime, 500)) { + if (comm->IsCancelled()) { + throw Communicator::CancelledException(); + } + comm->CommPostMessage(Communicator::CURRENT_RECORD, count); + } + } + + // kept to achieve parity in binary comparison with old versions + // TODO: Remove at next version of .graph file + m_map.getPoint(filled[size_t(i)]).m_misc = miscs(filled[size_t(i)].y, filled[size_t(i)].x); + m_map.getPoint(filled[size_t(i)]).m_cumangle = cumangles(filled[size_t(i)].y, filled[size_t(i)].x); + } + + std::string radius_text; + if (int(m_radius) != -1) { + if (m_map.getRegion().width() > 100.0) { + radius_text = std::string(" R") + dXstring::formatString(m_radius, "%.f"); + } else if (m_map.getRegion().width() < 1.0) { + radius_text = std::string(" R") + dXstring::formatString(m_radius, "%.4f"); + } else { + radius_text = std::string(" R") + dXstring::formatString(m_radius, "%.2f"); + } + } + // n.b. these must be entered in alphabetical order to preserve col indexing: + std::string mean_depth_col_text = std::string("Angular Mean Depth") + radius_text; + int mean_depth_col = attributes.getOrInsertColumn(mean_depth_col_text.c_str()); + std::string total_detph_col_text = std::string("Angular Total Depth") + radius_text; + int total_depth_col = attributes.getOrInsertColumn(total_detph_col_text.c_str()); + std::string count_col_text = std::string("Angular Node Count") + radius_text; + int count_col = attributes.getOrInsertColumn(count_col_text.c_str()); + + auto dataIter = col_data.begin(); + for (auto row : rows) { + row->setValue(total_depth_col, dataIter->mean_depth); + row->setValue(total_depth_col, dataIter->total_depth); + row->setValue(count_col, dataIter->count); + dataIter++; + } + + m_map.overrideDisplayedAttribute(-2); + m_map.setDisplayedAttribute(mean_depth_col); + + return true; +} + +void VGAAngularOpenMP::extractAngular(Node &node, std::set &pixels, PointMap *pointdata, + const AngularTriple &curs, depthmapX::RowMatrix &miscs, + depthmapX::RowMatrix &cumangles) { + if (curs.angle == 0.0f || pointdata->getPoint(curs.pixel).blocked() || pointdata->blockedAdjacent(curs.pixel)) { + for (int i = 0; i < 32; i++) { + Bin &bin = node.bin(i); + for (auto pixVec : bin.m_pixel_vecs) { + for (PixelRef pix = pixVec.start(); pix.col(bin.m_dir) <= pixVec.end().col(bin.m_dir);) { + if (miscs(pix.y, pix.x) == 0) { + // n.b. dmap v4.06r now sets angle in range 0 to 4 (1 = 90 degrees) + float ang = (curs.lastpixel == NoPixel) + ? 0.0f + : (float)(angle(pix, curs.pixel, curs.lastpixel) / (M_PI * 0.5)); + float &cumangle = cumangles(pix.y, pix.x); + if (cumangle == -1.0 || curs.angle + ang < cumangle) { + cumangle = cumangles(curs.pixel.y, curs.pixel.x) + ang; + pixels.insert(AngularTriple(cumangle, pix, curs.pixel)); + } + } + pix.move(bin.m_dir); + } + } + } + } +} diff --git a/modules/vgaparallel/core/vgaangularopenmp.h b/modules/vgaparallel/core/vgaangularopenmp.h new file mode 100644 index 00000000..1ad3cf9d --- /dev/null +++ b/modules/vgaparallel/core/vgaangularopenmp.h @@ -0,0 +1,46 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include "salalib/ianalysis.h" +#include "salalib/options.h" +#include "salalib/pixelref.h" +#include "salalib/pointdata.h" + +#include "genlib/simplematrix.h" + +class VGAAngularOpenMP : public IAnalysis { + private: + PointMap &m_map; + double m_radius; + bool m_gates_only; + + struct DataPoint { + float total_depth, mean_depth, count; + }; + + void extractAngular(Node &node, std::set &pixels, PointMap *pointdata, const AngularTriple &curs, + depthmapX::RowMatrix &miscs, depthmapX::RowMatrix &cumangles); + + public: + VGAAngularOpenMP(PointMap &map, double radius, bool gates_only) + : m_map(map), m_radius(radius), m_gates_only(gates_only) {} + std::string getAnalysisName() const override { return "Angular Analysis (OpenMP)"; } + bool run(Communicator *comm) override; +}; diff --git a/modules/vgaparallel/core/vgametricopenmp.cpp b/modules/vgaparallel/core/vgametricopenmp.cpp new file mode 100644 index 00000000..db9df19d --- /dev/null +++ b/modules/vgaparallel/core/vgametricopenmp.cpp @@ -0,0 +1,195 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "vgametricopenmp.h" + +#include "genlib/stringutils.h" + +#include + +bool VGAMetricOpenMP::run(Communicator *comm) { + time_t atime = 0; + + if (comm) { + qtimer(atime, 0); + comm->CommPostMessage(Communicator::NUM_RECORDS, m_map.getFilledPointCount()); + } + + AttributeTable &attributes = m_map.getAttributeTable(); + + std::vector filled; + std::vector rows; + + for (int i = 0; i < m_map.getCols(); i++) { + for (int j = 0; j < m_map.getRows(); j++) { + PixelRef curs = PixelRef(static_cast(i), static_cast(j)); + if (m_map.getPoint(curs).filled()) { + filled.push_back(curs); + rows.push_back(attributes.getRowPtr(AttributeKey(curs))); + } + } + } + + int count = 0; + + std::vector col_data(filled.size()); + + int i, N = int(filled.size()); +#pragma omp parallel for default(shared) private(i) schedule(dynamic) + for (i = 0; i < N; i++) { + if (m_gates_only) { + count++; + continue; + } + + DataPoint &dp = col_data[i]; + + depthmapX::RowMatrix miscs(m_map.getRows(), m_map.getCols()); + depthmapX::RowMatrix dists(m_map.getRows(), m_map.getCols()); + depthmapX::RowMatrix cumangles(m_map.getRows(), m_map.getCols()); + + miscs.initialiseValues(0); + dists.initialiseValues(-1.0f); + cumangles.initialiseValues(0.0f); + + float euclid_depth = 0.0f; + float total_depth = 0.0f; + float total_angle = 0.0f; + int total_nodes = 0; + + // note that m_misc is used in a different manner to analyseGraph / PointDepth + // here it marks the node as used in calculation only + + std::set search_list; + search_list.insert(MetricTriple(0.0f, filled[size_t(i)], NoPixel)); + while (search_list.size()) { + std::set::iterator it = search_list.begin(); + MetricTriple here = *it; + search_list.erase(it); + if (int(m_radius) != -1 && double(here.dist) * m_map.getSpacing() > m_radius) { + break; + } + Point &p = m_map.getPoint(here.pixel); + int &p1misc = miscs(here.pixel.y, here.pixel.x); + float &p1cumangle = cumangles(here.pixel.y, here.pixel.x); + // nb, the filled check is necessary as diagonals seem to be stored with 'gaps' left in + if (p.filled() && p1misc != ~0) { + extractMetric(p.getNode(), search_list, &m_map, here, miscs, dists, cumangles); + p1misc = ~0; + if (!p.getMergePixel().empty()) { + Point &p2 = m_map.getPoint(p.getMergePixel()); + int &p2misc = miscs(p.getMergePixel().y, p.getMergePixel().x); + float &p2cumangle = cumangles(p.getMergePixel().y, p.getMergePixel().x); + if (p2misc != ~0) { + p2cumangle = p1cumangle; + extractMetric(p2.getNode(), search_list, &m_map, + MetricTriple(here.dist, p.getMergePixel(), NoPixel), miscs, dists, cumangles); + p2misc = ~0; + } + } + total_depth += here.dist * float(m_map.getSpacing()); + total_angle += p1cumangle; + euclid_depth += float(m_map.getSpacing() * dist(here.pixel, filled[size_t(i)])); + total_nodes += 1; + } + } + + // kept to achieve parity in binary comparison with old versions + // TODO: Remove at next version of .graph file + m_map.getPoint(filled[size_t(i)]).m_misc = miscs(filled[size_t(i)].y, filled[size_t(i)].x); + m_map.getPoint(filled[size_t(i)]).m_dist = dists(filled[size_t(i)].y, filled[size_t(i)].x); + m_map.getPoint(filled[size_t(i)]).m_cumangle = cumangles(filled[size_t(i)].y, filled[size_t(i)].x); + + dp.mspa = float(double(total_angle) / double(total_nodes)); + dp.mspl = float(double(total_depth) / double(total_nodes)); + dp.dist = float(double(euclid_depth) / double(total_nodes)); + dp.count = float(total_nodes); + + count++; // <- increment count + + if (comm) { + if (qtimer(atime, 500)) { + if (comm->IsCancelled()) { + throw Communicator::CancelledException(); + } + comm->CommPostMessage(Communicator::CURRENT_RECORD, count); + } + } + } + + std::string radius_text; + if (int(m_radius) != -1) { + if (m_radius > 100.0) { + radius_text = std::string(" R") + dXstring::formatString(m_radius, "%.f"); + } else if (m_map.getRegion().width() < 1.0) { + radius_text = std::string(" R") + dXstring::formatString(m_radius, "%.4f"); + } else { + radius_text = std::string(" R") + dXstring::formatString(m_radius, "%.2f"); + } + } + // n.b. these must be entered in alphabetical order to preserve col indexing: + std::string mspa_col_text = std::string("Metric Mean Shortest-Path Angle") + radius_text; + int mspa_col = attributes.insertOrResetColumn(mspa_col_text.c_str()); + std::string mspl_col_text = std::string("Metric Mean Shortest-Path Distance") + radius_text; + int mspl_col = attributes.insertOrResetColumn(mspl_col_text.c_str()); + std::string dist_col_text = std::string("Metric Mean Straight-Line Distance") + radius_text; + int dist_col = attributes.insertOrResetColumn(dist_col_text.c_str()); + std::string count_col_text = std::string("Metric Node Count") + radius_text; + int count_col = attributes.insertOrResetColumn(count_col_text.c_str()); + + auto dataIter = col_data.begin(); + for (auto row : rows) { + row->setValue(mspa_col, dataIter->mspa); + row->setValue(mspl_col, dataIter->mspl); + row->setValue(dist_col, dataIter->dist); + row->setValue(count_col, dataIter->count); + dataIter++; + } + + m_map.overrideDisplayedAttribute(-2); + m_map.setDisplayedAttribute(mspl_col); + + return true; +} + +void VGAMetricOpenMP::extractMetric(Node &node, std::set &pixels, PointMap *pointdata, + const MetricTriple &curs, depthmapX::RowMatrix &miscs, + depthmapX::RowMatrix &dists, depthmapX::RowMatrix &cumangles) { + if (curs.dist == 0.0f || pointdata->getPoint(curs.pixel).blocked() || pointdata->blockedAdjacent(curs.pixel)) { + for (int i = 0; i < 32; i++) { + Bin &bin = node.bin(i); + for (auto pixVec : bin.m_pixel_vecs) { + for (PixelRef pix = pixVec.start(); pix.col(bin.m_dir) <= pixVec.end().col(bin.m_dir);) { + float &pixdist = dists(pix.y, pix.x); + if (miscs(pix.y, pix.x) == 0 && + (pixdist == -1.0 || (curs.dist + dist(pix, curs.pixel) < pixdist))) { + pixdist = curs.dist + (float)dist(pix, curs.pixel); + // n.b. dmap v4.06r now sets angle in range 0 to 4 (1 = 90 degrees) + cumangles(pix.y, pix.x) = + cumangles(curs.pixel.y, curs.pixel.x) + + (curs.lastpixel == NoPixel + ? 0.0f + : (float)(angle(pix, curs.pixel, curs.lastpixel) / (M_PI * 0.5))); + pixels.insert(MetricTriple(pixdist, pix, curs.pixel)); + } + pix.move(bin.m_dir); + } + } + } + } +} diff --git a/modules/vgaparallel/core/vgametricopenmp.h b/modules/vgaparallel/core/vgametricopenmp.h new file mode 100644 index 00000000..11a8b786 --- /dev/null +++ b/modules/vgaparallel/core/vgametricopenmp.h @@ -0,0 +1,47 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include "salalib/ianalysis.h" +#include "salalib/options.h" +#include "salalib/pixelref.h" +#include "salalib/pointdata.h" + +#include "genlib/simplematrix.h" + +class VGAMetricOpenMP : public IAnalysis { + private: + PointMap &m_map; + double m_radius; + bool m_gates_only; + + struct DataPoint { + float mspa, mspl, dist, count; + }; + + void extractMetric(Node &node, std::set &pixels, PointMap *pointdata, const MetricTriple &curs, + depthmapX::RowMatrix &miscs, depthmapX::RowMatrix &dists, + depthmapX::RowMatrix &cumangles); + + public: + VGAMetricOpenMP(PointMap &map, double radius, bool gates_only) + : m_map(map), m_radius(radius), m_gates_only(gates_only) {} + std::string getAnalysisName() const override { return "Metric Analysis (OpenMP)"; } + bool run(Communicator *comm) override; +}; diff --git a/modules/vgaparallel/core/vgavisualglobalopenmp.cpp b/modules/vgaparallel/core/vgavisualglobalopenmp.cpp new file mode 100644 index 00000000..fa0b4675 --- /dev/null +++ b/modules/vgaparallel/core/vgavisualglobalopenmp.cpp @@ -0,0 +1,259 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "vgavisualglobalopenmp.h" + +#include "genlib/stringutils.h" + +#include + +bool VGAVisualGlobalOpenMP::run(Communicator *comm) { + time_t atime = 0; + + if (comm) { + qtimer(atime, 0); + comm->CommPostMessage(Communicator::NUM_RECORDS, m_map.getFilledPointCount()); + } + + AttributeTable &attributes = m_map.getAttributeTable(); + + std::vector filled; + std::vector rows; + + for (int i = 0; i < m_map.getCols(); i++) { + for (int j = 0; j < m_map.getRows(); j++) { + PixelRef curs = PixelRef(i, j); + if (m_map.getPoint(curs).filled()) { + filled.push_back(curs); + rows.push_back(attributes.getRowPtr(AttributeKey(curs))); + } + } + } + + int count = 0; + + if (comm) { + qtimer(atime, 0); + comm->CommPostMessage(Communicator::NUM_STEPS, 1); + comm->CommPostMessage(Communicator::CURRENT_STEP, 1); + comm->CommPostMessage(Communicator::NUM_RECORDS, filled.size()); + } + std::vector col_data(filled.size()); + +#pragma omp parallel for + for (int i = 0; i < filled.size(); i++) { + + if ((m_map.getPoint(filled[i]).contextfilled() && !filled[i].iseven()) || (m_gatesOnly)) { + count++; + continue; + } + DataPoint &dp = col_data[i]; + + depthmapX::RowMatrix miscs(m_map.getRows(), m_map.getCols()); + depthmapX::RowMatrix extents(m_map.getRows(), m_map.getCols()); + + for (int ii = 0; ii < m_map.getCols(); ii++) { + for (int jj = 0; jj < m_map.getRows(); jj++) { + miscs(jj, ii) = 0; + extents(jj, ii) = PixelRef(ii, jj); + } + } + + int total_depth = 0; + int total_nodes = 0; + + std::vector distribution; + std::vector search_tree; + search_tree.push_back(PixelRefVector()); + search_tree.back().push_back(filled[i]); + + int level = 0; + while (search_tree[level].size()) { + search_tree.push_back(PixelRefVector()); + distribution.push_back(0); + for (size_t n = search_tree[level].size() - 1; n != -1; n--) { + PixelRef curr = search_tree[level][n]; + Point &p = m_map.getPoint(curr); + int &p1misc = miscs(curr.y, curr.x); + if (p.filled() && p1misc != ~0) { + total_depth += level; + total_nodes += 1; + distribution.back() += 1; + if ((int)m_radius == -1 || + (level < (int)m_radius && (!p.contextfilled() || search_tree[level][n].iseven()))) { + extractUnseen(p.getNode(), search_tree[level + 1], miscs, extents); + p1misc = ~0; + if (!p.getMergePixel().empty()) { + Point &p2 = m_map.getPoint(p.getMergePixel()); + int &p2misc = miscs(p.getMergePixel().y, p.getMergePixel().x); + if (p2misc != ~0) { + extractUnseen(p2.getNode(), search_tree[level + 1], miscs, extents); + p2misc = ~0; + } + } + } else { + p1misc = ~0; + } + } + search_tree[level].pop_back(); + } + level++; + } + + // only set to single float precision after divide + // note -- total_nodes includes this one -- mean depth as per p.108 Social Logic of Space + + dp.count = float(total_nodes); // note: total nodes includes this one; + + // ERROR !!!!!! + if (total_nodes > 1) { + double mean_depth = double(total_depth) / double(total_nodes - 1); + dp.depth = float(mean_depth); + // total nodes > 2 to avoid divide by 0 (was > 3) + if (total_nodes > 2 && mean_depth > 1.0) { + double ra = 2.0 * (mean_depth - 1.0) / double(total_nodes - 2); + // d-value / p-values from Depthmap 4 manual, note: node_count includes this one + double rra_d = ra / dvalue(total_nodes); + double rra_p = ra / pvalue(total_nodes); + double integ_tk = teklinteg(total_nodes, total_depth); + dp.integ_dv = float(1.0 / rra_d); + dp.integ_pv = float(1.0 / rra_p); + + if (total_depth - total_nodes + 1 > 1) { + dp.integ_tk = float(integ_tk); + } else { + dp.integ_tk = -1.0f; + } + } else { + dp.integ_dv = -1.0f; + dp.integ_pv = -1.0f; + dp.integ_tk = -1.0f; + } + double entropy = 0.0, rel_entropy = 0.0, factorial = 1.0; + // n.b., this distribution contains the root node itself in distribution[0] + // -> chopped from entropy to avoid divide by zero if only one node + for (size_t k = 1; k < distribution.size(); k++) { + if (distribution[k] > 0) { + double prob = double(distribution[k]) / double(total_nodes - 1); + entropy -= prob * log2(prob); + // Formula from Turner 2001, "Depthmap" + factorial *= double(k + 1); + double q = (pow(mean_depth, double(k)) / double(factorial)) * exp(-mean_depth); + rel_entropy += (float)prob * log2(prob / q); + } + } + dp.entropy = float(entropy); + dp.rel_entropy = float(rel_entropy); + } else { + dp.depth = -1.0f; + dp.entropy = -1.0f; + dp.rel_entropy = -1.0f; + } + count++; // <- increment count + + if (comm) { + if (qtimer(atime, 500)) { + if (comm->IsCancelled()) { + throw Communicator::CancelledException(); + } + comm->CommPostMessage(Communicator::CURRENT_RECORD, count); + } + } + + // kept to achieve parity in binary comparison with old versions + // TODO: Remove at next version of .graph file + size_t filledIdx = size_t(filled[i].y * m_map.getCols() + filled[i].x); + m_map.getPoint(filled[i]).m_misc = miscs(filled[i].y, filled[i].x); + m_map.getPoint(filled[i]).m_extent = extents(filled[i].y, filled[i].x); + } + + int entropy_col, rel_entropy_col, integ_dv_col, integ_pv_col, integ_tk_col, depth_col, count_col; + + std::string radius_text; + if (m_radius != -1) { + radius_text = std::string(" R") + dXstring::formatString(int(m_radius), "%d"); + } + + // n.b. these must be entered in alphabetical order to preserve col indexing: + // dX simple version test // TV + std::string entropy_col_text = std::string("Visual Entropy") + radius_text; + std::string integ_dv_col_text = std::string("Visual Integration [HH]") + radius_text; + std::string integ_pv_col_text = std::string("Visual Integration [P-value]") + radius_text; + std::string integ_tk_col_text = std::string("Visual Integration [Tekl]") + radius_text; + std::string depth_col_text = std::string("Visual Mean Depth") + radius_text; + std::string count_col_text = std::string("Visual Node Count") + radius_text; + std::string rel_entropy_col_text = std::string("Visual Relativised Entropy") + radius_text; + + attributes.insertOrResetColumn(integ_dv_col_text.c_str()); + + attributes.insertOrResetColumn(entropy_col_text.c_str()); + attributes.insertOrResetColumn(integ_pv_col_text.c_str()); + attributes.insertOrResetColumn(integ_tk_col_text.c_str()); + attributes.insertOrResetColumn(depth_col_text.c_str()); + attributes.insertOrResetColumn(count_col_text.c_str()); + attributes.insertOrResetColumn(rel_entropy_col_text.c_str()); + + integ_dv_col = attributes.getOrInsertColumn(integ_dv_col_text.c_str()); + + entropy_col = attributes.getOrInsertColumn(entropy_col_text.c_str()); + integ_pv_col = attributes.getOrInsertColumn(integ_pv_col_text.c_str()); + integ_tk_col = attributes.getOrInsertColumn(integ_tk_col_text.c_str()); + depth_col = attributes.getOrInsertColumn(depth_col_text.c_str()); + count_col = attributes.getOrInsertColumn(count_col_text.c_str()); + rel_entropy_col = attributes.getOrInsertColumn(rel_entropy_col_text.c_str()); + + auto dataIter = col_data.begin(); + for (auto row : rows) { + row->setValue(integ_dv_col, dataIter->integ_dv); + row->setValue(integ_pv_col, dataIter->integ_pv); + row->setValue(integ_tk_col, dataIter->integ_tk); + row->setValue(count_col, dataIter->count); + row->setValue(depth_col, dataIter->depth); + row->setValue(entropy_col, dataIter->entropy); + row->setValue(rel_entropy_col, dataIter->rel_entropy); + dataIter++; + } + + m_map.setDisplayedAttribute(integ_dv_col); + + return true; +} + +void VGAVisualGlobalOpenMP::extractUnseen(Node &node, PixelRefVector &pixels, depthmapX::RowMatrix &miscs, + depthmapX::RowMatrix &extents) { + for (int i = 0; i < 32; i++) { + Bin &bin = node.bin(i); + for (auto pixVec : bin.m_pixel_vecs) { + for (PixelRef pix = pixVec.start(); pix.col(bin.m_dir) <= pixVec.end().col(bin.m_dir);) { + int &misc = miscs(pix.y, pix.x); + PixelRef &extent = extents(pix.y, pix.x); + if (misc == 0) { + pixels.push_back(pix); + misc |= (1 << i); + } + // 10.2.02 revised --- diagonal was breaking this as it was extent in diagonal or horizontal + if (!(bin.m_dir & PixelRef::DIAGONAL)) { + if (extent.col(bin.m_dir) >= pixVec.end().col(bin.m_dir)) + break; + extent.col(bin.m_dir) = pixVec.end().col(bin.m_dir); + } + pix.move(bin.m_dir); + } + } + } +} diff --git a/modules/vgaparallel/core/vgavisualglobalopenmp.h b/modules/vgaparallel/core/vgavisualglobalopenmp.h new file mode 100644 index 00000000..2a584dd2 --- /dev/null +++ b/modules/vgaparallel/core/vgavisualglobalopenmp.h @@ -0,0 +1,46 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include "salalib/ianalysis.h" +#include "salalib/options.h" +#include "salalib/pixelref.h" +#include "salalib/pointdata.h" + +#include "genlib/simplematrix.h" + +class VGAVisualGlobalOpenMP : public IAnalysis { + private: + PointMap &m_map; + double m_radius; + bool m_gatesOnly; + + struct DataPoint { + float count, depth, integ_dv, integ_pv; + float integ_tk, entropy, rel_entropy; + }; + void extractUnseen(Node &node, PixelRefVector &pixels, depthmapX::RowMatrix &miscs, + depthmapX::RowMatrix &extents); + + public: + VGAVisualGlobalOpenMP(PointMap &map, double radius, bool gatesOnly) + : m_map(map), m_radius(radius), m_gatesOnly(gatesOnly) {} + std::string getAnalysisName() const override { return "Global Visibility Analysis (OpenMP)"; } + bool run(Communicator *) override; +}; diff --git a/modules/vgaparallel/core/vgavisuallocaladjmatrix.cpp b/modules/vgaparallel/core/vgavisuallocaladjmatrix.cpp new file mode 100644 index 00000000..b2952755 --- /dev/null +++ b/modules/vgaparallel/core/vgavisuallocaladjmatrix.cpp @@ -0,0 +1,165 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "vgavisuallocaladjmatrix.h" + +#include "genlib/stringutils.h" + +#include + +bool VGAVisualLocalAdjMatrix::run(Communicator *comm) { + time_t atime = 0; + + if (comm) { + qtimer(atime, 0); + comm->CommPostMessage(Communicator::NUM_RECORDS, m_map.getFilledPointCount()); + } + + AttributeTable &attributes = m_map.getAttributeTable(); + + std::vector filled; + std::vector rows; + + for (int i = 0; i < m_map.getCols(); i++) { + for (int j = 0; j < m_map.getRows(); j++) { + PixelRef curs = PixelRef(static_cast(i), static_cast(j)); + if (m_map.getPoint(curs).filled()) { + filled.push_back(curs); + rows.push_back(attributes.getRowPtr(AttributeKey(curs))); + } + } + } + + int count = 0; + + std::vector col_data(filled.size()); + + int i; + const long N = long(filled.size()); + + std::map refToFilled; + for (i = 0; i < N; ++i) { + refToFilled.insert(std::make_pair(filled[size_t(i)], i)); + } + + std::vector hoods(N * N); + +#pragma omp parallel for default(shared) private(i) schedule(dynamic) + for (i = 0; i < N; ++i) { + Point &p = m_map.getPoint(filled[size_t(i)]); + std::set neighbourhood; +#pragma omp critical(dumpNeighbourhood) + { dumpNeighbourhood(p.getNode(), neighbourhood); } + for (auto &neighbour : neighbourhood) { + if (m_map.getPoint(neighbour).hasNode()) { + hoods[long(i * N + refToFilled[neighbour])] = true; + } + } + } + +#pragma omp parallel for default(shared) private(i) schedule(dynamic) + for (i = 0; i < N; ++i) { + + DataPoint &dp = col_data[i]; + + Point &p = m_map.getPoint(filled[size_t(i)]); + if ((p.contextfilled() && !filled[size_t(i)].iseven()) || (m_gates_only)) { + count++; + continue; + } + + std::vector totalHood(N); + + int cluster = 0; + float control = 0.0f; + + int hoodSize = 0; + for (int j = 0; j < N; j++) { + if (hoods[i * N + j]) { + hoodSize++; + int retHood = 0; + for (int k = 0; k < N; k++) { + if (hoods[j * N + k]) { + totalHood[k] = true; + retHood++; + if (hoods[i * N + k]) + cluster++; + } + } + control += 1.0f / float(retHood); + } + } + int totalReach = 0; + for (int j = 0; j < N; j++) { + if (totalHood[j]) + totalReach++; + } +#pragma omp critical(add_to_col) + { + if (hoodSize > 1) { + dp.cluster = float(cluster / double(hoodSize * (hoodSize - 1.0))); + dp.control = float(control); + dp.controllability = float(double(hoodSize) / double(totalReach)); + } else { + dp.cluster = -1.0f; + dp.control = -1.0f; + dp.controllability = -1; + } + } + +#pragma omp critical(count) + { + count++; // <- increment count + if (comm) { + if (qtimer(atime, 500)) { + if (comm->IsCancelled()) { + throw Communicator::CancelledException(); + } + comm->CommPostMessage(Communicator::CURRENT_RECORD, count); + } + } + } + } + + int cluster_col = attributes.insertOrResetColumn("Visual Clustering Coefficient"); + int control_col = attributes.insertOrResetColumn("Visual Control"); + int controllability_col = attributes.insertOrResetColumn("Visual Controllability"); + + auto dataIter = col_data.begin(); + for (auto row : rows) { + row->setValue(cluster_col, dataIter->cluster); + row->setValue(control_col, dataIter->control); + row->setValue(controllability_col, dataIter->controllability); + dataIter++; + } + m_map.setDisplayedAttribute(cluster_col); + + return true; +} + +void VGAVisualLocalAdjMatrix::dumpNeighbourhood(Node &node, std::set &hood) const { + for (int i = 0; i < 32; i++) { + Bin &bin = node.bin(i); + for (auto pixVec : bin.m_pixel_vecs) { + for (PixelRef pix = pixVec.start(); pix.col(bin.m_dir) <= pixVec.end().col(bin.m_dir);) { + hood.insert(pix); + pix.move(bin.m_dir); + } + } + } +} diff --git a/modules/vgaparallel/core/vgavisuallocaladjmatrix.h b/modules/vgaparallel/core/vgavisuallocaladjmatrix.h new file mode 100644 index 00000000..9fad889f --- /dev/null +++ b/modules/vgaparallel/core/vgavisuallocaladjmatrix.h @@ -0,0 +1,40 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include "salalib/ianalysis.h" +#include "salalib/options.h" +#include "salalib/pixelref.h" +#include "salalib/pointdata.h" + +class VGAVisualLocalAdjMatrix : public IAnalysis { + private: + PointMap &m_map; + bool m_gates_only; + + struct DataPoint { + float cluster, control, controllability; + }; + void dumpNeighbourhood(Node &node, std::set &hood) const; + + public: + VGAVisualLocalAdjMatrix(PointMap &map, bool gates_only) : m_map(map), m_gates_only(gates_only) {} + std::string getAnalysisName() const override { return "Local Visibility Analysis (OpenMP)"; } + bool run(Communicator *comm) override; +}; diff --git a/modules/vgaparallel/core/vgavisuallocalopenmp.cpp b/modules/vgaparallel/core/vgavisuallocalopenmp.cpp new file mode 100644 index 00000000..8a03e72b --- /dev/null +++ b/modules/vgaparallel/core/vgavisuallocalopenmp.cpp @@ -0,0 +1,159 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "vgavisuallocalopenmp.h" + +#include "genlib/stringutils.h" + +#include + +bool VGAVisualLocalOpenMP::run(Communicator *comm) { + time_t atime = 0; + + if (comm) { + qtimer(atime, 0); + comm->CommPostMessage(Communicator::NUM_RECORDS, m_map.getFilledPointCount()); + } + + AttributeTable &attributes = m_map.getAttributeTable(); + + std::vector filled; + std::vector rows; + + for (int i = 0; i < m_map.getCols(); i++) { + for (int j = 0; j < m_map.getRows(); j++) { + PixelRef curs = PixelRef(static_cast(i), static_cast(j)); + if (m_map.getPoint(curs).filled()) { + filled.push_back(curs); + rows.push_back(attributes.getRowPtr(AttributeKey(curs))); + } + } + } + + int count = 0; + + count = 0; + + std::vector col_data(filled.size()); + + if (comm) { + qtimer(atime, 0); + comm->CommPostMessage(Communicator::NUM_STEPS, 1); + comm->CommPostMessage(Communicator::CURRENT_STEP, 1); + comm->CommPostMessage(Communicator::NUM_RECORDS, filled.size()); + } + std::vector> hoods(filled.size()); + + int i, N = int(filled.size()); + std::map refToFilled; + for (i = 0; i < N; ++i) { + refToFilled.insert(std::make_pair(filled[size_t(i)], i)); + } +#pragma omp parallel for default(shared) private(i) schedule(dynamic) + for (i = 0; i < N; ++i) { + Point &p = m_map.getPoint(filled[size_t(i)]); + std::set neighbourhood; +#pragma omp critical(dumpNeighbourhood) + { dumpNeighbourhood(p.getNode(), neighbourhood); } + for (auto &neighbour : neighbourhood) { + if (m_map.getPoint(neighbour).hasNode()) { + hoods[size_t(i)].insert(refToFilled[neighbour]); + } + } + } + +#pragma omp parallel for default(shared) private(i) schedule(dynamic) + for (i = 0; i < N; ++i) { + + DataPoint &dp = col_data[i]; + + Point &p = m_map.getPoint(filled[size_t(i)]); + if ((p.contextfilled() && !filled[size_t(i)].iseven())) { + count++; + continue; + } + + // This is much easier to do with a straight forward list: + std::set &neighbourhood = hoods[size_t(i)]; + std::set totalneighbourhood; + int cluster = 0; + float control = 0.0f; + + for (auto &neighbour : neighbourhood) { + std::set &retneighbourhood = hoods[size_t(neighbour)]; + std::set intersect; + std::set_intersection(neighbourhood.begin(), neighbourhood.end(), retneighbourhood.begin(), + retneighbourhood.end(), std::inserter(intersect, intersect.begin())); + totalneighbourhood.insert(retneighbourhood.begin(), retneighbourhood.end()); + control += 1.0f / float(retneighbourhood.size()); + cluster += intersect.size(); + } +#pragma omp critical(add_to_col) + { + if (neighbourhood.size() > 1) { + dp.cluster = float(cluster / double(neighbourhood.size() * (neighbourhood.size() - 1.0))); + dp.control = float(control); + dp.controllability = float(double(neighbourhood.size()) / double(totalneighbourhood.size())); + } else { + dp.cluster = -1.0f; + dp.control = -1.0f; + dp.controllability = -1.0f; + } + } + +#pragma omp critical(count) + { + count++; // <- increment count + if (comm) { + if (qtimer(atime, 500)) { + if (comm->IsCancelled()) { + throw Communicator::CancelledException(); + } + comm->CommPostMessage(Communicator::CURRENT_RECORD, count); + } + } + } + } + + int cluster_col = attributes.insertOrResetColumn("Visual Clustering Coefficient"); + int control_col = attributes.insertOrResetColumn("Visual Control"); + int controllability_col = attributes.insertOrResetColumn("Visual Controllability"); + + auto dataIter = col_data.begin(); + for (auto row : rows) { + row->setValue(cluster_col, dataIter->cluster); + row->setValue(control_col, dataIter->control); + row->setValue(controllability_col, dataIter->controllability); + dataIter++; + } + m_map.setDisplayedAttribute(cluster_col); + + return true; +} + +void VGAVisualLocalOpenMP::dumpNeighbourhood(Node &node, std::set &hood) const { + for (int i = 0; i < 32; i++) { + Bin &bin = node.bin(i); + for (auto pixVec : bin.m_pixel_vecs) { + for (PixelRef pix = pixVec.start(); pix.col(bin.m_dir) <= pixVec.end().col(bin.m_dir);) { + hood.insert(pix); + pix.move(bin.m_dir); + } + } + } +} diff --git a/modules/vgaparallel/core/vgavisuallocalopenmp.h b/modules/vgaparallel/core/vgavisuallocalopenmp.h new file mode 100644 index 00000000..8cd217cc --- /dev/null +++ b/modules/vgaparallel/core/vgavisuallocalopenmp.h @@ -0,0 +1,39 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include "salalib/ianalysis.h" +#include "salalib/options.h" +#include "salalib/pixelref.h" +#include "salalib/pointdata.h" + +class VGAVisualLocalOpenMP : public IAnalysis { + private: + PointMap &m_map; + struct DataPoint { + float cluster, control, controllability; + }; + + void dumpNeighbourhood(Node &node, std::set &hood) const; + + public: + VGAVisualLocalOpenMP(PointMap &map) : m_map(map) {} + std::string getAnalysisName() const override { return "Local Visibility Analysis (OpenMP)"; } + bool run(Communicator *comm) override; +}; diff --git a/modules/vgaparallel/coreTest/CMakeLists.txt b/modules/vgaparallel/coreTest/CMakeLists.txt new file mode 100644 index 00000000..2aee88b0 --- /dev/null +++ b/modules/vgaparallel/coreTest/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (C) 2020 Petros Koutsolampros + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(vgaparallelcoretest vgaparallelcoretest) +set(vgaparallelcoretest_SRCS + vgaparallelcoretest.cpp) + +set(modules_coreTest "${modules_coreTest}" "vgaparallelcoretest" CACHE INTERNAL "modules_coreTest" FORCE) + +add_compile_definitions(VGAPARALLEL_CORE_TEST_LIBRARY) + +add_library(${vgaparallelcoretest} OBJECT ${vgaparallelcoretest_SRCS}) diff --git a/modules/vgaparallel/coreTest/vgaparallelcoretest.cpp b/modules/vgaparallel/coreTest/vgaparallelcoretest.cpp new file mode 100644 index 00000000..bb89174f --- /dev/null +++ b/modules/vgaparallel/coreTest/vgaparallelcoretest.cpp @@ -0,0 +1,20 @@ +// Copyright (C) 2020 Petros Koutsolampros + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "catch.hpp" +#include "salalib/axialmap.h" +#include "salalib/mapconverter.h" + +TEST_CASE("Dummy VGA Parallel test", "") {} diff --git a/modules/vgaparallel/gui/CMakeLists.txt b/modules/vgaparallel/gui/CMakeLists.txt new file mode 100644 index 00000000..8f67521a --- /dev/null +++ b/modules/vgaparallel/gui/CMakeLists.txt @@ -0,0 +1,45 @@ +# Copyright (C) 2020 Petros Koutsolampros + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(vgaparallelgui vgaparallelgui) + +set(CMAKE_AUTOMOC ON) +# AUTOUIC is required to trigger the equivalent function in depthmapX +# the .ui files in this target are however generated using qt5_wrap_ui +set(CMAKE_AUTOUIC_SEARCH_PATHS UI ../../../depthmapX/UI) +set(CMAKE_AUTOUIC ON) +find_package(Qt5 COMPONENTS Core Widgets Gui OpenGL REQUIRED) + +set(vgaparallelgui_SRCS + uictrigger.cpp + vgaparallelmainwindow.cpp +) + +set(modules_gui "${modules_gui}" "vgaparallelgui" CACHE INTERNAL "modules_gui" FORCE) + + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +include_directories(${Qt5Core_INCLUDE_DIRS}) +include_directories(${Qt5Widgets_INCLUDE_DIRS}) +include_directories(${Qt5Gui_INCLUDE_DIRS}) + +add_definitions(${Qt5Core_DEFINITIONS}) +add_definitions(${Qt5Widgets_DEFINITIONS}) +add_definitions(${Qt5Gui_DEFINITIONS}) + +add_compile_definitions(VGAPARALLEL_GUI_LIBRARY) + +add_library(${vgaparallelgui} OBJECT ${vgaparallelgui_SRCS} ${vgaparallelgui_UIS}) diff --git a/modules/vgaparallel/gui/uictrigger.cpp b/modules/vgaparallel/gui/uictrigger.cpp new file mode 100644 index 00000000..3d79019a --- /dev/null +++ b/modules/vgaparallel/gui/uictrigger.cpp @@ -0,0 +1,5 @@ +// This file is required to trigger cmake's AUTOUIC in the depthmapX directory +// if it is not included then the ui_*.h files in that directory are not generated +// and then not found by the module which is built first because it is a dependency +// of depthmapX. +#include "depthmapX/ui_ColourScaleDlg.h" diff --git a/modules/vgaparallel/gui/vgaparallelmainwindow.cpp b/modules/vgaparallel/gui/vgaparallelmainwindow.cpp new file mode 100644 index 00000000..25326086 --- /dev/null +++ b/modules/vgaparallel/gui/vgaparallelmainwindow.cpp @@ -0,0 +1,180 @@ +// Copyright (C) 2020 Petros Koutsolampros + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "vgaparallelmainwindow.h" + +#include "modules/vgaparallel/core/vgaangularopenmp.h" +#include "modules/vgaparallel/core/vgametricopenmp.h" +#include "modules/vgaparallel/core/vgavisualglobalopenmp.h" +#include "modules/vgaparallel/core/vgavisuallocaladjmatrix.h" +#include "modules/vgaparallel/core/vgavisuallocalopenmp.h" + +#include "depthmapX/mainwindowhelpers.h" +#include "genlib/exceptions.h" + +#include +#include +#include + +bool VGAParallelMainWindow::createMenus(MainWindow *mainWindow) { + QMenu *toolsMenu = MainWindowHelpers::getOrAddRootMenu(mainWindow, tr("&Tools")); + QMenu *visibilityMenu = MainWindowHelpers::getOrAddMenu(toolsMenu, tr("&Visibility")); + QMenu *vgaParallelMenu = MainWindowHelpers::getOrAddMenu(visibilityMenu, tr("Parallel Visibility Graph Analysis")); + + QAction *visualGlobalAct = new QAction(tr("Global Visibility"), mainWindow); + connect(visualGlobalAct, &QAction::triggered, this, + [this, mainWindow] { OnVGAParallel(mainWindow, AnalysisType::VISUAL_GLOBAL_OPENMP); }); + vgaParallelMenu->addAction(visualGlobalAct); + QAction *visualLocalAct = new QAction(tr("Local Visibility"), mainWindow); + connect(visualLocalAct, &QAction::triggered, this, + [this, mainWindow] { OnVGAParallel(mainWindow, AnalysisType::VISUAL_LOCAL_OPENMP); }); + vgaParallelMenu->addAction(visualLocalAct); + QAction *visualLocalAdjMatrixAct = new QAction(tr("Local Visibility (Adjacency Matrix)"), mainWindow); + connect(visualLocalAdjMatrixAct, &QAction::triggered, this, + [this, mainWindow] { OnVGAParallel(mainWindow, AnalysisType::VISUAL_LOCAL_ADJMATRIX); }); + vgaParallelMenu->addAction(visualLocalAdjMatrixAct); + QAction *metricAct = new QAction(tr("Global Metric"), mainWindow); + connect(metricAct, &QAction::triggered, this, + [this, mainWindow] { OnVGAParallel(mainWindow, AnalysisType::METRIC_OPENMP); }); + vgaParallelMenu->addAction(metricAct); + QAction *angularAct = new QAction(tr("Global Angular"), mainWindow); + connect(angularAct, &QAction::triggered, this, + [this, mainWindow] { OnVGAParallel(mainWindow, AnalysisType::ANGULAR_OPENMP); }); + vgaParallelMenu->addAction(angularAct); + + return true; +} + +void VGAParallelMainWindow::OnVGAParallel(MainWindow *mainWindow, AnalysisType analysisType) { + QGraphDoc *graphDoc = mainWindow->activeMapDoc(); + if (graphDoc == nullptr) + return; + + if (graphDoc->m_communicator) { + QMessageBox::warning(mainWindow, tr("Warning"), tr("Please wait, another process is running"), QMessageBox::Ok, + QMessageBox::Ok); + return; + } + if (graphDoc->m_meta_graph->getDisplayedMapType() != ShapeMap::POINTMAP) { + QMessageBox::warning(mainWindow, tr("Warning"), tr("Please make sure the displayed map is a vga map"), + QMessageBox::Ok, QMessageBox::Ok); + return; + } + + graphDoc->m_communicator = new CMSCommunicator(); + + switch (analysisType) { + case AnalysisType::VISUAL_GLOBAL_OPENMP: { + + bool ok; + QString radiusText = QInputDialog::getText(mainWindow, tr("Visibility radius"), + tr("This is the standard global-visibility analysis, parallelised " + "with OpenMP.\nRadius can be from 1 to 99 or n"), + QLineEdit::Normal, "n", &ok); + if (!ok) + return; + graphDoc->m_communicator->setAnalysis(std::unique_ptr(new VGAVisualGlobalOpenMP( + graphDoc->m_meta_graph->getDisplayedPointMap(), ConvertForVisibility(radiusText.toStdString()), false))); + break; + } + case AnalysisType::VISUAL_LOCAL_OPENMP: { + graphDoc->m_communicator->setAnalysis( + std::unique_ptr(new VGAVisualLocalOpenMP(graphDoc->m_meta_graph->getDisplayedPointMap()))); + break; + } + case AnalysisType::VISUAL_LOCAL_ADJMATRIX: { + graphDoc->m_communicator->setAnalysis(std::unique_ptr( + new VGAVisualLocalAdjMatrix(graphDoc->m_meta_graph->getDisplayedPointMap(), false))); + break; + } + case AnalysisType::METRIC_OPENMP: { + bool ok; + QString radiusText = + QInputDialog::getText(mainWindow, tr("Metric radius"), + tr("This is the standard global-metric analysis, parallelised " + "with OpenMP.\nRadius can be any positive number or n for unlimited radius"), + QLineEdit::Normal, "n", &ok); + if (!ok) + return; + graphDoc->m_communicator->setAnalysis(std::unique_ptr(new VGAMetricOpenMP( + graphDoc->m_meta_graph->getDisplayedPointMap(), ConvertForMetric(radiusText.toStdString()), false))); + break; + } + case AnalysisType::ANGULAR_OPENMP: { + + bool ok; + QString radiusText = + QInputDialog::getText(mainWindow, tr("Angular radius"), + tr("This is the standard global-angular analysis, parallelised " + "with OpenMP.\nRadius can be any positive number or n for unlimited radius"), + QLineEdit::Normal, "n", &ok); + if (!ok) + return; + graphDoc->m_communicator->setAnalysis(std::unique_ptr(new VGAAngularOpenMP( + graphDoc->m_meta_graph->getDisplayedPointMap(), ConvertForMetric(radiusText.toStdString()), false))); + break; + } + case AnalysisType::NONE: { + QMessageBox::warning(mainWindow, tr("Warning"), tr("Please select an analysis type"), QMessageBox::Ok, + QMessageBox::Ok); + return; + } + } + + graphDoc->m_communicator->SetFunction(CMSCommunicator::FROMCONNECTOR); + graphDoc->m_communicator->setSuccessUpdateFlags(QGraphDoc::NEW_DATA); + graphDoc->m_communicator->setSuccessRedrawFlags(QGraphDoc::VIEW_ALL, QGraphDoc::REDRAW_GRAPH, QGraphDoc::NEW_DATA); + + graphDoc->CreateWaitDialog(tr("Calculating shortest path...")); + graphDoc->m_thread.render(graphDoc); +} + +// Duplicating the radius converters here to keep the module self contained +// TODO: Transfer them to sala + +double VGAParallelMainWindow::ConvertForVisibility(const std::string &radius) const { + if (radius == "n") { + return -1.0; + } + char *end; + long rad = std::strtol(radius.c_str(), &end, 10); + if (rad < 1 || rad > 99) { + throw depthmapX::RuntimeException( + std::string("Radius for visibility analysis must be n for the whole range or an " + "integer between 1 and 99 inclusive. Got ") + + radius); + } + return static_cast(rad); +} + +double VGAParallelMainWindow::ConvertForMetric(const std::string &radius) const { + if (radius == "n") { + return -1.0; + } + char *end; + double rad = strtod(radius.c_str(), &end); + if (rad <= 0) { + throw depthmapX::RuntimeException( + std::string("Radius for metric vga must be n for the whole range or a positive number. Got ") + radius); + } + if (std::isnan(rad)) { + throw depthmapX::RuntimeException("Radius NaN?! Really?"); + } + if (std::isinf(rad)) { + throw depthmapX::RuntimeException("Radius inf?! Who are you kidding?"); + } + + return rad; +} diff --git a/modules/vgaparallel/gui/vgaparallelmainwindow.h b/modules/vgaparallel/gui/vgaparallelmainwindow.h new file mode 100644 index 00000000..427ef7e7 --- /dev/null +++ b/modules/vgaparallel/gui/vgaparallelmainwindow.h @@ -0,0 +1,39 @@ +// Copyright (C) 2020 Petros Koutsolampros + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include "depthmapX/imainwindowmodule.h" + +class VGAParallelMainWindow : public IMainWindowModule { + private: + enum class AnalysisType { + NONE, + VISUAL_LOCAL_OPENMP, + VISUAL_LOCAL_ADJMATRIX, + VISUAL_GLOBAL_OPENMP, + METRIC_OPENMP, + ANGULAR_OPENMP + }; + double ConvertForVisibility(const std::string &radius) const; + double ConvertForMetric(const std::string &radius) const; + + private slots: + void OnVGAParallel(MainWindow *mainWindow, AnalysisType analysisType); + + public: + VGAParallelMainWindow() : IMainWindowModule() {} + bool createMenus(MainWindow *mainWindow); +};